向量化 R-loop 以获得更好的性能
Vectorizing R-loop for better performance
我在为 R 中的特定循环找到 向量化 表示时遇到问题。我的 objective 是为了增强循环的性能,因为它必须在算法中 运行 数千次。
我想在由向量 'Level' 为每一行定义的特定数组部分中找到最低值的位置。
示例:
Level = c(2,3)
令数组 X 的第一行为:c(2, -1, 3, 0.5, 4)
.
在行的 1:Level[1]
范围内搜索最低值的位置(即 (2, -1)
),我得到 2,因为 -1 < 2 并且 -1 位于第二位行的位置。然后,搜索第二个范围 (Level[1]+1):(Level[1]+Level[2])
(即 (3, 0.5, 4)
)中最低值的位置,我得到 4,因为 0.5 < 3 < 4 并且 0.5 位于该行的第四个位置。
我必须对数组中的每一行执行此操作。
我的问题解决方案如下:
Level = c(2,3,3) #elements per section, here: 3 sections with 2,3 and 3 levels
rows = 10 #number of rows in array X
X = matrix(runif(rows*sum(Level),-5,5),rows,sum(Level)) #array with 10 rows and sum(Level) columns, here: 8
Position_min = matrix(0,rows,length(Level)) #array in which the position of minimum values for each section and row are stored
for(i in 1:rows){
for(j in 1:length(Level)){ #length(Level) is number of intervals, here: 3
if(j == 1){coeff=0}else{coeff=1}
Position_min[i,j] = coeff*sum(Level[1:(j-1)]) + which(X[i,(coeff*sum(Level[1:(j-1)])+1):sum(Level[1:j])] == min(X[i,(coeff*sum(Level[1:(j-1)])+1):sum(Level[1:j])]))
}
}
它工作正常,但我更喜欢性能更好的解决方案。有什么想法吗?
这将删除循环的外层:
Level1=c(0,cumsum(Level))
for(j in 1:(length(Level1)-1)){
Position_min[,j]=max.col(-X[,(Level1[j]+1):Level1[j+1]])+(Level1[j])
}
这是一个没有显式循环的 "fully vectorized" 解决方案:
findmins <- function(x, level) {
series <- rep(1:length(Level), Level)
x <- split(x, factor(series))
minsSplit <- as.numeric(sapply(x, which.min))
minsSplit + c(0, cumsum(level[-length(level)]))
}
Position_min_vectorized <- t(apply(X, 1, findmins, Level))
identical(Position_min, Position_min_vectorized)
## [1] TRUE
您可以通过将矩阵制成列表,然后使用 parallel
的 mclapply()
:
来获得更好的性能
X_list <- split(X, factor(1:nrow(X)))
do.call(rbind, parallel::mclapply(X_list, findmins, Level))
## [,1] [,2] [,3]
## 1 1 5 6
## 2 2 3 6
## 3 1 4 7
## 4 1 5 6
## 5 2 5 7
## 6 2 4 6
## 7 1 5 8
## 8 1 5 8
## 9 1 3 8
## 10 1 3 8
我在为 R 中的特定循环找到 向量化 表示时遇到问题。我的 objective 是为了增强循环的性能,因为它必须在算法中 运行 数千次。
我想在由向量 'Level' 为每一行定义的特定数组部分中找到最低值的位置。
示例:
Level = c(2,3)
令数组 X 的第一行为:c(2, -1, 3, 0.5, 4)
.
在行的 1:Level[1]
范围内搜索最低值的位置(即 (2, -1)
),我得到 2,因为 -1 < 2 并且 -1 位于第二位行的位置。然后,搜索第二个范围 (Level[1]+1):(Level[1]+Level[2])
(即 (3, 0.5, 4)
)中最低值的位置,我得到 4,因为 0.5 < 3 < 4 并且 0.5 位于该行的第四个位置。
我必须对数组中的每一行执行此操作。
我的问题解决方案如下:
Level = c(2,3,3) #elements per section, here: 3 sections with 2,3 and 3 levels
rows = 10 #number of rows in array X
X = matrix(runif(rows*sum(Level),-5,5),rows,sum(Level)) #array with 10 rows and sum(Level) columns, here: 8
Position_min = matrix(0,rows,length(Level)) #array in which the position of minimum values for each section and row are stored
for(i in 1:rows){
for(j in 1:length(Level)){ #length(Level) is number of intervals, here: 3
if(j == 1){coeff=0}else{coeff=1}
Position_min[i,j] = coeff*sum(Level[1:(j-1)]) + which(X[i,(coeff*sum(Level[1:(j-1)])+1):sum(Level[1:j])] == min(X[i,(coeff*sum(Level[1:(j-1)])+1):sum(Level[1:j])]))
}
}
它工作正常,但我更喜欢性能更好的解决方案。有什么想法吗?
这将删除循环的外层:
Level1=c(0,cumsum(Level))
for(j in 1:(length(Level1)-1)){
Position_min[,j]=max.col(-X[,(Level1[j]+1):Level1[j+1]])+(Level1[j])
}
这是一个没有显式循环的 "fully vectorized" 解决方案:
findmins <- function(x, level) {
series <- rep(1:length(Level), Level)
x <- split(x, factor(series))
minsSplit <- as.numeric(sapply(x, which.min))
minsSplit + c(0, cumsum(level[-length(level)]))
}
Position_min_vectorized <- t(apply(X, 1, findmins, Level))
identical(Position_min, Position_min_vectorized)
## [1] TRUE
您可以通过将矩阵制成列表,然后使用 parallel
的 mclapply()
:
X_list <- split(X, factor(1:nrow(X)))
do.call(rbind, parallel::mclapply(X_list, findmins, Level))
## [,1] [,2] [,3]
## 1 1 5 6
## 2 2 3 6
## 3 1 4 7
## 4 1 5 6
## 5 2 5 7
## 6 2 4 6
## 7 1 5 8
## 8 1 5 8
## 9 1 3 8
## 10 1 3 8