【问题标题】:How do I rewrite these for loops to lapply in R如何重写这些 for 循环以在 R 中应用
【发布时间】:2015-08-21 14:32:31
【问题描述】:

一个for循环的多个绘图

我正在开发一个绘图函数并编写了这些 for 循环。我一直在阅读 for 循环对 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 循环

我正在尝试使用 apply 函数而不是我目前正在考虑的 for 循环来解决的第二个问题:

我想读取位于不同文件夹(命名为 959D22959D41)中的多个光栅图像(命名为 1.png8.png)。 我想根据文件夹和文件名将行名分配给列表项。这应该返回一个光栅图像列表,然后我可以以特定值将其添加到我的绘图中。

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
    }
}

阅读后,我仍然不知道构建此列表的最佳方法是什么。也许通过首先创建列表名称然后将光栅图像添加到这些条目?因为我想返回一个值,所以 apply 的变体在这里更好吗?

【问题讨论】:

  • 就其本身而言,for 循环在 R 中并不是“坏的”。
  • R 中的“坏”(= 慢)是在循环中增长对象。相反,您应该提前将它们分配到所需的大小。
  • 感谢您的回复。我将阅读 for 循环有时变慢的原因(发现:adv-r.had.co.nz/Functionals.html#lapply
  • 您能否详细说明第二个 sn-p 应该做什么?在不知道这两个目录的内容的情况下想一想这个有点困难(为什么会有两个目录?)。
  • 文件位于绝望的文件夹中,我将其加载为“核心”。我想阅读它们并将它们添加到名称为“文件夹名称文件名称”的命名列表中

标签: r for-loop lapply


【解决方案1】:

对于您的第一个问题,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) &lt;- 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 个字母乘 8 个数字的 for 循环并没有那么慢,但也许 lapply 也可以改进。

【讨论】:

  • 哇,非常感谢您的详尽回答!你真的帮助了我:)。我不熟悉基准测试功能,但据我所知,for 功能平均比 lapply 功能多花费约 400 毫秒?
猜你喜欢
  • 2020-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-09
相关资源
最近更新 更多