使用 purrr::map_df 在函数中转发参数
Forwarding arguments in a function with purrr::map_df
我正在尝试创建一个函数,该函数使用 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 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
的简单函数中工作正常
read_test <- function(path, ...) {
path %>% readxl::read_excel(...)
}
read_test(path, skip = 10)
我认为以下方法可行:
read_all <- function(path, ...) {
path %>%
readxl::excel_sheets() %>%
purrr::set_names() %>%
map_df(~readxl::read_excel(path=path, sheet=.x), ...)
}
因为 map
系列有一个 ...
参数用于将附加参数传递给映射函数。但是,以下代码忽略了 n_max
参数,仍然 returns 各种数据帧的所有行,而不是具有 8 行的数据帧(四个 sheet 中的每一个 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
和 ...
中的任何其他参数被传递给 read_excel
和(显然)sheet 名称(这将是 .x
如果我们显式使用它)被隐式传递给 sheet
参数,我猜是因为已经提供了第一个 path
参数。我不太明白这一点,它似乎不是一种特别透明的方法,但我想我会把它放在那里,以防其他人可以解释发生了什么并提供更好的代码。
一种可能的解决方案是创建一个仅接受一个参数并将其传递给 map
的命名函数,这样唯一的参数就是您正在循环的 vector/list。
应用于您的问题的解决方案如下所示:
# 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
函数的环境处理不当造成的。为了避免这种情况,我建议在评论中使用匿名函数。显然,下面的方法也有效。
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
函数导致了问题,我们可以使用 as_mapper
重写上面命名的函数中函数。这再次产生错误,在省略号中有和没有附加参数。
# 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)
似乎当映射器函数中包含省略号时,as_mapper
不仅将第一个参数映射到 .x
,而且还会自动映射到省略号 ...
。我们可以通过创建一个带有两个参数 .x
和 ...
的简单映射器函数 paster
来验证这一点。
paster <- purrr::as_mapper(~ paste0(.x, ...))
paster(1)
> [1] "11"
paster(2)
> [1] "22"
现在的问题是:是否有另一种方法我们应该在映射器函数中使用省略号,或者这是一个错误。
我正在尝试创建一个函数,该函数使用 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 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
的简单函数中工作正常
read_test <- function(path, ...) {
path %>% readxl::read_excel(...)
}
read_test(path, skip = 10)
我认为以下方法可行:
read_all <- function(path, ...) {
path %>%
readxl::excel_sheets() %>%
purrr::set_names() %>%
map_df(~readxl::read_excel(path=path, sheet=.x), ...)
}
因为 map
系列有一个 ...
参数用于将附加参数传递给映射函数。但是,以下代码忽略了 n_max
参数,仍然 returns 各种数据帧的所有行,而不是具有 8 行的数据帧(四个 sheet 中的每一个 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
和 ...
中的任何其他参数被传递给 read_excel
和(显然)sheet 名称(这将是 .x
如果我们显式使用它)被隐式传递给 sheet
参数,我猜是因为已经提供了第一个 path
参数。我不太明白这一点,它似乎不是一种特别透明的方法,但我想我会把它放在那里,以防其他人可以解释发生了什么并提供更好的代码。
一种可能的解决方案是创建一个仅接受一个参数并将其传递给 map
的命名函数,这样唯一的参数就是您正在循环的 vector/list。
应用于您的问题的解决方案如下所示:
# 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
函数的环境处理不当造成的。为了避免这种情况,我建议在评论中使用匿名函数。显然,下面的方法也有效。
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
函数导致了问题,我们可以使用 as_mapper
重写上面命名的函数中函数。这再次产生错误,在省略号中有和没有附加参数。
# 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)
似乎当映射器函数中包含省略号时,as_mapper
不仅将第一个参数映射到 .x
,而且还会自动映射到省略号 ...
。我们可以通过创建一个带有两个参数 .x
和 ...
的简单映射器函数 paster
来验证这一点。
paster <- purrr::as_mapper(~ paste0(.x, ...))
paster(1)
> [1] "11"
paster(2)
> [1] "22"
现在的问题是:是否有另一种方法我们应该在映射器函数中使用省略号,或者这是一个错误。