如何按列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
-
setorder
和data.table
- 从
plyr
arrange
- 从
taRifx
sort
- 从
doBy
- 从
Deducer
大多数情况下,您应该使用dplyr
或data.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
你也可以使用plyr
的arrange()
函数,就像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))
(线从下四分位延伸到上四分位,点是中位数)
鉴于这些结果和称重简单与速度,我不得不点头arrange
在plyr
包中 。 它有一个简单的语法,但是与基本的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.table
的DT[order(...)]
语法比其他方法(dplyr
)的最快速度快10倍 ,同时消耗与dplyr
相同数量的内存。 -
data.table
的setorder()
比其他方法(dplyr
)的最快速度快了14倍,而只有0.4GB的额外内存 。dat
现在是我们要求的顺序(因为它是由参考更新)。
data.tablefunction:
速度:
-
data.table的sorting非常快,因为它实现了基数sorting 。
-
语法
DT[order(...)]
在内部进行了优化,以便使用data.table的快速sorting。 您可以继续使用熟悉的基本R语法,但可以加快进程(并使用更less的内存)。
记忆:
-
大多数情况下,重新sorting后我们不需要原始的data.frame或data.table 。 也就是说,我们通常把结果赋给同一个对象,例如:
DF <- DF[order(...)]
问题是这需要至less两次(2x)原始对象的内存。 为了提高内存效率 , data.table因此也提供了一个函数
setorder()
。setorder()
by reference
( in-placesetorder()
重新sortingdata.tables ,而不做任何额外的副本。 它只使用等于一列大小的额外内存。
其他特性:
-
它支持
integer
,logical
,numeric
,character
,甚至bit64::integer64
types。请注意,
factor
,Date
,POSIXct
等类是所有下面的integer
/numeric
types与附加属性,因此也支持。 -
在基础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),]
其中x
和z
是data.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 Age
sorting,而不是由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),]
以下行可以工作,因为order
在my.data
的列age
上my.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:
使用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
最好使用公式,因为它也捕获环境来评估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),]
这可能不是最快的,但它肯定简单可靠