【问题标题】:Applying a function that takes columns and rows of matrices as input with a matrix as output without using loop应用一个函数,该函数将矩阵的列和行作为输入,将矩阵作为输出,而不使用循环
【发布时间】:2018-09-29 13:24:20
【问题描述】:

我想写一个函数,它将矩阵的列和行作为参数,并给出一个矩阵作为输出。

例如,一个函数将 m 的行 i 通过 k 矩阵 A 和列 k 的 jn 矩阵 B,并返回一个矩阵 M 与元素m_i,j 等于min(A[i,] * B[,j])(逐元素乘法):

有什么简单的方法可以避免使用循环吗?是否存在矩阵的 sapply 等效项?

> matrix_A
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    2    3    4    5    6
[3,]    3    4    5    6    7
[4,]    0    1    2    3    4
[5,]    5    6    7    8    9
> matrix_B
     [,1] [,2] [,3] [,4] [,5]
[1,]    7    6    5    4    3
[2,]    6    5    4    3    2
[3,]    1    2    3    4    5
[4,]    8    7    6    5    4
[5,]    9    8    7    6    5
> 
> output_matrix <- matrix(, nrow=nrow(matrix_A), ncol=ncol(matrix_B))
> for (row_i in 1:nrow(matrix_A)) {
+         for (col_j in 1:ncol(matrix_B)) {
+                 output_matrix[row_i, col_j] <- min(matrix_A[row_i,]*matrix_B[,col_j])
+         }
+ }
> output_matrix
     [,1] [,2] [,3] [,4] [,5]
[1,]    3    6    5    4    3
[2,]    4    8   10    8    6
[3,]    5   10   15   12    8
[4,]    0    0    0    0    0
[5,]    7   14   21   18   12
> 

【问题讨论】:

  • 我会使用 Rcpp 来避免不必要的复制。

标签: r matrix vectorization


【解决方案1】:

使用来自基础 R 的 apply

apply(m2, 2, function(i) apply(m1, 1, function(j) min(j*i)))

给出,

     [,1] [,2] [,3] [,4] [,5]
[1,]    3    6    5    4    3
[2,]    4    8   10    8    6
[3,]    5   10   15   12    8
[4,]    0    0    0    0    0
[5,]    7   14   21   18   12

一个完全矢量化的解决方案可以是,

t(matrix(do.call(pmin, 
       as.data.frame(
          do.call(rbind, rep(split(m1, 1:nrow(m1)), each = 5)) * do.call(rbind, rep(split(t(m2), 1:nrow(m2)), 5)))), 
       nrow(m1)))

【讨论】:

    【解决方案2】:

    对于这个特定示例,您可以避免 R 循环(*apply 函数也是循环)。通常一个有效的解决方案是可能的,但需要一个特定的算法,正如我在这里演示的那样。如果您不需要优化速度,请使用循环。您的 for 循环提供了最佳的可读性并且易于理解。

    matrix_A <- matrix(c(1,2,3,0,5,
                         2,3,4,1,6,
                         3,4,5,2,7,
                         4,5,6,3,8,
                         5,6,7,4,9), 5)
    matrix_B <- matrix(c(7,6,1,8,9,
                         6,5,2,7,8,
                         5,4,3,6,7,
                         4,3,4,5,6,
                         3,2,5,4,5), 5)
    
    #all combinations of i and j
    inds <- expand.grid(seq_len(nrow(matrix_A)), seq_len(ncol(matrix_B)))
    
    #subset A and transposed B then multiply the resulting matrices
    #then calculate rowwise min and turn result into a matrix
    library(matrixStats)
    matrix(rowMins(matrix_A[inds[[1]],] * t(matrix_B)[inds[[2]],]), nrow(matrix_A))
    #     [,1] [,2] [,3] [,4] [,5]
    #[1,]    3    6    5    4    3
    #[2,]    4    8   10    8    6
    #[3,]    5   10   15   12    8
    #[4,]    0    0    0    0    0
    #[5,]    7   14   21   18   12
    

    【讨论】:

    • 感谢您指出可读性问题。我应该先尝试使用 for-loop 或 double apply() 看看效果如何。
    【解决方案3】:

    我们使用expand.grid 来创建行和列对的所有可能组合。然后我们使用mapply 将所有行列组合元素相乘,然后从中选择min

    mat <- expand.grid(1:nrow(A),1:nrow(B))
    mapply(function(x, y) min(matrix_A[x,] * matrix_B[, y]) , mat[,1], mat[,2])
    
    #[1]  3  4  5  0  7  6  8 10  0 14  5 10 15  0 21  4  8 12  0 18  3  6  8  0 12
    

    假设matrix_Amatrix_Boutput_matrix都具有相同的尺寸,我们可以relistmapply的输出得到原始尺寸。

    output_matrix <- mapply(function(x, y) min(matrix_A[x,] * matrix_B[, y]),
                            mat[,1], mat[,2])
    
    relist(output_matrix, matrix_A)
    
    #     [,1] [,2] [,3] [,4] [,5]
    #[1,]    3    6    5    4    3
    #[2,]    4    8   10    8    6
    #[3,]    5   10   15   12    8
    #[4,]    0    0    0    0    0
    #[5,]    7   14   21   18   12
    

    【讨论】:

      【解决方案4】:

      这里我们使用pmap来遍历A和B的行列:

      library(tidyverse)
      
      pmap_dbl(expand.grid(1:nrow(A), 1:nrow(B)), ~ min(A[..1, ] * B[ , ..2])) %>% 
        matrix(nrow=5)
      
           [,1] [,2] [,3] [,4] [,5]
      [1,]    3    6    5    4    3
      [2,]    4    8   10    8    6
      [3,]    5   10   15   12    8
      [4,]    0    0    0    0    0
      [5,]    7   14   21   18   12
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-24
        • 1970-01-01
        • 2021-11-30
        • 2013-04-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多