使用行号矩阵从矩阵中提取数值矩阵
我想要 高效地 从一个矩阵(vals)中提取值,使用一个单列号(val_col),以及一个行号矩阵(val_rows)。具体来说,我还希望我的 结果 与 val_rows 对应的矩阵形式。
# Matrix of values.
n_cols <- 3
n_rows <- 5
n_vals <- n_cols * n_rows
vals <- outer(
seq_len(n_rows), seq_len(n_cols),
paste, sep = "x"
)
vals
#> [,1] [,2] [,3]
#> [1,] "1x1" "1x2" "1x3"
#> [2,] "2x1" "2x2" "2x3"
#> [3,] "3x1" "3x2" "3x3"
#> [4,] "4x1" "4x2" "4x3"
#> [5,] "5x1" "5x2" "5x3"
# Column number.
val_col <- 2
# Matrix of row numbers.
n_rows_out <- 4
n_vals_out <- n_cols * n_rows_out
set.seed(111)
val_rows <- sample.int(n_rows, size = n_vals_out, replace = TRUE) |>
matrix(ncol = n_cols, byrow = TRUE)
val_rows
#> [,1] [,2] [,3]
#> [1,] 3 4 3
#> [2,] 1 3 5
#> [3,] 3 4 2
#> [4,] 1 5 5
现在我 想要 的结果是一个这样的矩阵:
result <- structure(
c("3x2", "1x2", "3x2", "1x2", "4x2", "3x2", "4x2", "5x2", "3x2", "5x2", "2x2", "5x2"),
dim = 4:3
)
result
#> [,1] [,2] [,3]
#> [1,] "3x2" "4x2" "3x2"
#> [2,] "1x2" "3x2" "5x2"
#> [3,] "3x2" "4x2" "2x2"
#> [4,] "1x2" "5x2" "5x2"
但当我仅用 [ 进行提取时,我的结果被“展平”为一个向量。
result <- vals[val_rows, val_col]
result
#> [1] "3x2" "1x2" "3x2" "1x2" "4x2" "3x2" "4x2" "5x2" "3x2" "5x2" "2x2" "5x2"
即使我指定 drop = FALSE,矩阵也并不像 val_rows 那样有结构。
result <- vals[val_rows, val_col, drop = FALSE]
result
#> [,1]
#> [1,] "3x2"
#> [2,] "1x2"
#> [3,] "3x2"
#> [4,] "1x2"
#> [5,] "4x2"
#> [6,] "3x2"
#> [7,] "4x2"
#> [8,] "5x2"
#> [9,] "3x2"
#> [10,] "5x2"
#> [11,] "2x2"
#> [12,] "5x2"
看来我可以在一行内简单地修改 dim()。
result <- vals[val_rows, val_col] |>
`dim<-`(c(n_rows_out, n_cols))
result
#> [,1] [,2] [,3]
#> [1,] "3x2" "4x2" "3x2"
#> [2,] "1x2" "3x2" "5x2"
#> [3,] "3x2" "4x2" "2x2"
#> [4,] "1x2" "5x2" "5x2"
result <- vals[val_rows, val_col, drop = TRUE] |>
`dim<-`(c(n_rows_out, n_cols))
result
#> [,1] [,2] [,3]
#> [1,] "3x2" "4x2" "3x2"
#> [2,] "1x2" "3x2" "5x2"
#> [3,] "3x2" "4x2" "2x2"
#> [4,] "1x2" "5x2" "5x2"
显然,我可以把向量输入到 matrix() 中,使其重组成与 val_rows 相同的结构。
result <- vals[val_rows, val_col] |>
matrix(ncol = n_cols, byrow = FALSE)
result
#> [,1] [,2] [,3]
#> [1,] "3x2" "4x2" "3x2"
#> [2,] "1x2" "3x2" "5x2"
#> [3,] "3x2" "4x2" "2x2"
#> [4,] "1x2" "5x2" "5x2"
但如果我记错了,通过 matrix() 填充值在大规模重复时效率并不高。我们难道不应该把整个结果按值传递给 dim() 以便就地修改其 dim 属性吗?
无论如何,这些做法冗余地“修复”本不应该发生的“损坏”。 那么,在保持 val_rows 结构的前提下,提取值的最有效方法是什么?
解决方案
是的,高效的解决方案就是在提取后重新恢复维度。
[ 在这里返回一个向量,因为矩阵索引在R 中遵循列主序。由于矩阵在R 中按列存储,vals[val_rows, val_col] 的结果已经按照与 val_rows 相同维度的矩阵的内部顺序排列。
所以你可以这样做:
result <- vals[val_rows, val_col]
dim(result) <- dim(val_rows)
result
# [,1] [,2] [,3]
# [1,] "#8" "#11" "#8"
# [2,] "#2" "#8" "#14"
# [3,] "#8" "#11" "#5"
# [4,] "#2" "#14" "#14"
# [5,] "#5" "#11" "#5"
这会得到与 val_rows 相对应的逐元素正确结果。
如果你想要一行代码:
result <- structure(vals[val_rows, val_col], dim = dim(val_rows))
或者,更明确地说:
result <- structure(vals[cbind(c(val_rows), val_col)], dim = dim(val_rows))
一个重要点是:matrix(..., byrow = TRUE) 在这里并不合适——它按行重新排序提取的值,而索引结果是按列主序生成的。
在R 层面,没有能保留索引矩阵形状的特殊提取形式:[ 返回一个向量,所以恢复 dim 是标准解决办法。因为在R 中,矩阵/数组本质上只是带有 "dim" 属性的向量:
dim(result) <- dim(val_rows)
至于复制:R使用的是copy-on-modify语义,因此来自R 代码的真正就地变更并不一定会发生。设置 dim 当对象未被共享时可能成本较低,但实际是否会发生复制,是实现细节,你不能依赖。
在纯R 中,dim<- / structure(..., dim = ...) 是惯用且高效的答案。