Pandas遍历DataFrame行按指定条件删除无效行的实现方法
问题核心逻辑
这个筛选需求本质是提取二维数据的帕累托最优非支配解集:对每一行数据,如果存在另一行数据在所有关注维度上都不比它差,且至少有一个维度严格更优,这行就是被支配的无效数据,需要剔除。
两个示例的规则只是维度优劣方向不同:
- 第一个5行样例:A列值越大越好,B列值越小越好,只要存在另一行A更大且B更小,当前行就删除
- 资源-得分业务场景:资源列值越小越好,得分列值越大越好,只要存在另一行资源不更多、得分不更低,且至少一项严格更优,当前行就删除
可直接运行的实现方案
用numpy广播做向量化运算,避免pandas逐行循环的性能问题,支持自定义两个维度的优劣方向,适配两种场景:
import pandas as pd import numpy as np def filter_non_dominated(df, col1, col2, col1_larger_better: bool, col2_larger_better: bool): """ 筛选非支配的有效行 参数说明: df: 输入的DataFrame col1: 第一个判断维度的列名 col2: 第二个判断维度的列名 col1_larger_better: 第一个维度是否值越大越好 col2_larger_better: 第二个维度是否值越大越好 """ val1 = df[col1].to_numpy() val2 = df[col2].to_numpy() # 统一把维度转换为「值越大越好」的方向,简化后续判断 if not col1_larger_better: val1 = -val1 if not col2_larger_better: val2 = -val2 # 广播计算所有行对的支配关系:dominates[j,i]为True代表第j行支配第i行 val1_ge = val1[:, None] >= val1[None, :] val2_ge = val2[:, None] >= val2[None, :] val1_gt = val1[:, None] > val1[None, :] val2_gt = val2[:, None] > val2[None, :] dominates = val1_ge & val2_ge & (val1_gt | val2_gt) # 只要被任意一行支配,就标记为待删除 drop_mask = dominates.any(axis=0) return df[~drop_mask].reset_index(drop=True)
场景1:资源-得分业务数据测试
df_biz = pd.DataFrame({ 'resources': [100,200,300,300,400,400,400,500,1000], 'score': [1,2,1,2,3,5,6,8,9] }) # 资源越小越好,得分越大越好 res_biz = filter_non_dominated(df_biz, 'resources', 'score', col1_larger_better=False, col2_larger_better=True) print(res_biz)
输出结果完全符合预期,剔除了所有被支配的无效行:
resources score 0 100 1 1 200 2 2 400 6 3 500 8 4 1000 9
场景2:5行3列样例测试
df_demo = pd.DataFrame({'A': [1,4,4,3,7], 'B': [1,2,2,6,4], 'C': [1,2,2,6,4]}) # A列越大越好,B列越小越好 res_demo = filter_non_dominated(df_demo, 'A', 'B', col1_larger_better=True, col2_larger_better=False) print(res_demo)
输出结果自动删除了A=3、B=6的无效行:
A B C 0 1 1 1 1 4 2 2 2 4 2 2 3 7 4 4
补充说明
- 实现里加了严格优判断:必须至少一个维度严格更好,另一个维度不更差才算支配,避免同值行被误删
- 向量化实现性能远高于逐行循环,万级以内的数据都可以毫秒级返回结果
- 如果后续需要加更多判断维度,只要按照同样的广播逻辑扩展维度判断条件即可
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

