簡體   English   中英

如何在R中向量化2個循環

[英]How to vectorize 2 loops in R

我有兩個矩陣:

matr1 = [k ; n]
matr2 = [n ; m]

和向量x length(x) = k向量x包含值1 : m

n = 5
m = 3
k = m * n

matr1 <- matrix(sample(seq(0,1, by = 0.1), size = k * n, replace = T), nrow = k, ncol = n )
matr2 <- matrix(sample(seq(0,1, by = 0.1), size = m * n, replace = T), nrow = n, ncol = m)
x <- sample(1:m, size = k, replace = T)

我必須執行以下操作,已使用for循環解決了該操作。

for( i in 1:k){
  for( j in 1:n){
    matr1[i, 1:(n-j+1)] <- matr1[i, 1:(n-j+1)] + 
      matr1[i, 1:(n-j+1)] * matr2[j , x[i]]
  }
}

有某種方法可以向量化它嗎?

還是可以使用某些技術來加快計算速度?

PS我考慮使用基本並行化,但是我想找到更多聰明的方法

花了我一段時間找出答案,但是:

foo <- apply(matr2,2,function(x) rev(cumprod(x+1)))
matr3 <- matr1*t(foo[,x])

-證明-

set.seed(100)
n = 5
m = 3
k = m * n

matr1 <- matrix(sample(seq(0,1, by = 0.1), size = k * n, replace = T), nrow = k, ncol = n )
matr2 <- matrix(sample(seq(0,1, by = 0.1), size = m * n, replace = T), nrow = n, ncol = m)
x <- sample(1:m, size = k, replace = T)

foo <- apply(matr2,2,function(x) rev(cumprod(x+1)))
matr3 <- matr1*t(foo[,x])

for( i in 1:k){
  for( j in 1:n){
    matr1[i, 1:(n-j+1)] <- matr1[i, 1:(n-j+1)] + 
      matr1[i, 1:(n-j+1)] * matr2[j , x[i]]
  }
}

all.equal(matr3,matr1)
# TRUE

-說明-

所以花了我一段時間才能正確地解決這個問題,但是這里...假設您的代碼並假設i = 1 ,我們基本上可以為j=1編寫:

matr1[1,1:5] <- matr1[1,1:5] + matr1[1,1:5] * matr2[1,3]

因此,您從第1行到第1列至第5列,並用原始數字加上這些數字乘以其他數字(在本例中為0.8 )來更新這些數字。 然后,當j=2

matr1[1,1:4] <- matr1[1,1:4] + matr1[1,1:4] * matr2[2,3]

因此,現在您只獲取除n所有列,並以與步驟1相同的方式更新值。最后,應該清楚的模式是matr1[1,1]被更新n次,而matr[1,n]更新1次(僅使用matr2[1,3]

我們通過一次性計算所有步驟來利用這種模式。 我們這樣做:

foo <- apply(matr2,2,function(x) rev(cumprod(x+1)))

這基本上是一個新表,其中對於matr1[i,]每一列都包含一個數字。 此數字是您之前的代碼遇到的所有循環的一個組合。 因此,代替需要5個乘法的matr1[1,1] ,我們現在只需做1。

現在我們有了:

for (i in 1:k) for (j in 1:n) matr1[i,j] <- matr1[i,j] * foo[j,x[i]]

我們可以將其減少為:

for (i in 1:k) matr1[i,] <- matr1[i,] * foo[,x[i]]

因為每次索引時i總是從1:k ,所以我們也可以向量化它:

matr <- matr*t(foo[,x])

我們完成了。

-基准-

我重新運行了我作為證明提供的代碼塊,但是n=100m=100

您的代碼:

# user  system elapsed 
# 6.85    0.00    6.86 

我的代碼:

# user  system elapsed 
# 0.02    0.00    0.02 

可以apply kn-grid代替雙循環。

suppressOutput <- apply(expand.grid(1:k, 1:n), 1, function(y){
    matr1[y[1], 1:(n-y[2]+1)] <<- matr1[y[1], 1:(n-y[2]+1)] + matr1[y[1], 1:(n-y[2]+1)] * matr2[y[2] , x[y[1]]]
})

在我的機器上節省超過50%的計算時間。 雖然不是很漂亮。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM