如何加速Pandas中分组计算相对负效用的apply操作?
优化数百万条记录的相对负效用计算速度
原始数据
Person,utility,selected,innovation 2012001153_7_E02005533_1_2012002698,130.2333,yes,0 2012001153_7_E02005533_1_2012002698,110.33,no,1 2012001153_7_E02005533_1_2012002698,83,no,2 2012001153_7_E02005533_1_2012002698,-100,no,3 2012001153_7_E02005533_1_2012002698,49,no,4
需求
新增一列relativeDisUtilityToSelected,计算每条记录相对于同Person分组中selected == "yes"项的效用差值(当前记录utility减去选中项的utility)。
原方案问题
原代码通过groupby().apply()处理百万级数据时速度极慢,原因是apply()会逐组调用Python函数,带来大量运行开销:
def get_relativeUtilityToSelected(group): selected_utility = group[group['selected']=='yes']['utility'].values[0] group['relativeDisUtilityToSelected'] = group['utility'] - selected_utility return group df = df.groupby(['Person']).apply(get_relativeUtilityToSelected)
优化方案
利用Pandas的矢量化操作替代逐组循环,大幅提升处理速度,以下两种方法都可行:
方法1:使用transform()广播分组选中值
# 提取每个Person分组中选中项的utility,用transform广播到全组所有行 selected_utility = df[df['selected'] == 'yes'].groupby('Person')['utility'].transform('first') # 计算相对负效用,无匹配选中项的行自动生成NaN(可按需处理) df['relativeDisUtilityToSelected'] = df['utility'] - selected_utility
方法2:构建映射字典后用map()匹配
如果每个Person仅对应一条selected == "yes"的记录,可先构建映射字典再快速匹配:
# 生成Person到选中项utility的映射字典 selected_map = df[df['selected'] == 'yes'].set_index('Person')['utility'].to_dict() # 映射并计算差值 df['relativeDisUtilityToSelected'] = df['utility'] - df['Person'].map(selected_map)
输出结果
两种方法均能得到预期输出:
Person,utility,selected,innovation,relativeDisUtilityToSelected 2012001153_7_E02005533_1_2012002698,130.2333,yes,0,0 2012001153_7_E02005533_1_2012002698,110.33,no,1,-19.9033 2012001153_7_E02005533_1_2012002698,83,no,2,-47.2333 2012001153_7_E02005533_1_2012002698,-100,no,3,-230.2333 2012001153_7_E02005533_1_2012002698,49,no,4,-81.2333
性能说明
transform()和map()均为Pandas底层优化的矢量化操作,避免了Python层面的循环,处理百万级数据的速度比原apply()方案快10-100倍。- 若存在一个
Person对应多条selected == "yes"的记录,可将transform('first')改为transform('mean')等聚合方式,按需调整逻辑。
内容的提问来源于stack exchange,提问作者LearningSlowly
相关产品推荐
相关产品推荐

