使用 Tidyverse 在 R 中嵌套 for 循环,其中在第一个循环中生成迭代器

Nested for loops in R Using Tidyverse, where iterator is generated within the first loop

我正在研究 R 中的一个小问题,我很好奇是否可以使用 purrr 遍历 excel 文件和 sheets.

我见过很多例子,其中 map() 被直接赋予可迭代对象,即 map(1:6, function(x)... 但我不确定 if/how 在我想要的时候这样做在第一个 map 调用中生成其中一个迭代器。

以这个例子为例,我们有一个包含 excel 个文件的文件夹,我们希望 运行 每个 sheet 具有相同的功能。所以我们需要两个循环,一个循环遍历文件,一个循环遍历 sheets.

    library(tidyverse)
    library(readxl)

    fileList <- list.files()

    
    customFunction <- function(xlsheet, filePath){
        return(1)
    }

    output <- list()
    i <- 1 

    for (file in fileList){
      # get a list of excel sheets in each file
      sheet_list <- excel_sheets(file)
        for (sheet in sheet_list){
          # apply customFunction to each sheet of each file
          output[[i]] <- customFunction(sheet, file)
          i <- i + 1
       }
    }

我认为我被卡住的地方是我在每次 customFunction().

的调用中都需要来自第一个和第二个循环的参数

看另一个问题中@r2evans 的例子,他们似乎在描述这样的事情:

map(fileList, ~ map(excel_sheets(.x), ~ customFunction(.x, .y)))

但是 returns 我的实际代码中的一个错误(在这个例子中它 returns 是一个嵌套列表而不是像 for 循环那样的单个列表,注意我的例子赢了如果 sheet 和路径未正确传递给 customFunction)

,则不会失败

Error in is_string(path) : the ... list contains fewer than 2 elements

老实说,我对 .x.y 代词有点迷茫

最后,如果尝试用 purrr 做这件事很愚蠢,而 for 循环通常是更好的解决方案,那也是很好的反馈。

在嵌套 map 时,使用 .x.y 可能会造成混淆。我通常更喜欢使用匿名函数来清楚,是的,嵌套 map 会 return 嵌套列表。您可以使用 flatten 获得一个大列表,如 for 循环或使用 unlistrecursive = FALSE.

library(purrr)

flatten(map(fileList, function(file) 
        map(excel_sheets(file), function(sheet) 
        customFunction(file, sheet))))