如何在Polars中实现c1分组内的(c2,c3)组排名?
实现Polars分组内的(c2,c3)组合排名
方法一:使用窗口函数over()(推荐)
直接通过dense_rank()窗口函数,在c1分组内对(c2,c3)的唯一组合分配连续排名,再减1得到从0开始的编号,这是最简洁的实现方式:
import polars as pl df = pl.from_repr(""" ┌─────┬─────┬─────┐ │ c1 ┆ c2 ┆ c3 │ │ --- ┆ --- ┆ --- │ │ str ┆ str ┆ i64 │ ╞═════╪═════╪═════╡ │ a ┆ a ┆ 1 │ │ a ┆ a ┆ 1 │ │ a ┆ b ┆ 1 │ │ a ┆ c ┆ 1 │ │ d ┆ a ┆ 1 │ │ d ┆ b ┆ 1 │ └─────┴─────┴─────┘ """) result = df.with_columns( rank=pl.dense_rank().over("c1").order_by(["c2", "c3"]) - 1 ) print(result)
运行结果:
┌─────┬─────┬─────┬──────┐ │ c1 ┆ c2 ┆ c3 ┆ rank │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ str ┆ i64 ┆ i64 │ ╞═════╪═════╪═════╪══════╡ │ a ┆ a ┆ 1 ┆ 0 │ │ a ┆ a ┆ 1 ┆ 0 │ │ a ┆ b ┆ 1 ┆ 1 │ │ a ┆ c ┆ 1 ┆ 2 │ │ d ┆ a ┆ 1 ┆ 0 │ │ d ┆ b ┆ 1 ┆ 1 │ └─────┴─────┴─────┴──────┘
原理说明:
dense_rank():在c1分组内,对(c2,c3)的不同组合分配连续的排名(相同组合共享同一排名),默认从1开始。over("c1"):指定分组范围为c1的每个取值。order_by(["c2", "c3"]):确定排名的顺序(按c2、c3升序排列)。- 减1操作:将排名从1起始转换为0起始,匹配需求。
方法二:改进GroupBy+Join模式
基于你已掌握的全局排名思路修改,通过分组内生成唯一组合再分配排名,最后关联回原表:
result = df.join( # 按c1分组,提取每个组内的(c2,c3)唯一组合 df.group_by("c1", maintain_order=True) .agg(pl.col(["c2", "c3"]).unique().alias("pairs")) .explode("pairs") # 拆分结构体为c2、c3列,并在每个c1分组内生成从0开始的排名 .with_columns( c2=pl.col("pairs").struct.field("c2"), c3=pl.col("pairs").struct.field("c3"), rank=pl.int_range(0, pl.len()).over("c1") ) .drop("pairs"), on=["c1", "c2", "c3"] ) print(result)
此方法也能得到与期望一致的结果,但代码复杂度高于窗口函数方案。
内容的提问来源于stack exchange,提问作者ldrg
相关产品推荐
相关产品推荐

