根据另一个矩阵改进矩阵操作循环的方法

rma*_*nia 5 r matrix lapply mapply

我知道改进循环已被问过很多次.我们可以应用族函数来改进R中的for循环.

然而,有没有办法改善矩阵的操作,其中这些操作依赖于另一个矩阵?我的意思是这里,我设置为2的元素test基于另一个矩阵index:

for (i in 1:nrow(test)){
  test[i,index[i,]]  <- 2
}    # where index is predetermined matrix
Run Code Online (Sandbox Code Playgroud)

另一个例子是,我test根据另一个矩阵的行中元素的顺序设置值anyMatrix:

for (i in 1:nrow(test)){
   test[i,] <- order(anyMatrix[i,])
}
Run Code Online (Sandbox Code Playgroud)

我可以在这里使用lapply或sapply,但是它们会返回一个列表,并且将它转换回矩阵需要相同的时间.

可重复的例子:

test <- matrix(0, nrow = 10, ncol = 10)
set.seed(1234)
index <- matrix(sample.int(10, 10*10, TRUE), 10, 10)
anyMatrix <- matrix(rnorm(10*10), nrow = 10, ncol = 10)

for (i in 1:nrow(test)){
  test[i,index[i,]]  <- 2
}

for (i in 1:nrow(test)){
   test[i,] <- order(anyMatrix[i,])
}
Run Code Online (Sandbox Code Playgroud)

jos*_*ber 6

你在这里看起来确实有两个不同的问题.

问题1:给定一个矩阵index,每一行i和列j要设置test[i,j],如果到2 j出现在一行iindex.这可以通过简单的矩阵索引来完成,传递索引的2列矩阵,其中第一列是要索引的所有元素的行,第二列是要索引的所有元素的列:

test[cbind(as.vector(row(index)), as.vector(index))] <- 2
test
#       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#  [1,]    2    2    0    2    2    2    2    0    2     2
#  [2,]    2    0    2    2    2    2    2    0    2     2
#  [3,]    2    2    2    2    0    0    2    2    0     0
#  [4,]    2    2    0    0    0    2    2    2    0     2
#  [5,]    2    2    2    2    0    0    0    0    2     0
#  [6,]    0    0    0    0    0    2    2    2    2     0
#  [7,]    2    0    2    2    2    2    2    0    0     0
#  [8,]    2    0    2    2    2    2    0    2    0     2
#  [9,]    2    2    2    2    0    0    2    0    2     2
# [10,]    2    0    2    0    0    2    2    2    2     0
Run Code Online (Sandbox Code Playgroud)

由于这会在单个矢量化操作中执行所有操作,因此它应该比循环遍历行并单独处理它们更快.这是一个包含100万行和10列的示例:

OP <- function(test, index) {
  for (i in 1:nrow(test)){
    test[i,index[i,]]  <- 2
  }
  test
}
josliber <- function(test, index) {
  test[cbind(as.vector(row(index)), as.vector(index))] <- 2
  test
}
test.big <- matrix(0, nrow = 1000000, ncol = 10)
set.seed(1234)
index.big <- matrix(sample.int(10, 1000000*10, TRUE), 1000000, 10)
identical(OP(test.big, index.big), josliber(test.big, index.big))
# [1] TRUE
system.time(OP(test.big, index.big))
#    user  system elapsed 
#   1.564   0.014   1.591 
system.time(josliber(test.big, index.big))
#    user  system elapsed 
#   0.408   0.034   0.444 
Run Code Online (Sandbox Code Playgroud)

这里,矢量化方法快3.5倍.

问题2:你要设置行itest,以order应用到相应的行anyMatrix.你可以这样做apply:

(test <- t(apply(anyMatrix, 1, order)))
#       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#  [1,]    1   10    7    8    4    5    3    6    2     9
#  [2,]    8    7    1    6    3    4    9    5   10     2
#  [3,]    4    9    7    1    3    2    6   10    5     8
#  [4,]    1    2    6    4   10    3    9    8    7     5
#  [5,]    9    6    5    1    2    7   10    4    8     3
#  [6,]    9    3    8    6    5   10    1    4    7     2
#  [7,]    3    7    2    5    6    8    9    4    1    10
#  [8,]    9    8    1    3    4    6    7   10    5     2
#  [9,]    8    4    3    6   10    7    9    5    2     1
# [10,]    4    1    9    3    6    7    8    2   10     5
Run Code Online (Sandbox Code Playgroud)

我不希望这里的运行时有太大的变化,因为apply实际上只是循环遍历行,类似于你在解决方案中循环的方式.尽管如此,我更喜欢这种解决方案,因为它更少打字和更"R"的做事方式.

请注意,这两个应用程序使用了非常不同的代码,这在R数据操作中非常典型 - 有许多不同的专用运算符,您需要选择适合您任务的运算符.我认为没有一个函数或者甚至是一小组函数能够处理所有矩阵操作,其中该操作基于来自另一个矩阵的数据.