如何在r中矢量化此代码?也许使用apply()函数?
-
16-10-2019 - |
题
我真的很难在不使用1或2用于循环的情况下复制R代码中的DIST()函数的输出。 (如果您想知道我为什么这样做,那就是我可以进行距离计算,并提高我的R技能 - 因此,请仅涉及R!)
概述:矩阵传递给DIST(),该()计算欧几里得距离行,并输出每行之间距离的完整距离矩阵(例如,第1和50行之间的距离将在DistanceMatrix [1,50]和DistanceMatrix [ 50,1])。快速代码看起来像这样:
distancematrix <- as.matrix(dist(myMatrix, method="euclidean", diag = T, upper = T))
我使用以下代码成功地在R中成功产生了相同的输出:
for (i in 1:nrow(myMatrix)) {
for (j in 1:nrow(myMatrix)) {
distancematrix[i, j] <- sum(abs(myMatrix[i,] - myMatrix[j,]))
}
}
但是,使用两个嵌套的环比使用DIST()要慢得多。我已经阅读了很多有关使用apply()来优化较慢的循环的信息,但是到目前为止,我还没有让自己围绕它。我相信,只要输出矢量并在最后处理它,至少可以避免使用其中之一。但是,我无法生存一生,如何删除两个循环。
有人有想法吗?
解决方案
首先,应该注意的是,您发布的代码实际上并未复制 dist
功能,因为该行:
distancematrix[i, j] <- sum(abs(myMatrix[i,] - myMatrix[j,]))
不计算欧几里得距离;它应该是:
distancematrix[i, j] <- sqrt(sum((myMatrix[i,] - myMatrix[j,]) ^ 2))
这是两个依赖的解决方案 apply
. 。它们被简化了,尤其是不利用距离矩阵的对称性(如果考虑的话会导致2倍速度)。首先,生成一些测试数据:
# Number of data points
N <- 2000
# Dimensionality
d <- 10
# Generate data
myMatrix = matrix(rnorm(N * d), nrow = N)
为方便起见,定义:
# Wrapper for the distance function
d_fun <- function(x_1, x_2) sqrt(sum((x_1 - x_2) ^ 2))
第一种方法是组合 apply
和 sapply
:
system.time(
D_1 <-
apply(myMatrix, 1, function(x_i)
sapply(1:nrow(myMatrix), function(j) d_fun(x_i, myMatrix[j, ]))
)
)
user system elapsed
14.041 0.100 14.001
第二个仅使用 apply
(但要浏览索引,这些索引是使用 expand.grid
):
system.time(
D_2 <-
matrix(apply(expand.grid(i = 1:nrow(myMatrix), j = 1:nrow(myMatrix)), 1, function(I)
d_fun(myMatrix[I[["i"]], ], myMatrix[I[["j"]], ])
)
)
)
user system elapsed
39.313 0.498 39.561
但是,正如预期的那样,两者都比 dist
:
system.time(
distancematrix <- as.matrix(
dist(myMatrix, method = "euclidean", diag = T, upper = T)
)
)
user system elapsed
0.337 0.054 0.388