sort() 对向量的值排序
对于普通向量,sort() 的行为完全符合你的预期:按升序返回这些值(原向量不变):
有两个选项值得了解。decreasing = TRUE 会反转方向。另外,sort() 默认会悄悄地丢掉 NA 值:sort(c(3, NA, 1)) 只返回 1 3。如果排序后必须保留缺失值,就要指明它们放在哪里:sort(x, na.last = TRUE) 会保留它们,放在排好序的值后面。
到目前为止都很显然。有意思的是,为什么 sort() 是给数据框排序的错误工具。
order() 返回的是位置:思维上的转变
sort() 回答的是“这些值按顺序排列是什么样?”order() 回答的是另一个问题:“我要按哪些位置依次访问,才能按顺序看到这些值?”
把 order(times) 当作一份指令来读:最小的值在位置 2,下一个在位置 4,然后是 1,再然后是 3。按照这份指令做索引,times[order(times)],得到的结果和 sort(times) 完全一样。
这为什么重要?因为数据框的一行是一组必须一起移动的值。用 sort() 只对 time 列排序,会让这一列重新排列,而同一行的其他值却留在原地,每一行都乱套了。order() 给你的是行的位置,把这些位置放在方括号的行位置上,移动的就是整行:
Ben 的 9.8 排到最前面时,Ben 的名字也跟着一起过来了。这就是全部的技巧,也是在基础 R 中给数据框排序的标准写法:df[order(df$column), ]。(打印出来的行标签 2 4 1 3 是跟着行一起移动的原始行号,不影响结果。)
降序与多列排序
数值降序排序时,给列取负:对 -time 升序排序,就等于对 time 降序排序。而且 order() 可以接受多列,靠前的列优先,靠后的列用来区分并列的情况:
第二个调用先按字母顺序排列部门,然后在每个部门内部按工资从高到低排列。取负的技巧只对数字有效:对字符列写 -df$name 会报错。要对文本降序排序,改用 order(df$name, decreasing = TRUE)(它会一次性反转所有排序键)。
rank() 是第三个兄弟
顺便一提:rank(x) 回答的又是另一个问题,“每个值排第几?”,而且不移动任何东西:
sort() 给出排好序的值,order() 给出要访问的位置,rank() 在原位置给出每个值的名次。人们经常把 order() 和 rank() 搞混;注意它们互为逆排列,只有当你确实需要名次(排行榜、百分位)时才用 rank()。
dplyr 的方式:arrange()
dplyr 的 arrange() 把整套 order() 操作包装成了一个动词:列就是排序键,desc() 反转其中一列(以下是静态代码,沙盒只运行基础 R):
library(dplyr)
runners |> arrange(time)
staff |> arrange(dept, desc(salary))
不用方括号,不用 $,也不用取负的技巧,desc() 对字符列也有效。有一个行为上的区别值得了解:无论升序还是降序,arrange() 都把 NA 值放在最后,而基础 R 的 order() 默认也是 na.last = TRUE。arrange() 如何与 filter() 等函数串联使用,请参阅 dplyr 入门。
字符排序有一些坑
对文本排序时有两点要注意。第一,以文本形式存储的数字会按文本排序,字符比较是一个符号一个符号地进行的:
"10" 排在 "2" 前面,因为比较在第一个符号就结束了:"1" < "2"。如果一列数字的排序结果很奇怪,几乎可以肯定它是以字符型存储的,先用 as.numeric() 转换(这经常是 CSV 导入不规范的后遗症)。
第二,文本的排序取决于系统的区域设置(locale):重音符号、大小写和非拉丁文字,在你的笔记本电脑上和在使用 C 区域设置的服务器上,排序结果可能不同。如果排序输出必须在不同机器上完全一致,要么显式设置区域设置,要么对一个规范化后的键排序。
要点回顾
sort(x)对向量的值排序;decreasing = TRUE反转方向;除非设置na.last = TRUE,否则NA会被丢掉。order(x)返回位置,df[order(df$x), ]是基础 R 中给数据框排序的标准写法。- 多列排序:
order(df$a, -df$b);用取负实现降序只对数字有效。 rank()给出名次而不重新排序,不能代替order()。- dplyr 的
arrange(dept, desc(salary))是同样的排序,只是标点符号更少。 - 在文本中,
"10"排在"2"前面,记得检查列的类型。
下一篇:用 table()、tapply()、aggregate() 和 dplyr 的 group_by() 把多行汇总成分组统计。
常见问题
R 中怎么按某一列给数据框排序?
在行的方括号里使用 order():df[order(df$price), ]。order() 按排好的顺序返回行的位置,用它们做索引就能重新排列整个数据框。这里不能用 sort():它只对一个向量的值排序,排完之后这些值和其他列就对不上了。
R 中 sort() 和 order() 有什么区别?
sort(x) 返回按顺序重新排列后的 x 的值。order(x) 返回能让 x 变得有序的位置(索引),而这正是按某一列给整个数据框排序所需要的:df[order(df$x), ]。
R 中怎么降序排序?
向量:sort(x, decreasing = TRUE)。数据框:df[order(-df$x), ](对数值列取负),或者 df[order(df$x, decreasing = TRUE), ],后者也适用于不能取负的字符列。在 dplyr 中:arrange(df, desc(x))。
R 中怎么按多列排序?
把它们都传给 order():df[order(df$dept, -df$salary), ] 先按部门排序,再在每个部门内按工资降序排序。靠前的参数是主排序键,靠后的参数用来区分并列的情况。在 dplyr 中:arrange(df, dept, desc(salary))。