You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用groupby transform高效计算2018-2019年各分组的平均比率(适用于超大型数据集)

如何利用groupby transform高效计算2018-2019年各分组的平均比率(适用于超大型数据集)

嘿,你的需求我完全get到了——处理200M行的超大型数据集,还想尽量用transform或者更高效的方式替代嵌套循环,甚至希望有简洁的一行式写法对吧?先不说别的,你原来的循环+transform+嵌套loc的方法虽然能跑,但对于千万级别的数据来说肯定会慢到离谱,因为每次lambda里的索引操作都是重复计算,嵌套循环还会进一步放大时间成本。

下面给你几个更高效的优化方案,从适合大数据的最优实践,到你想要的transform优化版,再到简洁的一行式,按需选择:


方案一:聚合+合并(大数据集最优解)

这是pandas处理这类分组统计需求的常规操作——先一次性聚合出所有需要的统计量,再合并回原数据。相比transform循环,它的效率提升几个数量级,因为聚合是批量计算,避免了对每一行重复执行操作:

# 1. 筛选2018-2019年数据,按group+year聚合foo/bar的总和
agg_data = df[df['year'].isin([2018, 2019])].groupby(['group', 'year'])[['foo', 'bar']].sum()

# 2. 计算每个分组每年的比率
agg_data['rate'] = agg_data['foo'] / agg_data['bar']

# 3. 转成宽格式(把年份从行转成列),方便后续合并
wide_agg = agg_data.unstack('year').swaplevel(axis=1).sort_index(axis=1)
# 重命名列,让命名更直观(比如foo_2018、rate_2019)
wide_agg.columns = [f'{col}_{year}' for year, col in wide_agg.columns]

# 4. 计算每个分组两年的平均比率
wide_agg['2018_2019_avg_rate'] = wide_agg[['rate_2018', 'rate_2019']].mean(axis=1)

# 5. 合并回原数据,每个group的所有行都会带上对应的统计值
df = df.merge(wide_agg, on='group', how='left')

这个方案全程没有循环,所有操作都是pandas的向量化原生操作,对于200M行的数据集,性能会比你原来的方法好太多。


方案二:优化后的transform写法

如果你坚持想用transform实现,我把你原来的代码做了简化,去掉了lambda里嵌套loc的低效操作,直接在分组后的列上过滤求和:

# 批量处理foo和bar的年度总和
for col in ['foo', 'bar']:
    for year in [2018, 2019]:
        df[f'{col}_{year}_total'] = df.groupby('group')[col].transform(
            lambda x: x[df.loc[x.index, 'year'] == year].sum()
        )

# 计算年度比率和两年平均比率
for year in [2018, 2019]:
    df[f'rate_{year}'] = df[f'foo_{year}_total'] / df[f'bar_{year}_total']

df['2018_2019_avg_rate'] = df[['rate_2018', 'rate_2019']].mean(axis=1)

这个写法相比你原来的版本,去掉了where和dropna的额外开销,直接对分组后的目标列过滤求和,效率会有明显提升。


方案三:一行式(满足简洁需求)

如果你追求极致简洁,这里把方案一的步骤用链式调用串成了一行,结果和方案一完全一致,适合喜欢简洁代码的场景:

df = df.merge(
    df[df['year'].isin([2018,2019])].groupby(['group','year'])[['foo','bar']].sum()
    .assign(rate=lambda x: x['foo']/x['bar'])
    .unstack('year').swaplevel(axis=1).sort_index(axis=1)
    .pipe(lambda x: x.set_axis([f'{c}_{y}' for y,c in x.columns], axis=1))
    .assign(2018_2019_avg_rate=lambda x: x[['rate_2018','rate_2019']].mean(axis=1)),
    on='group', how='left'
)

补充:你原来的方法效率低的原因

你原来的transform写法里,lambda x: df.loc[x.where(x==y).dropna().index, col].sum()存在几个关键问题:

  1. 对每个分组的每一行都重复执行loc索引,200M行的话会产生巨量的冗余计算
  2. 嵌套的列循环+年份循环进一步放大了时间成本
  3. where和dropna的操作完全可以通过直接过滤分组后的列来避免

对于200M行的超大型数据集,我强烈推荐方案一的聚合+合并法,这是pandas处理这类分组统计需求的最优实践,既能保证效率,又能保证代码的可读性。

备注:内容来源于stack exchange,提问作者amance

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 18:09:36