如何用Polars生成按区间求和而非计数的直方图?
用Polars统计分组时间区间的总耗时(补全零值区间)
解决思路
核心是先明确所有时间区间,补全分组与区间的全部组合(解决零值区间缺失问题),最后聚合得到紧凑的总耗时列表。
具体实现步骤
1. 准备示例数据
import polars as pl # 构造测试数据集 df = pl.DataFrame({ "nam": ["A", "A", "B", "B", "B"], "ela": [15, 35, 50, 55, 70] })
2. 定义时间区间(可与hist对齐)
如果需要和hist函数的区间完全一致,可先通过hist提取bins;也可手动指定:
# 手动指定区间边界,示例为[0,20), [20,40), [40,60), [60,80) bins = [0, 20, 40, 60, 80] # 若要对齐hist的区间,可先提取: # hist_bins = df.group_by("nam").agg(pl.col("ela").hist(bins=4)).row(0)["ela"]["bins"] # bins = hist_bins
3. 对耗时字段分箱
使用pl.cut给ela字段分箱,保留区间断点方便后续匹配:
df_with_bins = df.with_columns( pl.col("ela").cut(bins=bins, include_breaks=True).alias("ela_bin") )
4. 分组计算区间总耗时
按nam和分箱后的区间分组,统计每个区间的ela总和:
agg_df = df_with_bins.group_by(["nam", "ela_bin"]).agg( pl.col("ela").sum().alias("total_ela") )
5. 补全零值区间
生成所有nam与区间的笛卡尔积,左连接聚合结果并填充空值为0,确保每个分组的所有区间都有数据:
# 获取所有唯一分组和所有区间 unique_nams = df["nam"].unique().to_list() all_bins = pl.select(pl.cut([], bins=bins, include_breaks=True))["breakpoint"].to_list() # 生成所有可能的组合 full_combinations = pl.DataFrame({ "nam": pl.Series(unique_nams).repeat(len(all_bins)), "ela_bin": pl.Series(all_bins).repeat(len(unique_nams)).sort() }) # 左连接并填充缺失值为0 full_agg = full_combinations.join(agg_df, on=["nam", "ela_bin"], how="left").fill_null(0)
6. 整理为紧凑格式
按nam分组,将每个区间的总耗时整理为列表,得到和hist类似的输出结构:
final_result = full_agg.sort(["nam", "ela_bin"]).group_by("nam").agg( pl.col("total_ela").alias("total_ela_by_bin") ) print(final_result)
输出结果:
shape: (2, 2) ┌─────┬───────────────────┐ │ nam ┆ total_ela_by_bin │ │ --- ┆ --- │ │ str ┆ list[f64] │ ╞═════╪═══════════════════╡ │ A ┆ [15.0, 35.0, 0.0, 0.0] │ │ B ┆ [0.0, 0.0, 105.0, 70.0]│ └─────┴───────────────────┘
关键说明
- 之前用
cut+implode的问题在于:implode只会保留存在数据的区间,缺失的零值区间会被丢弃;通过笛卡尔积补全所有组合,再填充空值为0,就能解决这个问题。 - 若需要动态生成区间,可根据数据的最小/最大值自动划分,比如用
pl.col("ela").min()和pl.col("ela").max()结合numpy.linspace生成等距区间。
内容的提问来源于stack exchange,提问作者blitzkopf
相关产品推荐
相关产品推荐

