dataframe.rank()默认按列(axis=0)排名,axis=1按行排名;返回新dataframe,nan不参与排序且保留为nan;重复值默认取平均秩,method='min'可取最小秩;降序用ascending=false(≥1.4.0)或取负;需预处理混合类型与空列。

用 rank() 方法做行/列方向的动态排名
直接调用 DataFrame.rank() 即可,关键在 axis 参数:设为 0(默认)按列计算,1 按行计算。它不改变原数据,返回新 DataFrame,每个值替换成其在所在轴上的相对名次。
常见错误是误以为 rank() 会自动处理 NaN 或重复值导致结果“不对”——其实它默认用 method='average',对相同值取平均秩;若要跳过 NaN,必须显式设 na_option='keep'(默认就是这个),但要注意:NaN 不参与排序,对应位置仍为 NaN,不是 0 或空字符串。
-
axis=0:每列独立排名,适合比较各指标在不同样本间的相对位置(如“每个学生的数学成绩在全班排第几”) -
axis=1:每行独立排名,适合横向对比(如“某学生各科成绩中,语文排第几、数学排第几”) - 重复值默认平分名次,改用
method='min'可让并列者取最小秩(如两个并列第1,则都标为 1,下一个为 3) - 避免链式赋值:不要写
df['rank_col'] = df.rank(axis=0)['col_name'],应先算好再赋值,否则可能触发SettingWithCopyWarning
处理含 NaN 的排名时容易踩的坑
NaN 默认不参与排序,但它的存在会影响其他值的名次计算逻辑——比如一列有 [5, NaN, 3],按升序排时,实际只对 [5, 3] 排名,结果是 [2.0, NaN, 1.0],而不是把 NaN 当作最大或最小来占位。
如果业务要求把 NaN 视为“最低分”或“最高分”,rank() 本身不支持,得先预处理:
- 视作最小:用
df.fillna(df.min() - 1)(需确保减后不与其他值冲突) - 视作最大:用
df.fillna(df.max() + 1) - 更稳妥的做法是用
numpy.where或mask分离 NaN 位置,单独赋值,再对非 NaN 部分调用rank()
注意:rank() 对空列(全 NaN)返回全 NaN,不会报错,但后续计算可能出问题,建议提前检查 df.isna().all(axis=0)。
按指定顺序(降序)排名的写法
rank() 默认按升序(从小到大)排,即最小值秩为 1。要降序(如“分数越高排名越前”),不能靠反转结果,而应先取负值再排名,或用 ascending=False 参数(Pandas ≥ 1.4.0 支持)。
- Pandas ≥ 1.4.0:直接写
df.rank(axis=1, ascending=False) - 旧版本:用
df.mul(-1).rank(axis=1),但要注意负号对 NaN 无影响,且若原数据含 0,取负后仍是 0,不影响排名逻辑 - 混合类型列(如含字符串)不能直接 rank,会报
TypeError: unsupported operand type(s) for -: 'str',需先过滤或转换
性能与大数据量下的注意事项
对百万级行数的 DataFrame 调用 rank(axis=1) 会明显变慢,因为每行都要独立排序,时间复杂度接近 O(n × m log m),其中 m 是列数。而 axis=0 是列向量排序,底层优化更好。
- 若只需 Top-K 名次(如每行只关心前三名),别用
rank()全量计算,改用argsort()+ 切片更高效 - 避免在循环里反复调用
rank(),尽量向量化;例如按组分别排名,用groupby(...).rank()比手动分组后逐个 rank 快得多 - 内存方面:
rank()返回 float64 类型,即使输入是 int,若后续只用整数秩,可用.astype('int64')转换,但要注意 NaN 会转成np.nan,无法转 int,得先fillna(0)或用Int64dtype
真正麻烦的是跨列逻辑依赖的“动态”——比如“按 A 列排序后,取 B 列对应位置的值再排名”,这种得拆成多步,rank() 本身不支持引用其他列的排序结果。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











