如何按列sorting数据框?

我想sorting一个data.frame多列。 例如,在下面的data.frame中,我想按列z (降序)然后按列b (升序)sorting:

 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 bxyz 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)), ] bxyz 4 Low C 9 2 2 Med D 3 1 1 Hi A 8 1 3 Hi A 9 1 

一些2+年后编辑:它只是被问及如何通过列索引来做到这一点。 答案是简单地将所需的sorting列传递给order()函数:

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

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

你的select

  • base order
  • dplyr arrange
  • setorderdata.table
  • plyr arrange
  • taRifx sort
  • doBy
  • Deducer

大多数情况下,您应该使用dplyrdata.table解决scheme,除非没有依赖关系是重要的,在这种情况下使用base::order


我最近添加sort.data.frame到一个CRAN包,使其类兼容,如下所述: 最好的方法来创buildsort.data.framegenerics/方法一致性?

因此,给定data.frame dd,你可以按如下方式sorting:

 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 ) 

如果您是此function的原作者之一,请与我联系。 关于公共领域的讨论在这里: 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赖特或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) 

由于连接/分离,似乎与microbenchmark不兼容。


 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命名法,其中sorting对象被sort(object)调用,但我明白为什么哈德利这样做是由于上面链接问题中讨论的问题。

德克的答案很好。 它还突出了用于索引data.frame s和data.table s的语法中的一个关键区别:

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

这两个电话之间的差别很小,但可能会产生重要的后果。 特别是如果你写出产品代码和/或关心你的研究的正确性,最好避免不必要的重复variables名称。 data.table帮助你做到这一点。

下面是一个例子,说明重复variables名可能会使你陷入困境:

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

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

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

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

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

data.table我们关心这样的小细节。 所以我们做了一些简单的事情来避免input两次variables名。 东西很简单。 i已经在dd的框架内自动评估了。 with()完全不需要。

代替

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

只是

 dd[order(-z, b)] 

而不是

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

只是

 quarterlyreport[order(-z,b)] 

这是一个非常小的差异,但它可能只会挽救你的脖子有一天。 在权衡这个问题的不同答案时,考虑将variables名称的重复次数作为决定的标准之一。 有些答案有不less重复,有些则没有。

这里有很多优秀的答案,但是dplyr给出了我可以快速和容易地记住的唯一语法(现在经常使用):

 library(dplyr) # sort mtcars by mpg, ascending... use desc(mpg) for descending arrange(mtcars, mpg) # sort mtcars first by mpg, then by cyl, then by wt) arrange(mtcars , mpg, cyl, wt) 

对于OP的问题:

 arrange(dd, desc(z), b) bxyz 1 Low C 9 2 2 Med D 3 1 3 Hi A 8 1 4 Hi A 9 1 

R包data.table提供了一个简单的语法(Matt 在他的回答中很好地突出显示的一部分)对数据表的快速高效的sorting。 从那时起,已经有了很多的改进,并且还有一个新的函数setorder()setorder()也可以在v1.9.5 v1.9.5+使用data.frames

首先,我们将创build一个足够大的数据集,并对从其他答案中提到的不同方法进行基准testing,然后列出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.tableDT[order(...)]语法比其他方法( dplyr )的最快速度快10倍 ,同时消耗与dplyr相同数量的内存。

  • data.tablesetorder()比其他方法( dplyr )的最快速度快了14倍,而只有0.4GB的额外内存dat现在是我们要求的顺序(因为它是由参考更新)。

data.tablefunction:

速度:

  • data.table的sorting非常快,因为它实现了基数sorting 。

  • 语法DT[order(...)]在内部进行了优化,以便使用data.table的快速sorting。 您可以继续使用熟悉的基本R语法,但可以加快进程(并使用更less的内存)。

记忆:

  • 大多数情况下,重新sorting后我们不需要原始的data.framedata.table 。 也就是说,我们通常把结果赋给同一个对象,例如:

     DF <- DF[order(...)] 

    问题是这需要至less两次(2x)原始对象的内存。 为了提高内存效率data.table因此也提供了一个函数setorder()

    setorder() by referencein-place setorder()重新sortingdata.tables ,而不做任何额外的副本。 它只使用等于一列大小的额外内存。

其他特性:

  1. 它支持integerlogicalnumericcharacter ,甚至bit64::integer64types。

    请注意, factorDatePOSIXct等类是所有下面的integer / numerictypes与附加属性,因此也支持。

  2. 在基础R中,我们不能使用-在一个字符向量上按照列降序sorting。 相反,我们必须使用-xtfrm(.)

    但是,在data.table中 ,我们可以做例如dat[order(-x)]setorder(dat, -x)

