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

如何在Polars中预计算高开销表达式(含group_by及通用场景)

Polars分组聚合高效复用高开销计算的优化方案

一、GroupBy场景的优化方案

1. 简化列表聚合写法(最直接的优化)

你的预计算思路是对的,但可以把冗余的.list.eval(pl.element().<aggfunc>()).arr.first()简化为Polars内置的列表聚合函数,可读性和效率都更好:

%%timeit
(
    df
    .group_by('column_0')
    .agg(expensive.alias('expensive'))  # 仅计算一次高开销的expensive
    .select(
        'column_0',
        pl.col('expensive').list.sum().alias('sum'),
        pl.col('expensive').list.median().alias('median'),
        *[pl.col('expensive').list.max().alias(f'max{x}') for x in range(10)]
    )
)

这种写法去掉了冗余的list.eval和arr.first(),直接用list.sum()/list.median()等方法,逻辑更直观,性能和你之前的预计算版本几乎一致。

2. 使用map_groups实现更清晰的逻辑

如果追求代码的可读性和逻辑连贯性,可以用map_groups,在每个分组的子DataFrame中仅计算一次expensive,然后直接执行各种聚合操作,完全避免列表操作:

%%timeit
(
    df
    .group_by('column_0')
    .map_groups(lambda group_df: (
        group_df
        .with_columns(expensive)  # 子DF内仅计算一次expensive
        .select(
            pl.col('column_0').first(),
            pl.col('expensive').sum().alias('sum'),
            pl.col('expensive').median().alias('median'),
            *[pl.col('expensive').max().alias(f'max{x}') for x in range(10)]
        )
    ))
)

这种写法的逻辑和传统的Pandas分组处理更接近,代码可读性拉满。需要注意的是,map_groups的性能略低于纯表达式写法,但对于大多数场景来说完全够用,且逻辑更清晰。

3. 进阶:用pl.struct封装计算(适合复杂场景)

如果你的高开销计算需要生成多个中间结果,还可以用pl.struct把它们封装起来,一次性计算后再拆分使用:

# 假设需要同时计算两个高开销指标
complex_expensive = pl.struct(
    e1=pl.col('column_1').cum_prod().ewm_std(span=10),
    e2=pl.col('column_2').cum_sum().ewm_mean(span=5)
).alias('complex_expensive')

(
    df
    .group_by('column_0')
    .agg(complex_expensive)
    .select(
        'column_0',
        pl.col('complex_expensive').list.struct.field('e1').list.sum().alias('e1_sum'),
        pl.col('complex_expensive').list.struct.field('e2').list.median().alias('e2_median'),
    )
)

二、Select场景下的复用方案

如果不需要分组聚合,而是要在全局或窗口范围内复用高开销计算,可以通过以下方式实现:

1. 全局/逐行复用

直接在select中先计算一次高开销列,再基于它生成多个衍生列,最后可选择删除原列:

df_result = df.select(
    '*',
    expensive.alias('expensive'),
    # 基于expensive生成多个衍生列
    pl.col('expensive').sum().alias('global_sum'),
    pl.col('expensive').rank().alias('rank'),
    pl.col('expensive').sqrt().alias('sqrt_expensive')
).drop('expensive')  # 不需要保留中间列时删除

2. 窗口范围内复用

如果需要在窗口(如按column_0分区)内复用计算,可以结合over窗口函数,仅计算一次窗口内的expensive,再生成多个聚合结果:

df_result = df.select(
    '*',
    expensive.alias('expensive'),
    pl.col('expensive').sum().over('column_0').alias('sum_over_col0'),
    pl.col('expensive').median().over('column_0').alias('median_over_col0'),
    pl.col('expensive').max().over('column_0').alias('max_over_col0')
).drop('expensive')

性能对比总结

  • 纯表达式列表聚合:性能最优,适合对效率要求极高的场景
  • map_groups:可读性最优,性能略逊但足够应对多数业务场景
  • 窗口函数方案:适合需要保留原行数据的场景,避免分组后再合并

内容的提问来源于stack exchange,提问作者iliya malecki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 09:35:35