如何动态筛选DataFrame中所有列值大于行平均值40%的行?
问题
现有一个包含ID列和数值列A、B、C的DataFrame,需要筛选出所有行中A、B、C的值均大于该行平均值40%的记录。示例DataFrame定义如下:
import pandas as pd df = pd.DataFrame([['AA',10,8,12],['BB',10,2,18],['CC',10,6,14]], columns=['ID','A', 'B', 'C']) print(df) # 输出: # ID A B C # 0 AA 10 8 12 # 1 BB 10 2 18 # 2 CC 10 6 14
具体逻辑:以每行A、B、C的平均值的40%作为阈值,若该行任意一列值低于该阈值则移除该行。例如第0行均值为10,阈值为4,所有列值均大于4;第1行B列值为2小于4,需移除;第2行所有列值均大于4。
尝试以下代码后得到空DataFrame:
df = df[(df[['A','B','C']] > (0.4*df[['A','B','C']].mean(axis=1))).all(1)] print(df) # 输出空表: # ID A B C
仅当硬编码阈值(如0.4*10)时代码有效:
df[(df[['A','B','C']] > 0.4*10).all(1)]
如何实现基于行平均值动态计算阈值的筛选?
解决方案
问题出在广播对齐上:df[['A','B','C']].mean(axis=1)返回的是行方向的Series(索引为行号,值为每行的均值*0.4),直接和3列的DataFrame比较时,Pandas会按列名对齐,导致每行的元素和错误的阈值比较,最终得到全False的布尔矩阵。
只需将阈值Series转换为列向量,就能让每行的每个元素与该行的阈值正确比较,有两种实现方式:
方法1:用[:, None]扩展维度
# 计算每行的阈值:均值*0.4,转为列向量 row_threshold = 0.4 * df[['A','B','C']].mean(axis=1)[:, None] # 筛选所有列值大于阈值的行 filtered_df = df[(df[['A','B','C']] > row_threshold).all(axis=1)] print(filtered_df)
方法2:用to_frame()转置
row_threshold = 0.4 * df[['A','B','C']].mean(axis=1).to_frame().T filtered_df = df[(df[['A','B','C']] > row_threshold).all(axis=1)] print(filtered_df)
输出结果
两种方法都会得到正确的筛选结果:
ID A B C 0 AA 10 8 12 2 CC 10 6 14
原理说明
将行方向的Series转为列向量后,Pandas会自动广播为与原DataFrame相同的形状(3行3列),此时每行的A、B、C值会和该行对应的阈值逐一比较,生成正确的布尔矩阵,再通过.all(axis=1)判断该行所有列是否都满足条件,最终筛选出符合要求的行。
内容的提问来源于stack exchange,提问作者cph_sto
相关产品推荐
相关产品推荐

