Polars 本身不提供直接名为 row_number() 或 row_count() 的表达式,但可通过 pl.int_range() 配合 over() 实现按组编号的行序生成,完美替代 SQL 中的 ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...) 语义。
polars 本身不提供直接名为 `row_number()` 或 `row_count()` 的表达式,但可通过 `pl.int_range()` 配合 `over()` 实现按组编号的行序生成,完美替代 sql 中的 `row_number() over (partition by ... order by ...)` 语义。
在 Polars 中,若需为每个分组内的行分配连续递增的序号(如“某客户当日订单的第1单、第2单…”),核心思路是:
在每个窗口内生成从 0(或 1)开始的整数序列
。pl.int_range() 正是为此设计的表达式——它可动态生成指定长度的等差整数序列;结合 .over("group_col"),即可按分组分别生成独立计数。
以下是最常用且推荐的写法(从 0 开始编号):
若需严格对齐 SQL 的 ROW_NUMBER()(即从 1 开始编号),只需将起始值设为 1,并调整长度:
⚠️
重要注意事项
:
pl.int_range(start, end) 生成的是 [start, end) 区间的整数,因此 pl.len() 返回当前窗口行数 n,int_range(0, pl.len()) 生成 0,1,...,n-1;int_range(1, pl.len()+1) 生成 1,2,...,n。
此方法
不依赖原始顺序
——它按数据在 DataFrame 中的物理位置编号。若需按业务逻辑排序后再编号(例如“按下单时间排序后编号”),必须先用 sort() 显式排序,再应用 int_range().over(...):
此外,若需实现 RANK() 或 DENSE_RANK()(处理并列情况),则应使用 pl.Expr.rank(method=...),而非 int_range。例如:
综上,pl.int_range().over(...) 是 Polars 中实现 ROW_NUMBER() 语义最简洁、高效且表达力强的方案,适用于绝大多数需要分组行号的场景。
import polars as pl
df = pl.DataFrame({
"client_id": ["C001", "C001", "C001", "C002", "C002", "C003"],
"order_time": ["08:30", "10:15", "14:20", "09:00", "16:45", "11:22"],
"amount": [299.99, 149.50, 399.00, 89.99, 549.99, 219.99],
})
# 按 client_id 分组,为每组内行分配 0-based 序号(等价于 ROW_NUMBER() - 1)
df = df.with_columns(
pl.int_range(0, pl.len()).over("client_id").alias("row_number_0based")
)df = df.with_columns(
pl.int_range(1, pl.len() + 1).over("client_id").alias("row_number")
)df_sorted = df.sort(["client_id", "order_time"])
df_ranked = df_sorted.with_columns(
pl.int_range(1, pl.len() + 1).over("client_id").alias("order_seq")
)# 按金额降序排名,相同金额同名次(跳过后续名次)
df.with_columns(
pl.col("amount").rank("ordinal", descending=True).over("client_id").cast(pl.Int32).alias("rank_ordinal")
)