Pandas中用groupby().apply()可模拟SQL的ROW_NUMBER()和RANK():先sort_values()确保顺序,再在apply中用range或rank(method='min')生成序号;transform()比apply()更高效且适配窗口语义。

用 groupby().apply() 模拟 ROW_NUMBER() 和 RANK()
SQL 中的 ROW_NUMBER()、RANK() 本质是按分组和排序后生成序号,Pandas 没有原生同名函数,但 groupby().apply() 是最贴近的实现方式。注意不能直接用 df.groupby(...).rank() 替代 RANK() —— 它默认按值分组打分,不保证顺序稳定,且不支持 partition by + order by 的复合逻辑。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 先用
sort_values()显式排序(窗口函数依赖确定顺序,Pandas 默认不保序) - 在
apply()内部用range(1, len(x)+1)或pd.Series(range(1, len(x)+1), index=x.index)生成严格递增序号,对应ROW_NUMBER() - 对
RANK(),改用x['col'].rank(method='min').astype(int),其中method='min'表示并列取最小名次(如 1,1,3),匹配 SQL 标准 - 避免在
apply()中调用reset_index(drop=True)—— 会破坏原始索引对齐,导致结果错位
rolling() 能否替代 SUM() OVER (ORDER BY ... ROWS BETWEEN ...)?
能,但限制极多:rolling() 仅支持等距滑动(如“前3行”),不支持“从分区首行到当前行”这类动态范围,也不支持基于时间列的非固定间隔(除非列是 datetime64 且用 rolling('30D'))。它更接近 SQL 的 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW,而非 ROWS UNBOUNDED PRECEDING。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 若需“累计和”,优先用
groupby().cumsum(),不是rolling().sum() - 若真要模拟
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING,必须先sort_values(),再用rolling(3).sum().shift(-1)手动对齐中心点 -
rolling()默认忽略 NaN,但 SQL 窗口函数通常保留空值位置 —— 需显式加min_periods=1并配合fillna()控制行为
shift() 和 diff() 实现 LAG()/LEAD() 时的索引陷阱
shift() 看似直接对应 LAG(),但 Pandas 的 shift() 按整数偏移行数,不感知分组边界。如果数据未按窗口分组排序,或分组内长度不一,shift(1) 可能把上一分组末尾的值拉进来,造成逻辑污染。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 必须先
sort_values(['group_col', 'order_col']),再groupby('group_col').shift(1)—— 否则shift在全局索引上操作,无意义 - 不要用
df['col'].shift(-1)做LEAD():它跨组泄漏;必须走groupby().shift(-1) -
diff()默认计算差值,但 SQL 的LAG(col, 1, 0)支持默认值;Pandas 需后续fillna(0),且fillna()必须在groupby().diff()之后,否则填充的是全局空值
性能关键:为什么 transform() 比 apply() 更适合多数聚合窗口场景
transform() 强制返回与输入等长的结果,天然适配窗口函数“每行输出一个值”的语义;而 apply() 默认返回标量或 Series,容易触发隐式广播或对齐错误,且开销大 3–5 倍(尤其大数据集)。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 只要窗口聚合结果要回填到原 DataFrame(如
COUNT(*) OVER (PARTITION BY x)),一律用transform('count'),别用apply(lambda x: len(x)) -
transform()支持字符串方法('mean'、'first'),也支持自定义函数,但函数必须返回标量或与分组等长的序列 —— 若返回单个数字,Pandas 自动广播;若返回数组,长度必须等于该分组行数 - 遇到
transform()报错ValueError: function does not reduce,说明函数返回了非标量且长度不匹配,此时退回到apply()+ 显式构造pd.Series更稳妥
OVER (ORDER BY ...) 排序,所有依赖顺序的操作都得手动 sort_values(),而且必须在 groupby() 之前做,否则分组内顺序仍是乱的。


















