如何在 R 中对 data.table 每行的数值升序排序并保持行顺序?
数据小陈
可以先将 data.table 转成数值矩阵,再用 apply(..., 1L, sort) 对每行排序,最后转置并转回 data.table。排序只改变行内数值的位置,不改变行的先后顺序。
对每一行执行升序排序
对于题目中有数据行、至少两列且所有列都是普通数值的表,可以直接使用:
library(data.table)
result <- as.data.table(
t(apply(as.matrix(data), 1L, sort, na.last = TRUE))
)
setnames(result, names(data))
apply 的第二个参数 1L 表示按行处理,sort 默认按升序排序。apply 会把每行的排序结果作为返回矩阵的一列,因此需要用 t() 转置,恢复原来的行列方向。
setnames 保留原来的列名。排序后,各列表示该行数值从小到大的位置,原来数值与列名的对应关系不再保留。
可直接运行的示例
原始 dput 内容中的 <pointer: ...> 是指针的打印表示,不能直接作为 R 代码执行。可以用 fread 重建示例数据:
library(data.table)
data <- fread("20 1 54 5 55 33
2 48 20 28 6 3
7 23 4 6 55 40
20 56 51 6 22 3
48 28 55 52 38 15
12 11 30 42 10 33", sep = " ", header = FALSE)
result <- as.data.table(
t(apply(as.matrix(data), 1L, sort, na.last = TRUE))
)
setnames(result, names(data))
result
排序结果为:
V1 V2 V3 V4 V5 V6
1: 1 5 20 33 54 55
2: 2 3 6 20 28 48
3: 4 6 7 23 40 55
4: 3 6 20 22 51 56
5: 15 28 38 48 52 55
6: 10 11 12 30 33 42
为什么 arrange 和 setorder 不适用
dplyr::arrange 和 data.table::setorder 根据指定列的值重新排列整行。例如,按 V1 排序会改变行的先后顺序,但每行的数值仍留在原来的列中。
要对行内数值排序,需要分别对每一行执行 sort。
类型和缺失值注意事项
na.last = TRUE会保留NA,并将其放在每行末尾。sort默认会移除缺失值,可能导致各行排序后的长度不同。- 参与排序的列应全部是普通数值列。如果混入字符列,
as.matrix可能把整个矩阵转换为字符,排序也会变成字符顺序。 - 原示例中的
<num>表示 double,预期结果中的<int>表示 integer。数值排列相同并不意味着存储类型相同;排序本身不要求将 double 转成 integer。 - 上面的简洁写法适用于至少两列且有数据行的情况。单列数据无需进行行内排序,空表也可以直接保留。
转换矩阵和生成结果都会占用额外内存。如果数据量很大,应结合表的大小评估内存开销,不能仅凭行数判断性能。
备注:内容仅供参考。
