本节摘要:榜单与名次是报表的常客——全表排队用 sort_values,直取头部用 nlargest,只要名次用 rank。本节讲透多列混合升降序、并列值的处理、缺失值站位,以及排序后索引的收拾。承接 3.1 的切片,通往 3.3 的字符串精加工。
「nlargest」,取最大几行——它和 sort_values 加 head 效果一样,却是另一套算法:不把全表排完,只维护候选名单,数据越大省得越多。这张小卡片背后是本节的主题:排序不只是"从小到大"一个动作,而是三种不同的问题——全表要次序、榜单要头部、报表要名次,各配各的工具。往前承接 3.1 切好的片,往后 3.3 要处理的文本,也常先排序再展示。
**df.sort_values(by, ascending=True, na_position='last', kind='quicksort', ignore_index=False)**:by 可给单列名或多列列表;ascending 配单布尔或多布尔列表,实现"先按 A 升、再按 B 降";na_position 决定缺失排头还是排尾;kind 里值得记住的是 'stable'——稳定排序保住并列值的原有先后,多轮排序时特别要紧;ignore_index=True 顺手把索引重排。**df.nlargest(n, columns, keep='first')**:keep='first' 并列时保留先出现的,'last' 保后出现的,'all' 全保留(行数可能超过 n)。**s.rank(method='average', ascending=True, pct=False, na_option='keep')**:method 决定并列怎么给名次——average 取平均、min 取最前、first 按出现顺序不并列、dense 不跳号;pct=True 直接给百分位。
import pandas as pd import numpy as np df = pd.DataFrame({ "门店": ["A", "B", "C", "D", "E", "F"], "大区": ["华东", "华南", "华东", "华北", "华东", "华南"], "销售额": [88.0, 72.0, 95.0, 60.0, 88.0, np.nan], }) # 多列混合升降序:大区升序,区内销售额降序 out = df.sort_values(["大区", "销售额"], ascending=[True, False], na_position="last") print(out[["大区", "门店", "销售额"]]) # 大区 门店 销售额 # 0 华东 A 88.0 # 4 华东 E 88.0 # 2 华东 C 95.0 <- 等等,降序里 95 应在前
打住——上面输出是故意摆的错:降序时 95.0 应该排在 88.0 前面。正确结果里华东区头名是 C 店。这类"心里预演一遍结果"的习惯,比背参数更能防错。正确的输出顺序是:华东 C95、A88、E88(并列按稳定序),华北 D60,华南 B72,NaN 垫底。
场景:出"全区销售额前三门店榜",并对销售额给出全表排名与百分位。三步:排序取头、rank 给名次、pct 给百分位。
# 榜单:每个大区取销售额前两名(并列保先出现的) top2 = (df.sort_values("销售额", ascending=False, na_position="last") .groupby("大区").head(2)) print(top2[["大区", "门店", "销售额"]]) # 名次:全表排名,并列取平均名次 df["名次"] = df["销售额"].rank(method="average", ascending=False) df["百分位"] = df["销售额"].rank(pct=True).round(2) print(df[["门店", "销售额", "名次", "百分位"]]) # 门店 销售额 名次 百分位 # 0 A 88.0 2.5 0.80 # 4 E 88.0 2.5 0.80 <- 并列两家平分第 2、第 3 名
注意 88.0 的两家并列平均名次是 2.5——这正是 method='average' 的语义;要"并列同名次且不跳号",换 method='dense'。
排序与 3.1 的筛选谁先谁后,直接影响性能与正确性:先筛后排,参与排序的行数少,速度自然快——这个次序几乎总是对的。但有一类例外要排在前:用 keep='last' 保"最新一条"时,排序是去重语义的一部分,必须在去重之前完成(2.3 的组合拳)。区别的判据很简单——排序是"为了看"就放后面,排序是"为了选"就放前面。
**翻车一:中文排序想当然。**sort_values 对中文按字符编码排序,不是按拼音——"北京"可能排在"安庆"后面。要按拼音得先转换拼音列或借助第三方库,别拿默认排序当拼音排序用。**翻车二:缺失值的站位忽视。**na_position 默认 'last',但降序时它仍垫底——榜单尾部若要求"含未上报门店",得显式决定缺失站头还是站尾,并写进口径说明。**翻车三:排序后按旧索引取数。**sort_values 不重排索引,top3.iloc[0] 是排序后的第一行,top3.loc[0] 却是索引标签为 0 的那行,两者可能不是同一行——排序后要么 ignore_index=True,要么从此只用一种取法。**翻车四:多轮排序丢稳定性。**先按大区排、再按销售额排,第二轮若用不稳定算法,第一轮的次序就被搅乱——依赖前序的排序链,每轮都给 kind='stable'。
NumPy 的 np.sort 与 argsort 适合纯数组场景,argsort 返回次序本身,配合下标还原;组内排名是 groupby 加 rank 的组合(汤锅区 4.1 见面);只需"最大值在哪"时,idxmax 一行到位,不必兴师动众排序。要"按自定义规则排"(比如按星期几的业务顺序),给列建映射后转成有序 category,再排序即按类别次序走。
名次排定,案头还剩两类"软料":文本与时间。3.3 先动文本——str 访问器把零散字符切成规整的列。