如何按异常值切分连续数据 pd.cut/pd.qcut无法得到目标结果
实现方法
pd.cut和pd.qcut是按固定数值间隔、分位数间隔做静态分箱的工具,没法识别序列里的异常跳变做连续段切分,要实现你的需求,核心逻辑是先识别b列的异常值,再按「正常/异常」的连续段对行分组,最后提取每组对应的a值区间,步骤如下:
1. 构造示例数据
import pandas as pd import numpy as np df = pd.DataFrame({'a': [1, 2, 3, 4, 5, 6, 7, 8, 9], 'b': [2, 2, 4, 3, 1000, 2000, 1, 500, 3]})

2. 标记b列的异常值
如果有明确的业务阈值(比如已知b列正常值都在10以内,超过的就是异常),直接按阈值标记最准确:
# 按业务阈值标记异常,示例中b>10即为异常 df['is_outlier'] = df['b'] > 10
如果没有明确阈值,可以用鲁棒性更好的MAD(中位数绝对偏差)方法自动识别离群点,避免异常值本身干扰判断:
# 用MAD方法自动识别离群点 median = df['b'].median() mad = np.median(np.abs(df['b'] - median)) # 3倍MAD为异常阈值,可根据实际场景调整 threshold = 3 * mad df['is_outlier'] = np.abs(df['b'] - median) > threshold
3. 按连续段生成分组ID
相邻行的异常标记发生变化的位置就是切分点,对切分标记做累加即可得到每个连续段的组ID:
# 相邻行异常状态不一致时标记为切分点,累加生成组ID df['group_id'] = (df['is_outlier'] != df['is_outlier'].shift(1)).cumsum()
处理后的数据分组结果和预期完全一致:索引0-3(a=14)为同组,索引4-5(a=56)为同组,索引6(a=7)、索引7(a=8)、索引8(a=9)各为一组。
4. 提取a列的切分区间
按组ID聚合a列的最小值、最大值,格式化输出即可:
# 聚合得到每组a的起止值 group_info = df.groupby('group_id')['a'].agg(min_val='min', max_val='max').reset_index(drop=True) # 格式化区间 cut_ranges = [] for _, row in group_info.iterrows(): if row['min_val'] == row['max_val']: cut_ranges.append(f"{int(row['min_val'])}") else: cut_ranges.append(f"{int(row['min_val'])}-{int(row['max_val'])}")
最终cut_ranges的输出为:
['1-4', '5-6', '7', '8', '9']
如果你的异常判断逻辑不是「b值本身过大/过小」,而是b值和前序值的跳变幅度异常,只需要把第二步的异常标记逻辑替换为跳变幅度计算即可,整体分组逻辑不变。
内容的提问来源于stack exchange,提问作者chikich
相关产品推荐
相关产品推荐

