我想按多列对数据帧进行排序。例如,对于下面的数据帧,我希望按列“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

当前回答

另一种选择是使用rgr包:

> library(rgr)
> gx.sort.df(dd, ~ -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

其他回答

有了凯文·赖特(Kevin Wright)在R维基的提示部分发布的这个(非常有用的)功能,这很容易实现。

sort(dd,by = ~ -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

您的选择

从基础订购从dplyr安排data.table中的setorder和setorderv从plyer安排从taRifx排序orderBy来自doBy从推断器中排序数据

大多数时候,您应该使用dplyr或data.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 )

如果您是该函数的原始作者之一,请与我联系。关于公共域的讨论如下:https://chat.stackoverflow.com/transcript/message/1094290#1094290


您还可以使用plyr中的arrange()函数,正如Hadley在上面的线程中指出的那样:

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

基准测试:注意,我在一个新的R会话中加载了每个包,因为有很多冲突。特别是,加载doBy包会导致排序返回“以下对象从‘x(位置17)’屏蔽:b,x,y,z”,并且加载演绎器包会覆盖Kevin Wright或taRifx包的sort.data.frame。

#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[带(dd,顺序(-z,b)),]778

dd[订单(-dd$z,dd$b),]788

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

中位时间:1567

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

中位时间:862

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

中位时间:1694

注意,doBy需要很长时间来加载包。

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

无法使扣减器加载。需要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))

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


考虑到这些结果,并权衡了简单性与速度,我不得不同意在plyer包中进行安排。它有一个简单的语法,但它的速度几乎和基本的R命令一样快,而且具有复杂的机制。典型的杰出哈德利·威克姆作品。我唯一的不满是它打破了按排序(对象)调用排序对象的标准R命名法,但我理解哈德利之所以这样做,是因为上面所讨论的问题。

假设您有一个data.frame a,并且希望使用名为x降序的列对其进行排序。调用排序后的数据。frame newdata

newdata <- A[order(-A$x),]

如果需要升序,请将“-”替换为空。你可以吃类似的东西

newdata <- A[order(-A$x, A$y, -A$z),]

其中x和z是data.frame A中的一些列。这意味着按照x降序、y升序和z降序对data.frameA进行排序。

Dirk的回答很好,但如果您需要排序来持久化,您需要将排序应用回数据帧的名称。使用示例代码:

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

德克的回答很好。它还强调了用于索引data.frames和data.tables的语法的一个关键区别:

## 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)]

这两个调用之间的差异很小,但可能会产生重要的后果。特别是如果您编写生产代码和/或关注研究中的正确性,最好避免不必要的重复变量名。数据表帮助您做到这一点。

下面是一个重复变量名称可能会给您带来麻烦的示例:

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

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

好的,好的。这没什么错。接下来,你的老板要求你在报告中包括上一季度的报告。你通过代码,在不同的地方添加一个对象最后一个季度报告,不知怎么的(到底是怎么回事?)你最终得到了这样的结果:

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

这不是你的意思,但你没有发现它,因为你做得很快,而且它位于一个类似代码的页面上。代码不会出错(没有警告和错误),因为R认为这就是你的意思。你希望无论谁读你的报告都能发现,但也许他们没有。如果您经常使用编程语言,那么这种情况可能非常熟悉。你会说这是个“错别字”。我会纠正你对老板说的“错别字”。

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

而不是

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

只是

dd[order(-z, b)]

而不是

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

只是

quarterlyreport[order(-z,b)]

这是一个很小的区别,但也许有一天它会拯救你的脖子。在权衡这个问题的不同答案时,考虑将变量名称的重复次数作为决定的标准之一。有些答案有相当多的重复,其他答案没有。