有了这个(非常有用)的Kevin Wrightfunction ,在R维基的提示部分发布,这很容易实现。

 sort(dd,by = ~ -z + b) # bxyz # 4 Low C 9 2 # 2 Med D 3 1 # 1 Hi A 8 1 # 3 Hi A 9 1 

或者你可以使用包doBy

 library(doBy) dd <- orderBy(~-z+b, data=dd) 

假设你有一个data.frame A ,你想用列x降序sorting。 调用sorting后的data.frame newdata

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

如果你想要升序,那就用"-"replace一下。 你可以有类似的东西

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

其中xzdata.frame A中的一些列。 这意味着按x降序, y升序和z降序sortingdata.frame A

或者,使用包Deducer

 library(Deducer) dd<- sortData(dd,c("z","b"),increasing= c(FALSE,TRUE)) 

如果SQL自然而然地给你,sqldf处理ORDER BY作为Codd的意图。

我用下面的例子学习了一下,然后困惑了我很长一段时间:

 set.seed(1234) ID = 1:10 Age = round(rnorm(10, 50, 1)) diag = c("Depression", "Bipolar") Diagnosis = sample(diag, 10, replace=TRUE) data = data.frame(ID, Age, Diagnosis) databyAge = data[order(Age),] databyAge 

这个例子工作的唯一原因是因为order是通过vector Agesorting,而不是由data frame data名为Age的列进行sorting。

为了看到这一点,使用read.table创build一个相同的数据框,列名略有不同,而不使用上面的任何vector:

 my.data <- read.table(text = ' id age diagnosis 1 49 Depression 2 50 Depression 3 51 Depression 4 48 Depression 5 50 Depression 6 51 Bipolar 7 49 Bipolar 8 49 Bipolar 9 49 Bipolar 10 49 Depression ', header = TRUE) 

上面的order行结构不再起作用,因为没有名为age向量:

 databyage = my.data[order(age),] 

以下行可以工作,因为ordermy.data的列agemy.data

 databyage = my.data[order(my.data$age),] 

我认为这是值得发布的,因为我对这个例子的了解如此困惑。 如果这篇文章是不适合的线程我可以删除它。

编辑:2014年5月13日

下面是按每个列对数据框进行sorting而不指定列名的一般方法。 下面的代码显示了如何从左到右或从右到左sorting。 这适用于每列是数字 我还没有尝试添加一个字符列。

我在一个月或两个月前在另一个网站的旧post中find了do.call代码,但只是在广泛和困难的search之后。 我不确定我现在可以搬迁那个post。 目前的线程是在R订购data.frame的第一个命中。 所以,我认为我原来do.call代码的扩展版本可能是有用的。

 set.seed(1234) v1 <- c(0,0,0,0, 0,0,0,0, 1,1,1,1, 1,1,1,1) v2 <- c(0,0,0,0, 1,1,1,1, 0,0,0,0, 1,1,1,1) v3 <- c(0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1) v4 <- c(0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1) df.1 <- data.frame(v1, v2, v3, v4) df.1 rdf.1 <- df.1[sample(nrow(df.1), nrow(df.1), replace = FALSE),] rdf.1 order.rdf.1 <- rdf.1[do.call(order, as.list(rdf.1)),] order.rdf.1 order.rdf.2 <- rdf.1[do.call(order, rev(as.list(rdf.1))),] order.rdf.2 rdf.3 <- data.frame(rdf.1$v2, rdf.1$v4, rdf.1$v3, rdf.1$v1) rdf.3 order.rdf.3 <- rdf.1[do.call(order, as.list(rdf.3)),] order.rdf.3 

德克的答案是好的,但如果你需要sorting坚持,你会想将sorting应用到该数据框的名称。 使用示例代码:

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

针对在OP中添加的评论,以便如何以编程方式进行sorting:

使用dplyrdata.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

最好使用公式,因为它也捕获环境来评估expression式

data.table

 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 

为了完整起见,您还可以使用BBmisc包中的sortByCol()函数:

 library(BBmisc) sortByCol(dd, c("z", "b"), asc = c(FALSE, TRUE)) bxyz 4 Low C 9 2 2 Med D 3 1 1 Hi A 8 1 3 Hi A 9 1 

性能比较:

 library(microbenchmark) microbenchmark(sortByCol(dd, c("z", "b"), asc = c(FALSE, TRUE)), times = 100000) median 202.878 library(plyr) microbenchmark(arrange(dd,desc(z),b),times=100000) median 148.758 microbenchmark(dd[with(dd, order(-z, b)), ], times = 100000) median 115.872 

就像很久以前的机械卡片分拣机,首先按最不重要的按键sorting,然后是下一个最重要的按键,不需要图书馆,可以使用任意数量的按键以及任意组合的上升和下降按键。

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

现在我们准备做最重要的关键。 sorting是稳定的,最重要的关键的任何关系已经解决。

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

这可能不是最快的,但它肯定简单可靠