如何在Python中无循环对DataFrame按个体计算加权平均值
问题描述
现有如下DataFrame:
Individual stop x y z time 0 23 1 20 27 4 21 1 23 2 23 24 13 63 2 1756 2 5 41 73 12 3 1756 3 7 42 72 6 4 1756 4 4.5 39 72 45 5 1756 4 3 50 73 98 6 2153 2 121 12 6 33 7 2153 3 122.5 2 6 0 8 3276 1 54 33 -12 0 9 5609 1 -2 44 -32 56 10 5609 2 8 44 -32 23 11 5609 5 102 -23 16 76
需按Individual分组,计算x、y、z列以time为权重的加权平均值,生成如下格式的新DataFrame:
Individual bar_x bar_y bar_z 0 23 22.5 24.75 10.75 2 1756 3.72 45.96 72.68 6 2153 121 12 6 9 5609 50.48 11.15 24.16
此前通过循环实现,但DataFrame数据量增大时运行极慢,求基于pandas的高效解决方案。
高效解决方案
推荐使用pandas的向量化分组操作实现,避免循环带来的性能损耗,以下是两种高效实现方式:
方法一:分组聚合计算(简洁易读)
import pandas as pd # 构造原始DataFrame(实际场景可直接读取数据) df = pd.DataFrame({ 'Individual': [23,23,1756,1756,1756,1756,2153,2153,3276,5609,5609,5609], 'stop': [1,2,2,3,4,4,2,3,1,1,2,5], 'x': [20,23,5,7,4.5,3,121,122.5,54,-2,8,102], 'y': [27,24,41,42,39,50,12,2,33,44,44,-23], 'z': [4,13,73,72,72,73,6,6,-12,-32,-32,16], 'time': [21,63,12,6,45,98,33,0,0,56,23,76] }) # 按Individual分组计算加权平均,过滤time总和为0的无效分组 result = df.groupby('Individual').apply( lambda g: pd.Series({ 'bar_x': (g['x'] * g['time']).sum() / g['time'].sum() if g['time'].sum() != 0 else None, 'bar_y': (g['y'] * g['time']).sum() / g['time'].sum() if g['time'].sum() != 0 else None, 'bar_z': (g['z'] * g['time']).sum() / g['time'].sum() if g['time'].sum() != 0 else None }) ).dropna().reset_index().round(2) print(result)
方法二:预计算加权列(性能最优,适合大数据量)
这种方式完全基于pandas的向量化操作,避免apply的逐组处理逻辑,在数据量较大时性能提升更显著:
import pandas as pd df = pd.DataFrame({ 'Individual': [23,23,1756,1756,1756,1756,2153,2153,3276,5609,5609,5609], 'stop': [1,2,2,3,4,4,2,3,1,1,2,5], 'x': [20,23,5,7,4.5,3,121,122.5,54,-2,8,102], 'y': [27,24,41,42,39,50,12,2,33,44,44,-23], 'z': [4,13,73,72,72,73,6,6,-12,-32,-32,16], 'time': [21,63,12,6,45,98,33,0,0,56,23,76] }) # 预计算各列的加权值 df['x_weighted'] = df['x'] * df['time'] df['y_weighted'] = df['y'] * df['time'] df['z_weighted'] = df['z'] * df['time'] # 分组求和 grouped = df.groupby('Individual').agg( x_weighted_sum=('x_weighted', 'sum'), y_weighted_sum=('y_weighted', 'sum'), z_weighted_sum=('z_weighted', 'sum'), time_sum=('time', 'sum') ) # 计算加权平均并过滤无效分组 result = grouped[grouped['time_sum'] != 0].assign( bar_x=lambda x: x['x_weighted_sum'] / x['time_sum'], bar_y=lambda x: x['y_weighted_sum'] / x['time_sum'], bar_z=lambda x: x['z_weighted_sum'] / x['time_sum'] )[['bar_x', 'bar_y', 'bar_z']].reset_index().round(2) print(result)
两种方法最终输出结果均与目标格式一致,且性能远优于循环实现。
内容的提问来源于stack exchange,提问作者Aleksander
相关产品推荐
相关产品推荐

