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

开发Polars分组描述统计扩展,简化group_by.agg统计量生成

实现Polars分组描述统计的自定义扩展

以下是一个演示用的Polars DataFrame:

import polars as pl

df = pl.DataFrame(
    {
        "groups": ["A", "A", "A", "B", "B", "B"],
        "values": [1, 2, 3, 4, 5, 6],
    }
)

当前使用group_by.agg()生成描述统计量的方式较为繁琐:

print(
    df.group_by("groups").agg(
        pl.len().alias("count"),
        pl.col("values").mean().alias("mean"),
        pl.col("values").std().alias("std"),
        pl.col("values").min().alias("min"),
        pl.col("values").quantile(0.25).alias("25%"),
        pl.col("values").quantile(0.5).alias("50%"),
        pl.col("values").quantile(0.75).alias("75%"),
        pl.col("values").max().alias("max"),
        pl.col("values").skew().alias("skew"),
        pl.col("values").kurtosis().alias("kurtosis"),
    )
)

输出结果:

shape: (2, 11)
┌────────┬───────┬──────┬─────┬───┬─────┬─────┬──────┬──────────┐
│ groups ┆ count ┆ mean ┆ std ┆ … ┆ 75% ┆ max ┆ skew ┆ kurtosis │
│ ---    ┆ ---   ┆ ---  ┆ --- ┆   ┆ --- ┆ --- ┆ ---  ┆ ---      │
│ str    ┆ u32   ┆ f64  ┆ f64 ┆   ┆ f64 ┆ i64 ┆ f64  ┆ f64      │
╞════════╪═══════╪══════╪═════╪═══╪═════╪═════╪══════╪══════════╡
│ B      ┆ 3     ┆ 5.0  ┆ 1.0 ┆ … ┆ 6.0 ┆ 6   ┆ 0.0  ┆ -1.5     │
│ A      ┆ 3     ┆ 2.0  ┆ 1.0 ┆ … ┆ 3.0 ┆ 3   ┆ 0.0  ┆ -1.5     │
└────────┴───────┴──────┴─────┴───┴─────┴─────┴──────┴──────────┘

我们可以通过扩展Polars的方法,实现两种更简洁的调用方式来得到相同结果:

  • 直接在DataFrame上调用:df.describe(by="groups", percentiles=[0.25, 0.5, 0.75], skew=True, kurt=True)
  • 在分组对象上调用:df.group_by("groups").describe(percentiles=[0.25, 0.5, 0.75], skew=True, kurt=True)

自定义扩展实现代码

from typing import Optional, List, Union
from polars import Expr

# 生成描述统计表达式的工具函数
def _get_describe_exprs(
    percentiles: List[float] = [0.25, 0.5, 0.75],
    skew: bool = False,
    kurt: bool = False
) -> List[Expr]:
    exprs = [
        pl.len().alias("count"),
        pl.col(pl.NUMERIC_DTYPES).mean().alias("mean"),
        pl.col(pl.NUMERIC_DTYPES).std().alias("std"),
        pl.col(pl.NUMERIC_DTYPES).min().alias("min"),
        pl.col(pl.NUMERIC_DTYPES).max().alias("max"),
    ]
    # 添加自定义分位数
    for p in percentiles:
        exprs.append(pl.col(pl.NUMERIC_DTYPES).quantile(p).alias(f"{int(p*100)}%"))
    # 添加偏度和峰度(按需开启)
    if skew:
        exprs.append(pl.col(pl.NUMERIC_DTYPES).skew().alias("skew"))
    if kurt:
        exprs.append(pl.col(pl.NUMERIC_DTYPES).kurtosis().alias("kurtosis"))
    return exprs

# 给DataFrame/LazyFrame添加describe方法
def df_describe(
    self,
    by: Optional[Union[str, List[str]]] = None,
    percentiles: List[float] = [0.25, 0.5, 0.75],
    skew: bool = False,
    kurt: bool = False
) -> pl.DataFrame:
    exprs = _get_describe_exprs(percentiles, skew, kurt)
    if by is not None:
        return self.group_by(by).agg(exprs)
    # 无分组时返回长格式统计结果
    return self.select(exprs).melt(variable_name="statistic")

# 给GroupBy/LazyGroupBy添加describe方法
def groupby_describe(
    self,
    percentiles: List[float] = [0.25, 0.5, 0.75],
    skew: bool = False,
    kurt: bool = False
) -> pl.DataFrame:
    exprs = _get_describe_exprs(percentiles, skew, kurt)
    return self.agg(exprs)

# 注册方法到Polars类
pl.DataFrame.describe = df_describe
pl.LazyFrame.describe = df_describe
pl.GroupBy.describe = groupby_describe
pl.LazyGroupBy.describe = groupby_describe

验证效果

现在就可以用两种方式调用:

方式1:DataFrame直接调用

result1 = df.describe(by="groups", percentiles=[0.25, 0.5, 0.75], skew=True, kurt=True)
print(result1)

方式2:分组后调用

result2 = df.group_by("groups").describe(percentiles=[0.25, 0.5, 0.75], skew=True, kurt=True)
print(result2)

两种方式都会输出和原始繁琐写法完全一致的结果。


扩展说明

  • 自动识别所有数值类型列,无需手动指定统计列
  • 支持自定义分位数列表,默认值为[0.25, 0.5, 0.75]
  • 通过skew和kurt参数控制是否计算偏度和峰度
  • 同时支持DataFrame、LazyFrame以及对应的分组对象

内容的提问来源于stack exchange,提问作者Kevin Li

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 16:52:39