Sort (order) data frame rows by multiple columns

Sort (order) data frame rows by multiple columns

技术背景

在数据处理过程中,经常需要对数据框(data frame)的行按照多个列进行排序。R语言提供了多种方法来实现这一需求,不同方法在语法复杂度、性能和适用场景上有所不同。

实现步骤

1. 使用 base 包的 order() 函数

可以直接使用 order() 函数,通过指定排序的列和排序顺序来对数据框进行排序。

1
2
3
4
5
6
7
8
9
10
# 创建示例数据框
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[with(dd, order(-z, b)), ]
# 使用列索引排序
dd[order(-dd[,4], dd[,1]), ]

2. 使用 dplyr 包的 arrange() 函数

dplyr 包的 arrange() 函数提供了简洁的语法来对数据框进行排序。

1
2
library(dplyr)
arrange(dd, desc(z), b)

3. 使用 data.table 包的 setorder() 和 setorderv() 函数

data.table 包提供了高效的排序功能,特别是 setorder() 函数可以通过引用(in-place)的方式对数据框进行排序,节省内存。

1
2
3
4
5
6
7
8
library(data.table)
setDT(dd)
# 使用 setorder() 函数
setorder(dd, -z, b)
# 动态排序
sortBy <- c('Petal.Length', 'Petal.Width')
sortType <- c(-1, 1)
setorderv(dd, sortBy, sortType)

4. 使用 plyr 包的 arrange() 函数

plyr 包的 arrange() 函数也可以用于对数据框进行排序。

1
2
library(plyr)
arrange(dd, desc(z), b)

5. 使用 taRifx 包的 sort() 函数

1
2
library(taRifx)
sort(dd, f= ~ -z + b )

6. 使用 doBy 包的 orderBy() 函数

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

7. 使用 Deducer 包的 sortData() 函数

1
2
library(Deducer)
sortData(dd,c("z","b"),increasing= c(FALSE,TRUE))

8. 自定义排序函数

1
2
3
4
5
6
7
esort <- function(x, sortvar, ...) {
attach(x)
x <- x[with(x,order(sortvar,...)),]
return(x)
detach(x)
}
esort(dd, -z, b)

9. R 4.4.0 新函数 sort_by()

1
2
3
dd |>
sort_by(~ list(-z, b))
sort_by(dd, list(-dd$z, dd$b))

10. 使用 BBmisc 包的 sortByCol() 函数

1
2
library(BBmisc)
sortByCol(dd, c("z", "b"), asc = c(FALSE, TRUE))

11. 按列索引排序

1
2
ind <- do.call(what = "order", args = iris[,c(5,1,2,3)])
iris[ind, ]

12. 分步排序

1
2
dd <- dd[order(dd$b, decreasing = FALSE),]
dd <- dd[order(dd$z, decreasing = TRUE),]

核心代码

以下是几种常用方法的核心代码示例:

1
2
3
4
5
6
7
8
9
10
11
# base 包 order() 函数
dd[with(dd, order(-z, b)), ]

# dplyr 包 arrange() 函数
library(dplyr)
arrange(dd, desc(z), b)

# data.table 包 setorder() 函数
library(data.table)
setDT(dd)
setorder(dd, -z, b)

最佳实践

根据性能测试结果,data.table 包的 setorder() 函数在处理大数据时速度最快且内存使用效率最高。如果追求简洁的语法,dplyr 包的 arrange() 函数是不错的选择。在不需要额外依赖包的情况下,使用 base 包的 order() 函数也能满足基本需求。

常见问题

1. 性能问题

不同的排序方法在处理大数据时性能差异较大。可以使用 microbenchmark 包对不同方法进行性能测试,选择最适合的方法。

1
2
3
4
5
6
7
8
library(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
)

2. 与 microbenchmark 不兼容问题

部分自定义函数(如使用 attach()detach() 的函数)可能与 microbenchmark 不兼容,需要注意。

3. 动态排序问题

当需要动态指定排序的列和排序顺序时,可以使用 dplyrarrange_() 函数和 data.tablesetorderv() 函数。

1
2
3
4
5
6
7
8
9
10
11
12
# dplyr 动态排序
library(dplyr)
df1 <- tbl_df(iris)
sortBy <- c('Petal.Length', 'Petal.Width')
arrange_(df1, .dots = sortBy)

# data.table 动态排序
library(data.table)
dt1 <- data.table(iris)
sortBy <- c('Petal.Length', 'Petal.Width')
sortType <- c(-1, 1)
setorderv(dt1, sortBy, sortType)

Sort (order) data frame rows by multiple columns
https://119291.xyz/posts/sort-data-frame-rows-by-multiple-columns/
作者
ww
发布于
2025年6月9日
许可协议