【问题标题】:Use of function over all row-pairs of two matrices在两个矩阵的所有行对上使用函数
【发布时间】:2019-02-23 20:22:02
【问题描述】:

如果我想计算两个向量的n维距离,可以使用如下函数:

a = c(1:10)
b = seq(20, 23, length.out = length(a))

test_fun = 
  function(x,y) {
    return(
      sqrt(
        sum(
          (x - y) ^ 2
        )
      )
    )
  }

n_distance = test_fun(a,b)

现在,我想将其扩展为矩阵设置:我想计算两个矩阵的每对行的 n 维距离。

set.seed(123)
a_mtx = matrix(1:30, ncol = 5)
b_mtx = matrix(sample(1:15,15), ncol = 5)

n_distance_mtx = 
matrix(
  NA,
  nrow = nrow(b_mtx), 
  ncol = nrow(a_mtx)
  )
for(i in 1:nrow(b_mtx)) {
 for(j in 1:nrow(a_mtx)) {
  n_distance_mtx[i,j] = 
    test_fun(a_mtx[j,], b_mtx[i,])
 }
}

n_distance_mtx 的每一列包含a_mtxb_mtx 的每一行之间的距离度量(所以n_distance_mtx[,1]a_mtx[1,]b_mtx[1:3,] 之间的距离。

如果我计算n_distance_mtx 上的列均值,我可以获得a_mtx 中的每一行与b_mtx 的所有行之间的平均距离。

colMeans(n_distance_mtx)
#[1] 23.79094 24.90281 26.15618 27.53303 29.01668 30.59220

所以23.79094a_mtx[1,]b_mtx[1:3,]之间的平均距离,24.90281a_mtx[2,]b_mtx[1:3,]之间的平均距离,以此类推.

问题:如何在不使用 for 循环的情况下获得相同的解决方案?

我想将此方法应用于具有更大维度(大约数十万行)的矩阵。看看thisthis,好像一定有​​办法用Vectorizedouter函数来完成这个,但是我一直无法生成这样的函数。

test_fun_vec = 
 Vectorize(
   function(x,y) {
     outer(
       x,
       y,
       test_fun
       )
   }
 )
test_fun_vec(a_mtx,b_mtx)
#[1]  4  0  2  7  4  6  3  5  1  5  7  5 10  0  9 11 15 17  8 11  9 12 10 16
#[25] 10 22 20 25 15 24

【问题讨论】:

    标签: r


    【解决方案1】:

    我们可以使用Vectorizeouter

    f1 <- Vectorize(function(i, j) test_fun(a_mtx[j, ], b_mtx[i, ]))
    out <- outer(seq_len(nrow(b_mtx)), seq_len(nrow(a_mtx)), FUN = f1)
    out
    #         [,1]     [,2]     [,3]     [,4]     [,5]     [,6]
    #[1,] 20.88061 21.84033 22.97825 24.26932 25.69047 27.22132
    #[2,] 24.87971 25.57342 26.43861 27.45906 28.61818 29.89983
    #[3,] 25.61250 27.29469 29.05168 30.87070 32.74141 34.65545
    
    colMeans(out)
    #[1] 23.79094 24.90281 26.15618 27.53303 29.01668 30.59220
    
    identical(n_distance_mtx, out)
    #[1] TRUE
    

    【讨论】:

    • 我想知道为什么我的结果与你和 OP 的结果不同,然后我找到了sample
    • @R.Schifini 是的,这是 OP 未指定的 set.seed
    • 对此深表歉意。
    • @NigelStackhouse 没关系。我正在尝试将您的 for 循环输出作为参考
    • 就时间而言,这个答案很重要。非常感谢!经过的相对时间:n_distance_mtx_apply = 10.2; n_distance_mtx_forloop = 26.4; n_distance_mtx_vec = 1。
    【解决方案2】:

    如果我正确理解了您的问题,您希望 a_mtx 中的每个向量(行)与 b_mtx 中的其他向量之间的欧几里得距离。

    如果是这样,您可以像这样使用 apply 两次:

    result = apply(a_mtx, 1, function(x){ apply(b_mtx, 1, function(y){ test_fun(x,y) })})
    

    这给出了一个距离矩阵:

             [,1]     [,2]     [,3]     [,4]     [,5]     [,6]
    [1,] 20.88061 21.84033 22.97825 24.26932 25.69047 27.22132
    [2,] 24.87971 25.57342 26.43861 27.45906 28.61818 29.89983
    [3,] 25.61250 27.29469 29.05168 30.87070 32.74141 34.65545
    

    其中行索引是来自b_mtx 的对应向量(行),列索引是来自a_mtx 的对应向量

    最后,使用以下方法获得平均距离:

    colMeans(result)
    [1] 23.79094 24.90281 26.15618 27.53303 29.01668 30.59220
    

    【讨论】:

    • 当 OP 更新为 set.seed 时,最好更改输出
    猜你喜欢
    • 1970-01-01
    • 2016-07-04
    • 1970-01-01
    • 1970-01-01
    • 2015-07-31
    • 1970-01-01
    • 2015-02-21
    • 1970-01-01
    • 2019-10-04
    相关资源
    最近更新 更多