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

当前回答

有了凯文·赖特(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

其他回答

假设您有一个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进行排序。

德克的回答很好。它还强调了用于索引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)]

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

我通过下面的例子了解了秩序,这让我困惑了很长一段时间:

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

此示例之所以有效,唯一的原因是顺序是按向量Age排序,而不是按数据帧数据中名为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)

由于没有名为age的向量,上述order的行结构不再有效:

databyage = my.data[order(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

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

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

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

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

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

如果SQL是自然生成的,sqldf包将按照Codd的意图处理ORDERBY。