如何避免矩阵计算中的双for循环
How to avoid double for-loop in matrix calculation
我的函数可以工作,但是当我有大数据集时它会很慢。
我该怎么做才能加快速度?我知道我们应该避免使用双循环,但我不知道为什么。
非常感谢!
n <- 3
wr <- c(0.9, 0.6, 0.5)
mat <- matrix(1:9, nrow=3, byrow=TRUE)
tmp <- matrix(nrow = n, ncol = n)
out <- rep(0, n)
colsum <- apply(mat, 2, sum)
for (i in 1:n) {
for (j in 1:n) {
tmp[i, j] <- (mat[i, j]/ colsum[j])*(1-wr[j])
}
}
for (i in 1:n) {
out[i] <- 1-sum(tmp[1:n,i])
}
试试这个:
n <- 3
wr <- c(0.9, 0.6, 0.5)
mat <- matrix(1:9, nrow=3, byrow=TRUE)
tmp <- matrix(nrow = n, ncol = n)
out <- rep(0, n)
colsum <- apply(mat, 2, sum)
for (i in 1:n) {
for (j in 1:n) {
tmp[i, j] <- (mat[i, j]/ colsum[j])*(1-wr[j])
}
}
for (i in 1:n) {
out[i] <- 1-sum(tmp[1:n,i])
}
# alternatively:
tmp2 = t(t(mat) / colsum * (1-wr))
out2 = 1 - colSums(tmp)
> tmp
[,1] [,2] [,3]
[1,] 0.008333333 0.05333333 0.08333333
[2,] 0.033333333 0.13333333 0.16666667
[3,] 0.058333333 0.21333333 0.25000000
> out
[1] 0.9 0.6 0.5
> tmp2
[,1] [,2] [,3]
[1,] 0.008333333 0.05333333 0.08333333
[2,] 0.033333333 0.13333333 0.16666667
[3,] 0.058333333 0.21333333 0.25000000
> out2
[1] 0.9 0.6 0.5
使用apply
可以加快速度:
colsum <- apply(mat, 2, sum)
1 - rowSums(apply(mat, 1, function(x) (x / colsum)*(1-wr)))
我们可以清楚地看到 microbenchmark
包和使用更大 n
的区别:
n <- 1000
wr <- rep(c(0.9, 0.6, 0.5), length.out=n)
mat <- matrix(1:(n^2), nrow=n, byrow=TRUE)
tmp <- matrix(nrow = n, ncol = n)
out <- rep(0, n)
colsum <- apply(mat, 2, sum)
microbenchmark(
for_loops = {
for (i in 1:n) {
for (j in 1:n) {
tmp[i, j] <- (mat[i, j]/ colsum[j])*(1-wr[j])
}
}
for (i in 1:n) {
out[i] <- 1-sum(tmp[1:n,i])
}},
apply = {
out = 1 - rowSums(apply(mat, 1, function(x) (x / colsum)*(1-wr)))
},
transpose = {
tmp = t(t(mat) / colsum * (1-wr))
out = 1 - colSums(tmp)
},
rowSums = {
1 - rowSums(t(mat) / colsum * (1-wr))
}
)
有趣的是,@BellmanEqn 的转置方法似乎比使用 apply 更快,但使用 @user20650 提出的 rowSums
而不是第二次转置甚至超过平均水平。
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# for_loops 198.6269 211.68075 246.55071 220.3864 239.66485 476.6462 100 c
# apply 21.7299 23.98720 39.97067 29.9156 33.85995 232.0723 100 b
# transpose 11.1222 11.66100 23.86154 13.6034 19.52560 271.2242 100 a
# rowSums 8.6790 9.32655 14.09392 10.0072 15.18220 171.8077 100 a
Base R 单线:
1-colSums(t(t(prop.table(mat, 2)) * (1 - wr)))
或使用扫描:
1-rowSums(t(sweep(mat, 2, "/", STATS = colSums(mat))) * (1 - wr))
数据:
n <- 3
wr <- c(0.9, 0.6, 0.5)
mat <- matrix(1:9, nrow=3, byrow=TRUE)
我的函数可以工作,但是当我有大数据集时它会很慢。
我该怎么做才能加快速度?我知道我们应该避免使用双循环,但我不知道为什么。
非常感谢!
n <- 3
wr <- c(0.9, 0.6, 0.5)
mat <- matrix(1:9, nrow=3, byrow=TRUE)
tmp <- matrix(nrow = n, ncol = n)
out <- rep(0, n)
colsum <- apply(mat, 2, sum)
for (i in 1:n) {
for (j in 1:n) {
tmp[i, j] <- (mat[i, j]/ colsum[j])*(1-wr[j])
}
}
for (i in 1:n) {
out[i] <- 1-sum(tmp[1:n,i])
}
试试这个:
n <- 3
wr <- c(0.9, 0.6, 0.5)
mat <- matrix(1:9, nrow=3, byrow=TRUE)
tmp <- matrix(nrow = n, ncol = n)
out <- rep(0, n)
colsum <- apply(mat, 2, sum)
for (i in 1:n) {
for (j in 1:n) {
tmp[i, j] <- (mat[i, j]/ colsum[j])*(1-wr[j])
}
}
for (i in 1:n) {
out[i] <- 1-sum(tmp[1:n,i])
}
# alternatively:
tmp2 = t(t(mat) / colsum * (1-wr))
out2 = 1 - colSums(tmp)
> tmp
[,1] [,2] [,3]
[1,] 0.008333333 0.05333333 0.08333333
[2,] 0.033333333 0.13333333 0.16666667
[3,] 0.058333333 0.21333333 0.25000000
> out
[1] 0.9 0.6 0.5
> tmp2
[,1] [,2] [,3]
[1,] 0.008333333 0.05333333 0.08333333
[2,] 0.033333333 0.13333333 0.16666667
[3,] 0.058333333 0.21333333 0.25000000
> out2
[1] 0.9 0.6 0.5
使用apply
可以加快速度:
colsum <- apply(mat, 2, sum)
1 - rowSums(apply(mat, 1, function(x) (x / colsum)*(1-wr)))
我们可以清楚地看到 microbenchmark
包和使用更大 n
的区别:
n <- 1000
wr <- rep(c(0.9, 0.6, 0.5), length.out=n)
mat <- matrix(1:(n^2), nrow=n, byrow=TRUE)
tmp <- matrix(nrow = n, ncol = n)
out <- rep(0, n)
colsum <- apply(mat, 2, sum)
microbenchmark(
for_loops = {
for (i in 1:n) {
for (j in 1:n) {
tmp[i, j] <- (mat[i, j]/ colsum[j])*(1-wr[j])
}
}
for (i in 1:n) {
out[i] <- 1-sum(tmp[1:n,i])
}},
apply = {
out = 1 - rowSums(apply(mat, 1, function(x) (x / colsum)*(1-wr)))
},
transpose = {
tmp = t(t(mat) / colsum * (1-wr))
out = 1 - colSums(tmp)
},
rowSums = {
1 - rowSums(t(mat) / colsum * (1-wr))
}
)
有趣的是,@BellmanEqn 的转置方法似乎比使用 apply 更快,但使用 @user20650 提出的 rowSums
而不是第二次转置甚至超过平均水平。
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# for_loops 198.6269 211.68075 246.55071 220.3864 239.66485 476.6462 100 c
# apply 21.7299 23.98720 39.97067 29.9156 33.85995 232.0723 100 b
# transpose 11.1222 11.66100 23.86154 13.6034 19.52560 271.2242 100 a
# rowSums 8.6790 9.32655 14.09392 10.0072 15.18220 171.8077 100 a
Base R 单线:
1-colSums(t(t(prop.table(mat, 2)) * (1 - wr)))
或使用扫描:
1-rowSums(t(sweep(mat, 2, "/", STATS = colSums(mat))) * (1 - wr))
数据:
n <- 3
wr <- c(0.9, 0.6, 0.5)
mat <- matrix(1:9, nrow=3, byrow=TRUE)