我想按多个列对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
#1楼
德克的答案很好。 它还突出显示了用于索引data.frame
和data.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
中添加一个对象,然后以某种方式(到底是怎么回事?)结束了:
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)]
这是一个很小的差异,但是有一天可能会节省您的脖子。 在权衡此问题的不同答案时,请考虑将变量名的重复数作为决定的标准之一。 有些答案有很多重复,有些则没有。
#2楼
通过R wiki的提示部分中的Kevin Wright的(非常有用的)功能 ,可以轻松实现这一点。
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
#3楼
您可以直接使用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()
可以更容易/更直接地访问)。
#4楼
或者,使用软件包Deducer
library(Deducer)
dd<- sortData(dd,c("z","b"),increasing= c(FALSE,TRUE))
#5楼
我通过以下示例了解了order
,这使我很困惑:
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
而不是data frame data
名为Age
的列进行排序的。
要查看此内容,请使用read.table
创建一个相同的数据帧,但列名稍有不同,并且不使用上述任何向量:
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),]
以下行之所以my.data
,是因为order
按my.data
的列age
my.data
。
databyage = my.data[order(my.data$age),]
考虑到我这么长时间以来一直对这个示例感到困惑,所以我认为这值得发表。 如果该帖子不适合该主题,我可以将其删除。
编辑:2014年5月13日
下面是不指定列名就按每一列对数据帧进行排序的通用方法。 下面的代码显示了如何从左到右或从右到左排序。 如果每一列都是数字,则此方法有效。 我没有尝试添加字符列。
我在一两个月前在另一个网站上的旧帖子中找到了do.call
代码,但经过大量且困难的搜索之后才找到。 我不确定现在是否可以重新安置该职位。 当前线程是在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
#6楼
或者您可以使用软件包doBy
library(doBy)
dd <- orderBy(~-z+b, data=dd)
#7楼
这里有很多很好的答案,但是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)
b x y z
1 Low C 9 2
2 Med D 3 1
3 Hi A 8 1
4 Hi A 9 1
#8楼
如果SQL自然而然地出现,则sqldf
包将按Codd的意图处理ORDER BY
。
#9楼
就像很久以前的机械卡分类器一样,首先按最低有效位排序,然后再按最高有效位排序,依此类推。不需要库,即可使用任意数量的键以及升序和降序键的任何组合。
dd <- dd[order(dd$b, decreasing = FALSE),]
现在,我们准备执行最重要的密钥。 排序是稳定的,并且最重要的键中的所有关系都已解决。
dd <- dd[order(dd$z, decreasing = TRUE),]
这可能不是最快的,但是肯定是简单可靠的
#10楼
将R包data.table
提供既快速又用一个简单的语法data.tables的内存使用效率排序(其中马特很好地突出了一部分在他的回答 )。 从那时起,已经有了很多改进,并且还增加了一个新的函数setorder()
。 从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
符合我们要求的顺序(因为它已通过引用进行更新)。
data.table功能:
速度:
data.table的排序非常快,因为它实现了基数排序 。
语法
DT[order(...)]
在内部进行了优化,以使用data.table的快速排序。 您可以继续使用熟悉的base R语法,但是可以加快处理速度(并使用更少的内存)。
记忆:
在大多数情况下,重新排序后不需要原始的data.frame或data.table 。 也就是说,我们通常将结果分配回同一对象,例如:
DF <- DF[order(...)]
问题在于,这至少需要原始对象的两倍(2倍)的内存。 为了提高内存效率 , data.table因此还提供了一个
setorder()
函数。setorder()
重排序data.tablesby reference
( 就地 ),而不进行任何附加的拷贝。 它仅使用等于一列大小的额外内存。
其它功能:
它支持
integer
,logical
,numeric
,character
甚至bit64::integer64
类型。注意
factor
,Date
,POSIXct
等类都是带有附加属性的integer
/numeric
类型,因此也受支持。在基数R中,我们不能在字符向量上使用
-
以该列按降序排序。 相反,我们必须使用-xtfrm(.)
。但是,在data.table中 ,我们可以执行例如
dat[order(-x)]
或setorder(dat, -x)
。
#11楼
为了完整起见:您还可以使用BBmisc
包中的sortByCol()
函数:
library(BBmisc)
sortByCol(dd, c("z", "b"), asc = c(FALSE, TRUE))
b x y z
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
#12楼
针对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
#13楼
假设您有一个data.frame
A
并且想要使用称为x
降序的列对其进行排序。 调用排序的data.frame
newdata
newdata <- A[order(-A$x),]
如果要升序,则不要用"-"
代替。 你可以有类似的东西
newdata <- A[order(-A$x, A$y, -A$z),]
其中x
和z
是data.frame
A
中的某些列。 这意味着对data.frame
A
按x
降序, y
升序和z
降序排序。
#14楼
另一种选择,使用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
#15楼
当我想自动执行n列的排序过程时,我在上述解决方案中苦苦挣扎,n列的名称每次都可能不同。 我从psych
软件包中发现了一个超级有用的功能,可以很简单地做到这一点:
dfOrder(myDf, columnIndices)
其中columnIndices
是一个或多个列的索引,按照您要对其进行排序的顺序。 此处的更多信息:
#16楼
dplyer中的ranging()是我最喜欢的选项。 使用管道操作员,从最不重要的方面转到最重要的方面
dd1 <- dd %>%
arrange(z) %>%
arrange(desc(x))
#17楼
仅出于完整性考虑,由于关于列号排序的讨论很少,因此可以肯定地说,这样做通常是不理想的(因为列的顺序可能会发生变化,从而为错误铺平了道路),但是在某些特定情况下(例如,当您需要快速完成工作并且没有这样的更改列的风险)时,这可能是最明智的选择,尤其是在处理大量列时。
在这种情况下, do.call()
可以解决:
ind <- do.call(what = "order", args = iris[,c(5,1,2,3)])
iris[ind, ]
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 14 4.3 3.0 1.1 0.1 setosa
## 9 4.4 2.9 1.4 0.2 setosa
## 39 4.4 3.0 1.3 0.2 setosa
## 43 4.4 3.2 1.3 0.2 setosa
## 42 4.5 2.3 1.3 0.3 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 48 4.6 3.2 1.4 0.2 setosa
## 7 4.6 3.4 1.4 0.3 setosa
## (...)
#18楼
Dirk的答案很好,但是如果您需要保留排序,则需要将排序重新应用到该数据框的名称上。 使用示例代码:
dd <- dd[with(dd, order(-z, b)), ]
#19楼
您的选择
- 从
base
order
- 从
dplyr
arrange
-
setorder
和setorderv
从data.table
- 从
plyr
arrange
- 从
taRifx
sort
- 来自
doBy
orderBy
-
sortData
从Deducer
多数情况下,除非没有依赖性很重要,否则应使用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 )
如果您是此功能的原始作者之一,请与我联系。 有关公共领域的讨论在这里: http : //chat.stackoverflow.com/transcript/message/1094290#1094290
如哈德利在上述线程中所指出的,您还可以使用plyr
arrange()
函数:
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)
无法进行演绎器加载。 需要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))
(线从下四分位延伸到上四分位,点是中位数)
考虑到这些结果并权衡简单性与速度,我不得不点头arrange
在plyr
软件包中 。 它具有简单的语法,但几乎与基本R命令一样复杂,具有复杂的机加工。 通常是出色的哈德利·威克姆(Hadley Wickham)的作品。 我唯一的困扰是它打破了由sort(object)
调用排序对象的标准R术语,但是我理解为什么Hadley会因为上述问题中讨论的问题而采用这种方式。