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

当前回答

就像很久以前的机械卡片分拣机一样,首先按最不重要的键排序,然后按下一个最重要的键进行排序。不需要库,可以使用任意数量的键以及任意组合的升序和降序键。

 dd <- dd[order(dd$b, decreasing = FALSE),]

现在我们准备好做最重要的关键。这一类是稳定的,最重要的密钥中的任何联系都已经解决。

dd <- dd[order(dd$z, decreasing = TRUE),]

这可能不是最快的,但它确实简单可靠

其他回答

针对OP中添加的关于如何以编程方式排序的注释:

使用dplyr和data.table

library(dplyr)
library(data.table)

dplyr公司

只需使用arrange_,这是arrange的标准评估版本。

df1 <- tbl_df(iris)
#using strings or formula
arrange_(df1, c('Petal.Length', 'Petal.Width'))
arrange_(df1, ~Petal.Length, ~Petal.Width)
    Source: local data frame [150 x 5]

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          (dbl)       (dbl)        (dbl)       (dbl)  (fctr)
1           4.6         3.6          1.0         0.2  setosa
2           4.3         3.0          1.1         0.1  setosa
3           5.8         4.0          1.2         0.2  setosa
4           5.0         3.2          1.2         0.2  setosa
5           4.7         3.2          1.3         0.2  setosa
6           5.4         3.9          1.3         0.4  setosa
7           5.5         3.5          1.3         0.2  setosa
8           4.4         3.0          1.3         0.2  setosa
9           5.0         3.5          1.3         0.3  setosa
10          4.5         2.3          1.3         0.3  setosa
..          ...         ...          ...         ...     ...


#Or using a variable
sortBy <- c('Petal.Length', 'Petal.Width')
arrange_(df1, .dots = sortBy)
    Source: local data frame [150 x 5]

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          (dbl)       (dbl)        (dbl)       (dbl)  (fctr)
1           4.6         3.6          1.0         0.2  setosa
2           4.3         3.0          1.1         0.1  setosa
3           5.8         4.0          1.2         0.2  setosa
4           5.0         3.2          1.2         0.2  setosa
5           4.7         3.2          1.3         0.2  setosa
6           5.5         3.5          1.3         0.2  setosa
7           4.4         3.0          1.3         0.2  setosa
8           4.4         3.2          1.3         0.2  setosa
9           5.0         3.5          1.3         0.3  setosa
10          4.5         2.3          1.3         0.3  setosa
..          ...         ...          ...         ...     ...

#Doing the same operation except sorting Petal.Length in descending order
sortByDesc <- c('desc(Petal.Length)', 'Petal.Width')
arrange_(df1, .dots = sortByDesc)

更多信息请点击此处:https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html

最好使用公式,因为它还可以捕获环境来计算表达式

数据表

dt1 <- data.table(iris) #not really required, as you can work directly on your data.frame
sortBy <- c('Petal.Length', 'Petal.Width')
sortType <- c(-1, 1)
setorderv(dt1, sortBy, sortType)
dt1
     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
  1:          7.7         2.6          6.9         2.3 virginica
  2:          7.7         2.8          6.7         2.0 virginica
  3:          7.7         3.8          6.7         2.2 virginica
  4:          7.6         3.0          6.6         2.1 virginica
  5:          7.9         3.8          6.4         2.0 virginica
 ---                                                            
146:          5.4         3.9          1.3         0.4    setosa
147:          5.8         4.0          1.2         0.2    setosa
148:          5.0         3.2          1.2         0.2    setosa
149:          4.3         3.0          1.1         0.1    setosa
150:          4.6         3.6          1.0         0.2    setosa

您的选择

从基础订购从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命名法,但我理解哈德利之所以这样做,是因为上面所讨论的问题。

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

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

R包data.table使用简单的语法(Matt在回答中很好地强调了这一点)提供了data.table的快速排序和内存高效排序。从那时起,已经有了很多改进,并且有了一个新的函数setorder()。从v1.9.5+开始,setorder()也适用于data.frames。

首先,我们将创建一个足够大的数据集,并对其他答案中提到的不同方法进行基准测试,然后列出data.table的特性。

数据:

require(plyr)
require(doBy)
require(data.table)
require(dplyr)
require(taRifx)

set.seed(45L)
dat = data.frame(b = as.factor(sample(c("Hi", "Med", "Low"), 1e8, TRUE)),
                 x = sample(c("A", "D", "C"), 1e8, TRUE),
                 y = sample(100, 1e8, TRUE),
                 z = sample(5, 1e8, TRUE), 
                 stringsAsFactors = FALSE)

基准:

报告的计时来自运行system.time(…)的这些函数,如下所示。时间列表如下(按最慢到最快的顺序)。

orderBy( ~ -z + b, data = dat)     ## doBy
plyr::arrange(dat, desc(z), b)     ## plyr
arrange(dat, desc(z), b)           ## dplyr
sort(dat, f = ~ -z + b)            ## taRifx
dat[with(dat, order(-z, b)), ]     ## base R

# convert to data.table, by reference
setDT(dat)

dat[order(-z, b)]                  ## data.table, base R like syntax
setorder(dat, -z, b)               ## data.table, using setorder()
                                   ## setorder() now also works with data.frames 

# R-session memory usage (BEFORE) = ~2GB (size of 'dat')
# ------------------------------------------------------------
# Package      function    Time (s)  Peak memory   Memory used
# ------------------------------------------------------------
# doBy          orderBy      409.7        6.7 GB        4.7 GB
# taRifx           sort      400.8        6.7 GB        4.7 GB
# plyr          arrange      318.8        5.6 GB        3.6 GB 
# base R          order      299.0        5.6 GB        3.6 GB
# dplyr         arrange       62.7        4.2 GB        2.2 GB
# ------------------------------------------------------------
# data.table      order        6.2        4.2 GB        2.2 GB
# data.table   setorder        4.5        2.4 GB        0.4 GB
# ------------------------------------------------------------

data.table的DT[order(…)]语法比其他最快的方法(dplyr)快约10倍,同时消耗与dplyr相同的内存量。data.table的setorder()比其他最快的方法(dplyr)快了约14倍,只需要0.4GB的额外内存。dat现在按照我们要求的顺序(通过引用更新)。

数据表功能:

速度:

表的排序非常快,因为它实现了基数排序。语法DT[order(…)]在内部进行了优化,以使用data.table的快速排序。您可以继续使用熟悉的基本R语法,但可以加快处理速度(并且使用更少的内存)。

内存:

大多数时候,我们不需要重新排序后的原始data.frame或data.table。也就是说,我们通常将结果分配回同一个对象,例如:DF<-DF[订单(…)]问题是,这至少需要原始对象的两倍(2倍)内存。为了提高内存效率,data.table还提供了一个函数setorder()。setorder()通过引用(就地)重新排序data.tables,而无需创建任何其他副本。它只使用相当于一列大小的额外内存。

其他功能:

它支持整数、逻辑、数字、字符和偶数位64::integer64类型。请注意,factor、Date、POSIXct等.类下面都是带有附加属性的整数/数字类型,因此也受支持。在基R中,我们不能使用字符向量上的-按该列降序排序。相反,我们必须使用-xtfrm(.)。然而,在data.table中,我们可以只做dat[order(-x)]或setorder(dat,-x)。

另一种选择是使用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