基于Pandas DataFrame的Location匹配阈值范围筛选异常行
基于Pandas DataFrame的Location匹配阈值范围筛选异常行
嘿,这个需求很明确,我来一步步帮你实现基于Location匹配阈值、筛选异常行的功能,完全适配你提到的「cost列数量可变」的场景!
整体思路
- 先从df1中提取每个Location对应的max和min阈值,形成一个映射表
- 将这个阈值表和df2合并,让df2的每一行都带上对应Location的阈值
- 动态识别所有cost列(不管数量多少)
- 检查每一行的cost值是否有超出[min, max]范围的情况,标记异常行
- 提取所有异常行组成新的DataFrame
代码实现与解释
首先,我们先把示例数据准备好(你可以直接替换成自己的真实数据):
import pandas as pd # 创建示例df1 df1 = pd.DataFrame({ 'route_no': ['0010', '0011', '0012', '0013', '0014'], 'cost_h1': [20, 30, 67, 34, 44], 'cost_h2': [22, 25, 68, 33, 42], 'cost_h3': [21, 23, 68, 31, 40], 'cost_h4': [23, 31, 69, 30, 39], 'cost_h5': [26, 33, 65, 35, 50], 'max': [26, 33, 69, 35, 50], 'min': [20, 23, 67, 31, 39], 'location': ['NY', 'CA', 'GA', 'MO', 'WA'] }) # 创建示例df2 df2 = pd.DataFrame({ 'route_no': ['0020', '0021', '0023', '0022', '0025', '0030', '0032', '0034'], 'cost_h1': [19, 31, 66, 34, 41, 19, 37, 40], 'cost_h2': [27, 22, 67, 33, 42, 26, 31, 41], 'cost_h3': [21, 23, 68, 31, 40, 20, 31, 39], 'cost_h4': [24, 30, 70, 30, 39, 24, 20, 39], 'cost_h5': [20, 33, 65, 35, 50, 20, 35, 50], 'location': ['NY', 'CA', 'GA', 'MO', 'WA', 'NY', 'MO', 'WA'] })
步骤1:提取阈值映射表
从df1中取出location、max、min三列,并且确保每个location只保留一组阈值(如果df1中同一个location有多行,这里会自动保留第一组,你也可以根据需求改成取均值/最大阈值等):
# 提取每个location对应的阈值,去重确保唯一映射 thresholds = df1[['location', 'max', 'min']].drop_duplicates()
步骤2:合并阈值到df2
用merge操作把阈值表和df2按location关联,确保df2的每一行都拿到对应的阈值:
# 左连接保留df2的所有行,即使没有匹配的location df2_with_threshold = df2.merge(thresholds, on='location', how='left')
步骤3:动态识别所有cost列
通过列名前缀cost_h来筛选,不管有多少个cost列都能自动识别:
# 筛选所有以cost_h开头的列 cost_cols = [col for col in df2.columns if col.startswith('cost_h')]
步骤4:标记异常行
对每一行的cost值进行检查,只要有一个值超出[min, max]范围,就标记为异常行。同时我们也处理「没有匹配到阈值」的情况(比如df2里有个location在df1中不存在,这时候max/min会是NaN,我们也把这类行标记为异常):
# 检查每个cost值是否超出阈值范围 df2_with_threshold['is_outlier'] = df2_with_threshold.apply( lambda row: any(val < row['min'] or val > row['max'] for val in row[cost_cols]), axis=1 ) # 补充:把没有匹配到阈值(max/min为NaN)的行也标记为异常 df2_with_threshold['is_outlier'] = df2_with_threshold['is_outlier'] | df2_with_threshold[['max', 'min']].isna().any(axis=1)
步骤5:提取异常行
最后把标记为异常的行筛选出来,去掉临时的标记列:
# 提取所有异常行,并移除标记列 outlier_df = df2_with_threshold[df2_with_threshold['is_outlier']].drop(columns=['is_outlier']) # 查看结果 print("异常行结果:") print(outlier_df)
运行结果
执行后你会得到如下异常行(对应示例数据中超出阈值的行):
route_no cost_h1 cost_h2 cost_h3 cost_h4 cost_h5 location max min 0 0020 19 27 21 24 20 NY 26 20 1 0021 31 22 23 30 33 CA 33 23 2 0023 66 67 68 70 65 GA 69 67 5 0030 19 26 20 24 20 NY 26 20 6 0032 37 31 31 20 35 MO 35 31
这个方案完全适配cost列数量变化的场景,不管你有5个还是50个cost列,代码都能自动识别并检查~
备注:内容来源于stack exchange,提问作者S_Scouse
相关产品推荐
相关产品推荐

