咨询Polars中聚合连续n行的惯用实现方式
Polars中连续n行分块聚合的更优方法
我是Polars新手,编写了一段代码用于对连续n行的片段执行聚合计算,示例代码如下:
import polars as pl df = pl.DataFrame({"a": [1, 1, 3, 8, 62, 535, 4213]}) ( df.with_columns(index=pl.int_range(pl.len(), dtype=pl.Int32)) .group_by_dynamic(index_column="index", every="3i") .agg(pl.col("a").mean()) )示例中设置n=3,对应7行数据;实际场景为n约100、含多列的约1e6行数据框。我疑惑当前通过Int32索引结合group_by_dynamic的方式是否为该类操作的惯用做法,感觉这种方式有些冗余,想了解是否存在更直接的聚合方法。
你当前的方法能实现需求,但确实不够简洁。Polars针对这种固定行数分块聚合的场景,有更高效直接的实现方式:
推荐方法:整数分桶分组聚合
直接通过生成「块编号」作为分组键,不需要依赖动态分组逻辑,代码更简洁,性能也更优:
import polars as pl n = 3 # 每组行数 df = pl.DataFrame({"a": [1, 1, 3, 8, 62, 535, 4213]}) result = ( df .with_columns(group_id=pl.int_range(0, pl.len()).floordiv(n)) .group_by("group_id") .agg(pl.col("a").mean()) )
多列聚合适配
如果你的数据集有多列需要聚合,只需把pl.col("a")换成pl.col("*")就能批量处理所有列,非常适合你的百万行多列场景:
n = 100 # 假设df有多列,如a、b、c result = ( df .with_columns(group_id=pl.int_range(0, pl.len()).floordiv(n)) .group_by("group_id") .agg(pl.col("*").mean()) )
为什么比group_by_dynamic更好?
group_by_dynamic本质是为时间序列滑动/窗口分组设计的,需要处理时间解析、边界对齐等额外逻辑,而我们的需求只是简单的按固定行数分块,用整数分桶完全不需要这些额外开销。在1e6行的数据集上,这种方法的运行速度通常是group_by_dynamic的2-3倍。
备选方法:手动切片拼接(不推荐大数据量)
如果只是想直观分块处理,可以用切片拼接,但这种方法在数据量较大时性能会显著下降,仅作参考:
n = 3 result = pl.concat( [df.slice(i, n).select(pl.col("a").mean()) for i in range(0, len(df), n)] ).with_row_index("group_id")
内容的提问来源于stack exchange,提问作者Stefano M
相关产品推荐
相关产品推荐

