协慌网

登录 贡献 社区

如何按多列对数据框进行排序

我想按多列对 data.frame 进行排序。例如,对于下面的 data.frame,我想按列z (降序)排序,然后按列b (升序)排序:

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
dd
    b x y z
1  Hi A 8 1
2 Med D 3 1
3  Hi A 9 1
4 Low C 9 2

答案

您可以直接使用order()函数而无需使用附加工具 - 请参阅这个更简单的答案,该答案使用example(order)代码顶部的技巧:

R> dd[with(dd, order(-z, b)), ]
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1

大约 2 年多后编辑:只是通过列索引询问如何执行此操作。答案是简单地将所需的排序列传递给order()函数:

R> dd[order(-dd[,4], dd[,1]), ]
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1
R>

而不是使用列的名称(和with()更容易 / 更直接访问)。

你的选择

  • base order
  • dplyr arrange
  • setordersetordervdata.table
  • plyr arrange
  • taRifx sort
  • orderBydoBy
  • sortDataDeducer

大多数情况下你应该使用dplyrdata.table解决方案,除非重要性没有依赖性,在这种情况下使用base::order


我最近将 sort.data.frame 添加到 CRAN 包中,使其类兼容,如下所述: 为 sort.data.frame 创建通用 / 方法一致性的最佳方法?

因此,给定 data.frame dd,您可以按如下方式排序:

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
library(taRifx)
sort(dd, f= ~ -z + b )

如果您是此功能的原作者之一,请与我联系。关于公共领域的讨论在这里: http//chat.stackoverflow.com/transcript/message/1094290#1094290


您也可以使用plyrarrange()函数,正如 Hadley 在上面的主题中指出的那样:

library(plyr)
arrange(dd,desc(z),b)

基准:请注意,由于存在大量冲突,因此我将每个包加载到新的 R 会话中。特别是加载 doBy 包会导致sort返回 “以下对象被屏蔽'x(位置 17)':b,x,y,z”,并且加载 Deducer 包会覆盖 Kevin 的sort.data.frame Wright 或 taRifx 包。

#Load each time
dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
library(microbenchmark)

# Reload R between benchmarks
microbenchmark(dd[with(dd, order(-z, b)), ] ,
    dd[order(-dd$z, dd$b),],
    times=1000
)

中位数时间:

dd[with(dd, order(-z, b)), ] 778

dd[order(-dd$z, dd$b),] 788

library(taRifx)
microbenchmark(sort(dd, f= ~-z+b ),times=1000)

中位时间: 1,567

library(plyr)
microbenchmark(arrange(dd,desc(z),b),times=1000)

中位时间: 862

library(doBy)
microbenchmark(orderBy(~-z+b, data=dd),times=1000)

中位时间: 1,694

请注意,doBy 需要花费大量时间来加载包。

library(Deducer)
microbenchmark(sortData(dd,c("z","b"),increasing= c(FALSE,TRUE)),times=1000)

无法使 Deducer 加载。需要 JGR 控制台。

esort <- function(x, sortvar, ...) {
attach(x)
x <- x[with(x,order(sortvar,...)),]
return(x)
detach(x)
}

microbenchmark(esort(dd, -z, b),times=1000)

由于连接 / 分离,似乎与微基准不兼容。


m <- microbenchmark(
  arrange(dd,desc(z),b),
  sort(dd, f= ~-z+b ),
  dd[with(dd, order(-z, b)), ] ,
  dd[order(-dd$z, dd$b),],
  times=1000
  )

uq <- function(x) { fivenum(x)[4]}  
lq <- function(x) { fivenum(x)[2]}

y_min <- 0 # min(by(m$time,m$expr,lq))
y_max <- max(by(m$time,m$expr,uq)) * 1.05

p <- ggplot(m,aes(x=expr,y=time)) + coord_cartesian(ylim = c( y_min , y_max )) 
p + stat_summary(fun.y=median,fun.ymin = lq, fun.ymax = uq, aes(fill=expr))

微基准图

(线从下四分位数延伸到上四分位数,点是中位数)


鉴于这些结果和称重简单性与速度,我必须点头arrangeplyr包中 。它具有简单的语法,但几乎与基本的 R 命令一样快速,并且具有复杂的阴谋。通常辉煌的哈德利威克姆工作。我唯一的抱怨是它打破了标准的 R 术语,其中排序对象被sort(object)调用,但我理解为什么 Hadley 这样做是由于上面链接的问题中讨论的问题。

德克的答案很棒。它还强调了用于索引data.framedata.table的语法的主要区别:

## The data.frame way
dd[with(dd, order(-z, b)), ]

## The data.table way: (7 fewer characters, but that's not the important bit)
dd[order(-z, b)]

这两个电话之间的差异很小,但它可能会产生重要影响。特别是如果您编写生产代码和 / 或关注研究中的正确性,最好避免不必要的重复变量名称。 data.table可以帮助您实现这一目标。

这是一个如何重复变量名称可能会让您陷入麻烦的示例:

让我们从 Dirk 的答案中改变背景,并说这是一个更大的项目的一部分,其中有很多对象名称,它们很长很有意义; 而不是dd它被称为quarterlyreport 。它成为了 :

quarterlyreport[with(quarterlyreport,order(-z,b)),]

好的。没有错。接下来,您的老板要求您在报告中包含上一季度的报告。你lastquarterlyreport你的代码,在各个地方添加一个对象lastquarterlyreport并以某种方式(地球上怎么样?)你最终得到这个:

quarterlyreport[with(lastquarterlyreport,order(-z,b)),]

这不是你的意思,但你没有发现它,因为你做得很快,而且它坐落在一个类似代码的页面上。代码不会失败(没有警告也没有错误),因为 R 认为这就是你的意思。你希望看到你的报告的人发现它,但也许他们没有。如果您经常使用编程语言,那么这种情况可能都是熟悉的。你会说这是一个 “错字”。我会解决你对老板说的 “拼写错误”。

data.table我们关注像这样的微小细节。所以我们做了一些简单的事情,以避免两次输入变量名。非常简单。 i已经在dd的框架内自动进行了评估。你根本不需要with()

代替

dd[with(dd, order(-z, b)), ]

只是

dd[order(-z, b)]

而不是

quarterlyreport[with(lastquarterlyreport,order(-z,b)),]

只是

quarterlyreport[order(-z,b)]

这是一个非常小的差异,但它可能只是有一天挽救你的脖子。权衡此问题的不同答案时,请考虑将变量名称的重复计算为您决定的标准之一。有些答案有不少重复,有些则没有。