在R中对矩阵进行排序

4

我有一个矩阵,但是有 n 行。

set.seed(123)
mt <- replicate(5, sample(1:3, 4, replace = TRUE))
mt
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    3    3    3    1    3
#> [2,]    3    2    1    2    3
#> [3,]    3    2    2    3    1
#> [4,]    2    2    2    1    1

为了逐行对其进行排序,我使用具有order函数的代码,类似于这里

od2 <- order(mt[1, ], mt[2, ], mt[3, ], mt[4, ])
mt[, od2]
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    1    3    3    3    3
#> [2,]    2    1    2    3    3
#> [3,]    3    2    2    1    3
#> [4,]    1    2    2    1    2

有没有办法将这段代码适应于n行?我尝试了评论中给出的第二个版本,但没有成功。我不熟悉do.call函数。

3个回答

4

do.call需要将一个列表作为函数调用的参数。因此,您需要将矩阵的行拆分成列表。函数asplit可以通过按其边缘拆分数组或矩阵来实现。

mt[,do.call(order, asplit(mt, 1))]
#     [,1] [,2] [,3] [,4] [,5]
#[1,]    1    3    3    3    3
#[2,]    2    1    2    3    3
#[3,]    3    2    2    1    3
#[4,]    1    2    2    1    2

asplit 目前(4.2.3)通过选择切片并在 for 循环中填充列表来执行此操作。其他选项可在 将矩阵转换为列表将矩阵转换为列向量列表 中找到。


比较一些可能的方法与一个更大的矩阵:

m <- matrix(sample(0:9, 1e6, TRUE), 1e3)

bench::mark(
asplit = m[,do.call(order, asplit(m, 1))],
data.frame = m[, do.call(order, data.frame(t(m)))],
splitRow = m[, do.call(order, split(m, row(m)))],
lapply = m[,do.call(order, lapply(1:nrow(m), function(i) m[i,]))],
splitNrow = m[, do.call(order, split(m, 1:nrow(m)))],
apply = m[, do.call(order, apply(m, 1, identity, simplify = FALSE))],
tapply = m[, do.call(order, tapply(m, row(m), identity))]
)

结果

  expression     min  median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time
  <bch:expr> <bch:t> <bch:t>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm>
1 asplit      8.44ms  9.02ms     111.    19.25MB     73.8    30    20      271ms
2 data.frame  6.88ms  7.24ms     138.    15.47MB     80.3    36    21    261.4ms
3 splitRow   19.79ms 19.98ms      48.9   31.01MB     73.3    10    15    204.6ms
4 lapply      6.79ms  6.99ms     141.    11.57MB     33.8    54    13    384.2ms
5 splitNrow   8.36ms  8.55ms     117.     7.76MB     16.3    50     7    428.8ms
6 apply       7.53ms  7.74ms     128.    15.38MB     50.6    43    17    335.9ms
7 tapply     27.64ms 28.47ms      35.4   38.73MB    165.      3    14     84.7ms

data.framelapply方法是最快的,splitNrow分配的额外内存最少,但asplit也不落后太多,而且更通用,适用于数组,并允许轻松更改边缘。


谢谢你介绍asplit函数,我之前不知道它的存在。 - Wael
了不起的回答。感谢提供了多种解决方案并附带了一个基准。 - undefined

3

this answer适应于通过一堆transpose按行排序,您可以执行以下操作:

t(t(mt)[do.call(order, as.data.frame(t(mt))),])

#      [,1] [,2] [,3] [,4] [,5]
# [1,]    1    3    3    3    3
# [2,]    2    1    2    3    3
# [3,]    3    2    2    1    3
# [4,]    1    2    2    1    2

或者,更简单一点(根据@Ronak Shah的评论):
mt[, do.call(order, data.frame(t(mt)))]

2
确实!我已经在我的回答中添加了它,但如果您觉得需要,可以添加一个单独的答案。 - Maël

1

使用split的选项

mt[, do.call(order, split(mt, row(mt)))]
      [,1] [,2] [,3] [,4] [,5]
[1,]    1    3    3    3    3
[2,]    2    1    2    3    3
[3,]    3    2    2    1    3
[4,]    1    2    2    1    2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接