如何在pandas中高效按行计算平均绝对偏差(MAD)
问题背景
本次使用的DataFrame片段如下,实际数据集维度为200000行×130列:
ID 1-jan 2-jan 3-jan 4-jan 1. 4 5 7 8 2. 2 0 1 9 3. 5 8 0 1 4. 3 4 0 0
需求为逐行计算每个值与对应行均值的绝对差值(即每个值对应的平均绝对偏差分量),最终输出格式参考如下:
ID 1-jan 2-jan 3-jan 4-jan mean 1. 4 5 7 8 12.5 1_MAD 8.5 7.5 5.5 4.5 2. 2 0 1 9 6 2_MAD 4 6 5 3 ...
原有逐行循环的实现代码在大数据量下耗时过长,代码如下:
new_df = pd.DataFrame() for rows in (df['ID']): new_df[str(rows) + '_mad'] = mad(df3.loc[row_value][1:]) new_df.T
其中mad为自定义函数,功能是计算每个值与对应行均值的绝对差值。
高效实现方案
不要用Python层逐行循环,pandas/numpy的向量化运算跑在底层C实现上,比手写循环快2~3个数量级,20w行规模的数据可以秒级出结果,实现逻辑如下:
- 先排除ID列,提取所有需要参与计算的数值列
- 按行批量计算均值,利用pandas广播机制直接批量求所有值和行均值的绝对差,全程无逐行Python循环
- 最后按要求把原数据行和MAD行交替拼接,得到目标格式
完整代码:
import pandas as pd import numpy as np # 提取除ID外的所有数值列 num_cols = df.columns.drop("ID") # 批量计算每行均值,写入原df的mean列 df["mean"] = df[num_cols].mean(axis=1) # 批量计算每个值与对应行均值的绝对差,生成MAD数据 mad_df = df[num_cols].sub(df["mean"], axis=0).abs() # 给MAD数据设置对应ID,格式为{原ID}_MAD mad_df["ID"] = df["ID"].astype(str) + "_MAD" # 按ID排序拼接原数据和MAD数据,得到交替排列的最终结果 result = ( pd.concat([df, mad_df], axis=0) .sort_values(by="ID", kind="stable") .reset_index(drop=True) )
性能注意事项
- 不要用
df.apply传入自定义mad函数逐行计算,apply本质还是Python层循环,性能和手写for循环没有本质差异,大数据量下一样会严重卡顿。 - 上述实现对20w行×130列的数据集,普通消费级CPU上运行耗时不超过1秒。如果追求极致性能,可以把数值列转为numpy数组计算,还能再提10%左右的速度,对当前数据规模来说感知不强。
- 排序时指定
kind="stable"是为了保证相同ID前缀的原数据行排在MAD行前面,和需求格式一致。
内容的提问来源于stack exchange,提问作者skiddy
相关产品推荐
相关产品推荐

