当前位置:首页>python>Python Polars 分组聚合与窗口分享(一)

Python Polars 分组聚合与窗口分享(一)

  • 2026-09-02 18:54:17
Python Polars 分组聚合与窗口分享(一)
在之前的内容中,我们学习了Polars库的基础,今天来看看分组聚合与窗口分析。首先我们先模拟一下电商数据
import polars as plimport numpy as npfrom datetime import datetime, timedeltanp.random.seed(42)n = 10000df = pl.DataFrame({    "order_id": range(1, n+1),    "user_id": np.random.randint(1000, 2000, n),    "region": np.random.choice(["华北", "华东", "华南", "华西"], n),    "category": np.random.choice(["手机", "电脑", "配件", "服装"], n),    "amount": np.round(np.random.lognormal(5, 1, n), 2),  # 对数正态,更像真实价格    "quantity": np.random.randint(1, 5, n),    "order_date": [        datetime(2024, 1, 1) + timedelta(days=int(x), hours=int(y))        for x, y in zip(np.random.randint(0, 90, n), np.random.randint(0, 24, n))    ]})print(f"数据就绪:{n} 条订单记录")print(df.head(3))
根据模拟数据,我们来进行分析,查看每个区域的销售情况,还要知道谁是区域的Top销售
region_report = (    df    .group_by("region")    .agg([        # 基础指标        pl.count().alias("订单数"),        pl.sum("amount").alias("总销售额"),        pl.mean("amount").alias("客单价"),        # 用户指标(去重计数)        pl.col("user_id").n_unique().alias("购买人数"),        # 分布指标(看出水不水)        pl.median("amount").alias("中位数"),        pl.std("amount").alias("标准差"),        pl.max("amount").alias("最大单"),        # 计算指标(表达式直接算)        (pl.sum("amount") / pl.col("user_id").n_unique()).alias("人均贡献")    ])    .sort("总销售额", descending=True))print("区域销售体检表:")print(region_report)
这时候如果要分组内再次筛选,例如统计大额订单(大于500元)的情况:
conditional_report = (    df    .group_by("region")    .agg([        pl.count().alias("总订单数"),        # 只统计大额订单        pl.col("amount").filter(pl.col("amount") > 500).count()        .alias("大额订单数"),        pl.col("amount").filter(pl.col("amount") > 500).sum()        .alias("大额订单金额"),        # 占比(注意表达式可以互相引用)        (pl.col("amount").filter(pl.col("amount") > 500).sum() / pl.sum("amount") * 100)        .round(2)        .alias("大额贡献占比%")    ]))print("大额订单分析:")print(conditional_report)
pl.col().filter()可以在聚合里做条件筛选,不用先filter再group_by那么绕。
接下来我们要看的是窗口函数,如果我们要做每个区域的Top3 订单,最笨的方式是按区域分组,取最大,再合并.... ,而用窗口函数,很容易实现
df_ranked = (    df    .with_columns(        # dense:相同金额同排名,不跳号(1,2,2,3)        pl.col("amount").rank(method="dense", descending=True)        .over("region")        .alias("区域排名"),        # 区域订单总数(用于计算百分位)        pl.col("amount").count().over("region").alias("区域订单数")    )    .with_columns(        # 手动计算百分位排名 (0-1之间)        # 使用 average 排名来处理并列值        (            (pl.col("amount").rank(method="average", descending=True)             .over("region") - 1) /             (pl.col("区域订单数") - 1)        ).alias("区域百分位")    )    .with_columns(        # 直接标记 Top 3        pl.when(pl.col("区域排名") <= 3)        .then(pl.lit("🏆 区域 Top 3"))        .otherwise(pl.lit("其他"))        .alias("荣誉标记")    )    .drop("区域订单数")  # 删除临时列)print(df_ranked)
窗口函数不止可以做排名,还能做累积统计:
# 按用户+时间排序,看消费累积user_growth = (    df    .sort(["user_id", "order_date"])  # 必须先排序!    .with_columns(        # 这是用户第几单?        pl.col("order_id").cum_count().over("user_id")        .alias("第N单"),        # 累计消费金额(LTV 雏形)        pl.col("amount").cum_sum().over("user_id")        .alias("累计消费"),        # 距离上次购买多久?(复购周期)        pl.col("order_date").diff().over("user_id").dt.total_days()        .alias("距上次购买天数")    ))print("用户 1001 的成长轨迹:")print(    user_growth    .filter(pl.col("user_id") == 1001)    .select(["order_date", "amount", "第N单", "累计消费", "距上次购买天数"]))
第N单看出用户购买频次,累积消费就是用户LTV,距离上次购买天数可以识别流失风险。
本次内容,就先到这里,在下次我们看看时间序列的分析。

最新文章

随机文章