如何高效对Polars DataFrame的大量重叠分组执行聚合操作
问题:高效计算Polars中多布尔列的聚合函数结果
我有一个Polars DataFrame,包含列x、y、c_1、c_2……c_K,其中K的取值较大(约1000或2000)。每个c_i都是布尔列,我需要对c_i为True的行计算聚合函数f(x, y)(例如f(x,y) = x.sum() * y.sum())。
目前的实现方式如下:
ds.select([ f(pl.col("x").filter(pl.col(f"c_{i+1}")), pl.col("y").filter(pl.col(f"c_{i+1}"))) for i in range(K) ])
由于K值较大,上述查询效率偏低(需执行两次filter操作)。请问实现该需求的推荐、最高效且最优雅的方式是什么?
补充说明:
以下是可运行示例(代码见下方)及对应测试结果。结论:当前最优方案为方法1。
| 序号 | 方法 | 耗时 |
|---|---|---|
| 1 | 重复filter | 409ms |
| 2 | pl.concat | 29.6s(约慢70倍) |
| 2* | pl.concat(lazy模式) | 1.27s(慢3倍) |
| 3 | unpivot+聚合 | 1分17秒 |
| 3* | unpivot+聚合(lazy模式) | 1分17秒(与方法3耗时相同) |
import polars as pl import polars.selectors as cs import numpy as np rng = np.random.default_rng() def f(x,y): return x.sum() * y.sum() N = 2_000_000 K = 1000 dat = dict() dat["x"] = np.random.randn(N) dat["y"] = np.random.randn(N) for i in range(K): dat[f"c_{i+1}"] = rng.choice(2, N).astype(np.bool_) tmpds = pl.DataFrame(dat) ## Method 1 tmpds.select([ f( pl.col("x").filter(pl.col(f"c_{i+1}")), pl.col("y").filter(pl.col(f"c_{i+1}"))) .alias(f"f_{i+1}") for i in range(K) ]) ## Method 2 pl.concat([ tmpds.filter(pl.col(f"c_{i+1}")).select(f(pl.col("x"), pl.col("y")).alias(f"f_{i+1}")) for i in range(K) ], how="horizontal") ## Method 2* pl.concat([ tmpds.lazy().filter(pl.col(f"c_{i+1}")).select(f(pl.col("x"), pl.col("y")).alias(f"f_{i+1}")).collect() for i in range(K) ], how="horizontal") ## Method 3 ( tmpds .unpivot(on=cs.starts_with("c"), index=["x", "y"]) .filter("value") .group_by("variable") .agg( f(pl.col("x"), pl.col("y")) ) ) ##Method 3* ( tmpds .lazy() .unpivot(on=cs.starts_with("c"), index=["x", "y"]) .filter("value") .group_by("variable", maintain_order=True) .agg( f(pl.col("x"), pl.col("y")) ) .collect() )
内容的提问来源于Stack Exchange,提问作者Kevin
相关产品推荐
相关产品推荐

