簡體   English   中英

使用 purrr::map_df 在 function 中轉發 arguments

[英]Forwarding arguments in a function with purrr::map_df

我正在嘗試創建一個 function,它使用readxl::read_excel讀取 excel 工作簿中的所有工作表並將它們綁定到單個數據幀中,並允許我通過額外的read_excel 我可以很好地完成第一部分,但不能完成第二部分。

library(magrittr)

# example excel workbook with multiple sheets
path <- readxl::readxl_example("datasets.xlsx")

# function with simple forwarding
read_all <- function(path, ...) {

  path %>%
    readxl::excel_sheets() %>%
    rlang::set_names() %>%
    purrr::map_df(~ readxl::read_excel(path = path, sheet = .x, ...))

}

# errors with and without additional arguments
read_all(path)
read_all(path, skip = 5)

我應該取回一個文件,而不是我得到一個錯誤:

Error: Can't guess format of this cell reference: iris
In addition: Warning message: Cell reference follows neither the A1 nor R1C1 format. Example: iris NAs generated.

沒有參數傳遞 function 工作正常:

# Function works without passing extra params
read_all_0 <- function(path) {

  path %>%
    readxl::excel_sheets() %>%
    rlang::set_names() %>%
    purrr::map_df(~ readxl::read_excel(path = path, sheet = .x))

}

read_all_0(path)

參數傳遞在沒有purrr::map_df的簡單 function 中工作正常

read_test <- function(path, ...) {

  path %>% readxl::read_excel(...)
}
read_test(path, skip = 10)

一種可能的解決方案是創建一個名為 function 的僅接受一個參數並將其傳遞給map的參數,以便唯一的參數是您正在循環的向量/列表。

應用於您的問題的解決方案將如下所示:

# function with forwarding
read_all <- function(path, ...) {

  # function within function that sets the arguments path and ellipsis as given and only leaves sheet to be determined
  read_xl <- function(sheet) {
    readxl::read_excel(path = path, sheet, ...)
  }

  path %>%
    readxl::excel_sheets() %>%
    rlang::set_names() %>%
    purrr::map_df(read_xl)

}

# this allows you to pass along arguments in the ellipsis correctly
read_all(path)
read_all(path, col_names = FALSE)

這個問題似乎源於對purrr::as_mapper function 的環境處理不當。 為了避免這種情況,我建議在評論中使用匿名 function。 顯然,下面的方法也有效。

read_all <- function(path, ...) {

  path %>%
    readxl::excel_sheets() %>%
    rlang::set_names() %>%
    purrr::map_df(function(x) {
                      readxl::read_excel(path = path, sheet = x, ...)
                   })

}

為了驗證確實是導致問題的as_mapper function,我們可以使用as_mapper從上面重寫命名的函數中函數。 在省略號中有和沒有額外的 arguments 時,這再次產生錯誤。

# function with forwarding
read_all <- function(path, ...) {

  # named mapper function
  read_xl <- purrr::as_mapper(~ readxl::read_excel(path = path, sheet = .x, ...))

  path %>%
    readxl::excel_sheets() %>%
    rlang::set_names() %>%
    purrr::map_df(read_xl)

} 

更新知道as_mapper導致了這個問題,我們可以更深入地研究這個問題。 現在我們可以在 RStudio 調試器中檢查運行read_excel的簡單映射器版本時發生的情況:

read_xl <- purrr::as_mapper(~ readxl::read_excel(path = .x, sheet = .y, ...))
debugonce(read_xl) 
read_xl(path, 1)

似乎當映射器 function 中包含省略號時, as_mapper將第一個參數映射到.x ,而且還自動映射到省略號... 我們可以通過創建一個簡單的映射器paster粘貼兩個 arguments .x...來驗證這一點。

paster <- purrr::as_mapper(~ paste0(.x, ...))
paster(1)
> [1] "11"
paster(2)
> [1] "22"

現在的問題是:我們是否應該在映射器函數中使用省略號的另一種方式,或者這是一個錯誤。

我原以為以下方法會起作用:

read_all <- function(path, ...) {

  path %>%
    readxl::excel_sheets() %>%
    purrr::set_names() %>%
    map_df(~readxl::read_excel(path=path, sheet=.x), ...)

}

因為map系列有一個...參數,用於將額外的 arguments 傳遞給映射的 function。 但是,以下代碼忽略了n_max參數,仍然返回各種數據幀的所有行,而不是返回一個有 8 行的數據幀(四張表各有 2 行):

p <- readxl_example("datasets.xlsx")
read_all(p, n_max=2)

但是,這有效:

read_all <- function(path, ...) {

  path %>% 
    excel_sheets() %>% 
    set_names() %>%
    map_df(read_excel, path=path, ...)

}

p <- readxl_example("datasets.xlsx")
read_all(path=p, n_max=2)

在上面, path和任何其他 arguments in ...被傳遞給read_excel並且(顯然)工作表名稱(如果我們顯式使用它將是.x )被隱式傳遞給sheet參數,我猜是因為path已經提供了第一個參數。 我真的不明白這一點,它似乎不是一種特別透明的方法,但我想我會把它放在那里,以防其他人可以解釋發生了什么並提供更好的代碼。

暫無
暫無

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

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