如何在 R 中将这些 for 循环重写为 lapply
How do I rewrite these for loops to lapply in R
多个图和一个 for 循环
我正在研究绘图函数并编写了这些 for 循环。我一直在阅读 for loops are bad for memory use in R 并且我应该使用 apply 或其变体之一进行编程。
但是我不明白应该将什么数据框或列表作为第一个参数传递。
我想将此代码替换为使用 apply 的代码:
BasicPlot(depth, var[,1], xtitle=xlab)
for(i in 2:ncol(var))
BasicPlot(depth, var[,i], add=TRUE, xtitle=xlab, ...)
如果您想知道我想要实现的目标,请参阅我的 DepthPlotter project on github。
编辑:阅读 this site 关于应用函数后我找到了一个解决方案:
lapply(2:ncol(var), function(i) { BasicPlot(depth, var[,i], add=TRUE, xtitle=xlab, ...)})
这有效但给了我愚蠢的输出,在这种情况下是 [[1]] NULL [[2]] NULL
等的列表,我可以通过用 invisible(...)
.
包围代码来使其静音
这实际上比以前的代码更好吗?它 a) 更容易阅读和 b) 更快吗?
读取多个文件夹中的多个文件:双 for 循环
我试图用应用函数代替我目前正在考虑的 for 循环来解决的第二个问题:
我想读取多个光栅图像(名称为 1.png
到 8.png
),它们位于单独的文件夹中(名称为 959D22
到 959D41
)。
我想根据文件夹和文件名将行名分配给列表项。这应该 return 一个光栅图像列表,然后我可以将其添加到我的特定值的地块中。
cores <- list.files("data/core_splitpics/") # folder names
pics <- list() # according to replies below, this is the sort of thing that makes for-loops bad in R, because I'm expanding a list step by step.
for(i in 1:length(cores)){ # loop over folders
imgs <- list.files(paste("data/core_splitpics/", cores[i]) # filenames in folder i
for(j in 1:length(imgs)){ # loop over files
pics[[i, j]] <- as.raster(readPNG(paste("data/core_splitpics",
cores[i], paste(j, ".png", sep=""), sep="/"))) # something like this
}
}
看完之后,我仍然不知道构建此列表的最佳方法是什么。也许首先创建列表名称,然后将光栅图像添加到这些条目?因为我想 return 一个值,所以 apply 的变体在这里更好吗?
对于您的第一个问题,lapply 会增加复杂性,因为它会返回您未使用的列表,for 循环更直接但更慢,开销可能会或可能不会很大,具体取决于速度调用函数的。
对于第二部分我会这样做(例如虚拟输入,所以我保留了内部循环,如果不在内部循环中调用像 readPNG
这样的标量输入函数就可以避免):
cores <- list("A", "B", "C")
pics <- rep(list(vector("character",1)),length(cores))
for(i in 1:length(cores)) {
imgs <- list("1","2","3")
pics[[i]] <- vector("character",length(imgs))
for(j in 1:length(imgs)) {
pics[[i]][j] <- paste(cores[[i]],imgs[j],sep="/")
}
}
这样你就不会在每次迭代时增长和复制,而是分配一次尽可能少的时间。
输出:
> pics
[[1]]
[1] "A/1" "A/2" "A/3"
[[2]]
[1] "B/1" "B/2" "B/3"
[[3]]
[1] "C/1" "C/2" "C/3"
为了更方便地访问,您可以执行 names(pics) <- cores
以获取:
> pics
$A
[1] "A/1" "A/2" "A/3"
$B
[1] "B/1" "B/2" "B/3"
$C
[1] "C/1" "C/2" "C/3"
因此您可以单独访问每个核心,例如 pics$A
.
最后,如果你想处理所有文件 unlist(pics)
以获得所有文件的向量,你可以传递给 for
循环或 sapply
或任何其他函数一个向量作为输入。
> for(p in unlist(pics)) { print(p) }
[1] "A/1"
[1] "A/2"
[1] "A/3"
[1] "B/1"
[1] "B/2"
[1] "B/3"
[1] "C/1"
[1] "C/2"
[1] "C/3"
为了了解性能差异,我做了一些基准测试:
test.for <- function() {
cores <- LETTERS[1:26]
pics <- rep(list(vector("character",1)),length(cores))
for(i in 1:length(cores)) {
imgs <- 1:8
pics[[i]] <- vector("character",length(imgs))
for(j in 1:length(imgs)) {
pics[[i]][j] <- paste(cores[[i]],imgs[j],sep="/")
}
}
return(pics)
}
test.lapply <- function() {
cores <- LETTERS[1:26]
pics <- lapply( seq_along(cores),
function(i) {
imgs <- 1:8
return(unlist(lapply( seq_along(imgs),
function(j) {
paste( cores[[i]],
imgs[j],
sep="/"
)
})
)
)
})
return(pics)
}
identical(test.for(),test.lapply())
microbenchmark(test.for(),test.lapply(),times=10L)
结果:
> identical(test.for(),test.lapply())
[1] TRUE
> microbenchmark(test.for(),test.lapply(),times=10L)
Unit: microseconds
expr min lq mean median uq max neval
test.for() 1241.166 1279.239 1392.894 1318.636 1405.375 1724.522 10
test.lapply() 997.502 1013.393 1044.083 1024.152 1042.196 1155.090 10
在这个用例中,对于 26 个字母 x 8 个数字,for 循环并没有那么慢,但也许 lapply 也可以改进。
多个图和一个 for 循环
我正在研究绘图函数并编写了这些 for 循环。我一直在阅读 for loops are bad for memory use in R 并且我应该使用 apply 或其变体之一进行编程。 但是我不明白应该将什么数据框或列表作为第一个参数传递。
我想将此代码替换为使用 apply 的代码:
BasicPlot(depth, var[,1], xtitle=xlab)
for(i in 2:ncol(var))
BasicPlot(depth, var[,i], add=TRUE, xtitle=xlab, ...)
如果您想知道我想要实现的目标,请参阅我的 DepthPlotter project on github。
编辑:阅读 this site 关于应用函数后我找到了一个解决方案:
lapply(2:ncol(var), function(i) { BasicPlot(depth, var[,i], add=TRUE, xtitle=xlab, ...)})
这有效但给了我愚蠢的输出,在这种情况下是 [[1]] NULL [[2]] NULL
等的列表,我可以通过用 invisible(...)
.
这实际上比以前的代码更好吗?它 a) 更容易阅读和 b) 更快吗?
读取多个文件夹中的多个文件:双 for 循环
我试图用应用函数代替我目前正在考虑的 for 循环来解决的第二个问题:
我想读取多个光栅图像(名称为 1.png
到 8.png
),它们位于单独的文件夹中(名称为 959D22
到 959D41
)。
我想根据文件夹和文件名将行名分配给列表项。这应该 return 一个光栅图像列表,然后我可以将其添加到我的特定值的地块中。
cores <- list.files("data/core_splitpics/") # folder names
pics <- list() # according to replies below, this is the sort of thing that makes for-loops bad in R, because I'm expanding a list step by step.
for(i in 1:length(cores)){ # loop over folders
imgs <- list.files(paste("data/core_splitpics/", cores[i]) # filenames in folder i
for(j in 1:length(imgs)){ # loop over files
pics[[i, j]] <- as.raster(readPNG(paste("data/core_splitpics",
cores[i], paste(j, ".png", sep=""), sep="/"))) # something like this
}
}
看完之后,我仍然不知道构建此列表的最佳方法是什么。也许首先创建列表名称,然后将光栅图像添加到这些条目?因为我想 return 一个值,所以 apply 的变体在这里更好吗?
对于您的第一个问题,lapply 会增加复杂性,因为它会返回您未使用的列表,for 循环更直接但更慢,开销可能会或可能不会很大,具体取决于速度调用函数的。
对于第二部分我会这样做(例如虚拟输入,所以我保留了内部循环,如果不在内部循环中调用像 readPNG
这样的标量输入函数就可以避免):
cores <- list("A", "B", "C")
pics <- rep(list(vector("character",1)),length(cores))
for(i in 1:length(cores)) {
imgs <- list("1","2","3")
pics[[i]] <- vector("character",length(imgs))
for(j in 1:length(imgs)) {
pics[[i]][j] <- paste(cores[[i]],imgs[j],sep="/")
}
}
这样你就不会在每次迭代时增长和复制,而是分配一次尽可能少的时间。
输出:
> pics
[[1]]
[1] "A/1" "A/2" "A/3"
[[2]]
[1] "B/1" "B/2" "B/3"
[[3]]
[1] "C/1" "C/2" "C/3"
为了更方便地访问,您可以执行 names(pics) <- cores
以获取:
> pics
$A
[1] "A/1" "A/2" "A/3"
$B
[1] "B/1" "B/2" "B/3"
$C
[1] "C/1" "C/2" "C/3"
因此您可以单独访问每个核心,例如 pics$A
.
最后,如果你想处理所有文件 unlist(pics)
以获得所有文件的向量,你可以传递给 for
循环或 sapply
或任何其他函数一个向量作为输入。
> for(p in unlist(pics)) { print(p) }
[1] "A/1"
[1] "A/2"
[1] "A/3"
[1] "B/1"
[1] "B/2"
[1] "B/3"
[1] "C/1"
[1] "C/2"
[1] "C/3"
为了了解性能差异,我做了一些基准测试:
test.for <- function() {
cores <- LETTERS[1:26]
pics <- rep(list(vector("character",1)),length(cores))
for(i in 1:length(cores)) {
imgs <- 1:8
pics[[i]] <- vector("character",length(imgs))
for(j in 1:length(imgs)) {
pics[[i]][j] <- paste(cores[[i]],imgs[j],sep="/")
}
}
return(pics)
}
test.lapply <- function() {
cores <- LETTERS[1:26]
pics <- lapply( seq_along(cores),
function(i) {
imgs <- 1:8
return(unlist(lapply( seq_along(imgs),
function(j) {
paste( cores[[i]],
imgs[j],
sep="/"
)
})
)
)
})
return(pics)
}
identical(test.for(),test.lapply())
microbenchmark(test.for(),test.lapply(),times=10L)
结果:
> identical(test.for(),test.lapply())
[1] TRUE
> microbenchmark(test.for(),test.lapply(),times=10L)
Unit: microseconds
expr min lq mean median uq max neval
test.for() 1241.166 1279.239 1392.894 1318.636 1405.375 1724.522 10
test.lapply() 997.502 1013.393 1044.083 1024.152 1042.196 1155.090 10
在这个用例中,对于 26 个字母 x 8 个数字,for 循环并没有那么慢,但也许 lapply 也可以改进。