如何对用于置换的for循环进行矢量化?
我正在使用R进行分析,并希望执行置换测试。为此,我使用了一个非常慢的如何对用于置换的for循环进行矢量化?,r,performance,for-loop,vectorization,R,Performance,For Loop,Vectorization,我正在使用R进行分析,并希望执行置换测试。为此,我使用了一个非常慢的For循环,我希望使代码尽可能快。我认为矢量化是关键。然而,经过几天的尝试,我仍然没有找到一个合适的解决方案如何重新编码。我将非常感谢你的帮助 我有一个对称矩阵,种群之间的生态距离是成对的(“dist.mat”)。我想随机洗牌此距离矩阵的行和列,以生成置换距离矩阵(“dist.mat.mix”)。然后,我想将上三角值保存在这个置换距离矩阵中(大小为“nr.pairs”)。此过程应重复多次(“nr.runs”)。结果应该是一个矩阵
For
循环,我希望使代码尽可能快。我认为矢量化是关键。然而,经过几天的尝试,我仍然没有找到一个合适的解决方案如何重新编码。我将非常感谢你的帮助
我有一个对称矩阵,种群之间的生态距离是成对的(“dist.mat”
)。我想随机洗牌此距离矩阵的行和列,以生成置换距离矩阵(“dist.mat.mix”
)。然后,我想将上三角值保存在这个置换距离矩阵中(大小为“nr.pairs”
)。此过程应重复多次(“nr.runs”
)。结果应该是一个矩阵(“result”
),包含几个运行的排列上三角值,其维度为nrow=nr.runs
和ncol=nr.pairs
。下面是一个示例R代码,它正在使用for循环执行我想要的操作:
# example number of populations
nr.pops <- 20
# example distance matrix
dist.mat <- as.matrix(dist(matrix(rnorm(20), nr.pops, 5)))
# example number of runs
nr.runs <- 1000
# find number of unique pairwise distances in distance matrix
nr.pairs <- nr.pops*(nr.pops-1) / 2
# start loop
result <- matrix(NA, nr.runs, nr.pairs)
for (i in 1:nr.runs) {
mix <- sample(nr.pops, replace=FALSE)
dist.mat.mix <- dist.mat[mix, mix]
result[i, ] <- dist.mat.mix[upper.tri(dist.mat.mix, diag=FALSE)]
}
# inspect result
result
如果您知道如何使用简洁的矢量化解决方案加速我的for循环,我将非常感谢您的支持。这可能吗?稍微快一点:
minem <- function() {
result <- matrix(NA, nr.runs, nr.pairs)
ut <- upper.tri(matrix(NA, 4, 4)) # create upper triangular index matrix outside loop
for (i in 1:nr.runs) {
mix <- sample.int(nr.pops) # slightly faster sampling function
result[i, ] <- dist.mat[mix, mix][ut]
}
result
}
microbenchmark(my.for.loop(), my.replicate(), minem(), times = 100L)
# Unit: microseconds
# expr min lq mean median uq max neval cld
# my.for.loop() 75.062 78.222 96.25288 80.1975 104.6915 249.284 100 a
# my.replicate() 118.519 122.667 152.25681 126.0250 165.1355 495.407 100 a
# minem() 45.432 48.000 104.23702 49.5800 52.9380 4848.986 100 a
你的真实数据有多大?您想运行多少次
nr.runs
?nr.pops
是20次,nr.runs
是1000次。我在问题中更新了这个。这到底在哪里慢?你在做什么,这会变慢?运行my.for.loop()
的时间为0.01秒。你打过多次电话吗?有多少调用此函数1000次需要+/-20秒。对我来说,这似乎并不慢。这个for循环是一个更大的计算的一部分,它自身会重复数亿次。这一更大的计算将持续数周。减少所讨论的循环的计算时间(即使只有几微秒)可以节省我几个小时甚至几天的运行时间。我发现for
循环是我大型计算中最大的瓶颈,因此我想让它尽可能快。replicate
不是矢量化,它与sapply
/lappy
基本相同。这两个基本上是“for”循环。非常感谢,这正是我想要的!你能详细说明一下矩阵索引背后的逻辑吗?p1
和p2
在做什么?您如何从p
推导出最终矩阵结果2
?
minem <- function() {
result <- matrix(NA, nr.runs, nr.pairs)
ut <- upper.tri(matrix(NA, 4, 4)) # create upper triangular index matrix outside loop
for (i in 1:nr.runs) {
mix <- sample.int(nr.pops) # slightly faster sampling function
result[i, ] <- dist.mat[mix, mix][ut]
}
result
}
microbenchmark(my.for.loop(), my.replicate(), minem(), times = 100L)
# Unit: microseconds
# expr min lq mean median uq max neval cld
# my.for.loop() 75.062 78.222 96.25288 80.1975 104.6915 249.284 100 a
# my.replicate() 118.519 122.667 152.25681 126.0250 165.1355 495.407 100 a
# minem() 45.432 48.000 104.23702 49.5800 52.9380 4848.986 100 a
minem4 <- function() {
n <- dim(dist.mat)[1]
ut <- upper.tri(matrix(NA, n, n))
im <- matrix(1:n, n, n)
p1 <- im[ut]
p2 <- t(im)[ut]
dm <- unlist(dist.mat)
si <- replicate(nr.runs, sample.int(nr.pops))
p <- (si[p1, ] - 1L) * n + si[p2, ]
result2 <- matrix(dm[p], nr.runs, nr.pairs, byrow = T)
result2
}
microbenchmark(my.for.loop(), minem(), minem4(), times = 100L)
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# my.for.loop() 13.797526 14.977970 19.14794 17.071401 23.161867 29.98952 100 b
# minem() 8.366614 9.080490 11.82558 9.701725 15.748537 24.44325 100 a
# minem4() 7.716343 8.169477 11.91422 8.723947 9.997626 208.90895 100 a
minem5 <- function() {
n <- dim(dist.mat)[1]
ut <- upper.tri(matrix(NA, n, n))
im <- matrix(1:n, n, n)
p1 <- im[ut]
p2 <- t(im)[ut]
dm <- unlist(dist.mat)
require(dqrng)
si <- replicate(nr.runs, dqsample.int(nr.pops))
p <- (si[p1, ] - 1L) * n + si[p2, ]
result2 <- matrix(dm[p], nr.runs, nr.pairs, byrow = T)
result2
}
microbenchmark(my.for.loop(), minem(), minem4(), minem5(), times = 100L)
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# my.for.loop() 13.648983 14.672587 17.713467 15.265771 16.967894 36.18290 100 d
# minem() 8.282466 8.773725 10.679960 9.279602 10.335206 27.03683 100 c
# minem4() 7.719503 8.208984 9.039870 8.493231 9.097873 25.32463 100 b
# minem5() 6.134911 6.379850 7.226348 6.733035 7.195849 19.02458 100 a