purrr pmap 函数参数
purrr pmap function arguments
使用 *apply
或 purrr
进行函数式编程是我的生计,但我不明白 pmap
如何处理函数参数。
- 我可以使用它们对应的变量名(给定一个数据框作为输入)而不必担心顺序。
- 我可以更具体一点,将它们用作命名参数
- 我可以使用相似的参数名称
- 然而我不能使用完全不同的名字。
我一直在寻找多个类似的问题,但未能找到合适的答案
A) 发生了什么以及
B) 如何使用任意函数参数名称?
# dummy data -----------------------------------------------------------------
(iter_tibble <- tibble::tibble(a = 1:2,
b = 3:4,
c = 7:6))
#> # A tibble: 2 x 3
#> a b c
#> <int> <int> <int>
#> 1 1 3 7
#> 2 2 4 6
# pmap it --------------------------------------------------------------------
# standard way
purrr::pmap(iter_tibble, function(a, b, c) {
paste(a, b, c)
})
#> [[1]]
#> [1] "1 3 7"
#>
#> [[2]]
#> [1] "2 4 6"
# switch order
# works and a maps to a, b to b etc
purrr::pmap(iter_tibble, function(b, c, a) {
paste(a, b, c)
})
#> [[1]]
#> [1] "1 3 7"
#>
#> [[2]]
#> [1] "2 4 6"
# name arguments
purrr::pmap(iter_tibble, function(a1 = a, b1 = b, c1 = c) {
paste(a1, b1, c1)
})
#> [[1]]
#> [1] "1 3 7"
#>
#> [[2]]
#> [1] "2 4 6"
# name arguments and switch order
purrr::pmap(iter_tibble, function(b1 = b, c1 = c, asterix = a) {
paste(b1, asterix, c1)
})
#> [[1]]
#> [1] "3 1 7"
#>
#> [[2]]
#> [1] "4 2 6"
# but when using a different initial letter
# ERROR
purrr::pmap(iter_tibble,
purrr::safely(
function(b1 = b, c1 = c, obelix = a) {
paste(b1, obelix, c1)
}
))[1]
#> [[1]]
#> [[1]]$result
#> NULL
#>
#> [[1]]$error
#> <simpleError in .f(...): unused argument (a = 1)>
这种行为与在常规 R 函数中调用参数的方式有很大不同,在常规 R 函数中可以使用缩写(但不好的做法)但不能进行扩展。
# regular function usage -----------------------------------------------------
# abbrevate arguments - no problem
sample(1:4, s = 5, repla = TRUE)
#> [1] 1 3 4 3 1
# extend arguments? nope
sample(1:4, size = 5, replaceeeee = TRUE)
#> Error in sample(1:4, size = 5, replaceeeee = TRUE): unused argument (replaceeeee = TRUE)
我猜答案是 pmap
对 C 的调用而不是 R 中发生的事情。
这里的误解是您的第三个和第四个选项没有“命名参数”,而是默认参数值。您正在为 pmap
的 .f
参数提供函数定义,而不是函数调用。
pmap
以与基本 R 相同的方式进行部分参数匹配。打开 options(warnPartialMatchArgs = TRUE)
可能会使这一点更清楚。在这里,我将以您的第三个示例为例,分解函数定义以使发生的事情更清楚:
iter_tibble <- tibble::tibble(
a = 1:2,
b = 3:4,
c = 7:6
)
f3 <- function(a1 = a, b1 = b, c1 = c) {
paste(a1, b1, c1)
}
purrr::pmap(iter_tibble, f3)
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'a' to 'a1'
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'b' to 'b1'
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'c' to 'c1'
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'a' to 'a1'
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'b' to 'b1'
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'c' to 'c1'
#> [[1]]
#> [1] "1 3 7"
#>
#> [[2]]
#> [1] "2 4 6"
这与您描述的常规 R 函数的情况完全相同,其中提供的命名参数可以是函数参数的缩写。换句话说,对于table的第一行,pmap
基本上构造了调用f3(a = 1, b = 3, c = 7)
。 a
、b
和 c
来自列名,值来自行。
尝试评估此调用时,我们看到函数 f3
没有参数 a
,但它有参数 a1
。因此 调用 中的 命名参数 a = 1
与 函数定义中的 a1
部分匹配。这就是部分匹配警告在输出中描述的内容。没有“扩展”正在发生。参数 a1
的默认值为 a
这一事实与此处完全无关。
如果你想调用一个函数并将 tibble 中的值传递给不同命名的参数,使用它周围的包装器来更改接口。您可以使用单独的命名函数或(这很常见)使用 ~
匿名函数语法来执行此操作。使用您的第 5 个示例:
iter_tibble <- tibble::tibble(
a = 1:2,
b = 3:4,
c = 7:6
)
f5 <- function(b1, obelix, c1) {
paste(b1, obelix, c1)
}
f5_wrapper <- function(a, b, c) {
f5(b1 = b, obelix = a, c1 = c)
}
purrr::pmap(iter_tibble, f5_wrapper)
#> [[1]]
#> [1] "3 1 7"
#>
#> [[2]]
#> [1] "4 2 6"
purrr::pmap(iter_tibble, ~ f5(b1 = ..2, obelix = ..1, c1 = ..3))
#> [[1]]
#> [1] "3 1 7"
#>
#> [[2]]
#> [1] "4 2 6"
使用 *apply
或 purrr
进行函数式编程是我的生计,但我不明白 pmap
如何处理函数参数。
- 我可以使用它们对应的变量名(给定一个数据框作为输入)而不必担心顺序。
- 我可以更具体一点,将它们用作命名参数
- 我可以使用相似的参数名称
- 然而我不能使用完全不同的名字。
我一直在寻找多个类似的问题,但未能找到合适的答案
A) 发生了什么以及
B) 如何使用任意函数参数名称?
# dummy data -----------------------------------------------------------------
(iter_tibble <- tibble::tibble(a = 1:2,
b = 3:4,
c = 7:6))
#> # A tibble: 2 x 3
#> a b c
#> <int> <int> <int>
#> 1 1 3 7
#> 2 2 4 6
# pmap it --------------------------------------------------------------------
# standard way
purrr::pmap(iter_tibble, function(a, b, c) {
paste(a, b, c)
})
#> [[1]]
#> [1] "1 3 7"
#>
#> [[2]]
#> [1] "2 4 6"
# switch order
# works and a maps to a, b to b etc
purrr::pmap(iter_tibble, function(b, c, a) {
paste(a, b, c)
})
#> [[1]]
#> [1] "1 3 7"
#>
#> [[2]]
#> [1] "2 4 6"
# name arguments
purrr::pmap(iter_tibble, function(a1 = a, b1 = b, c1 = c) {
paste(a1, b1, c1)
})
#> [[1]]
#> [1] "1 3 7"
#>
#> [[2]]
#> [1] "2 4 6"
# name arguments and switch order
purrr::pmap(iter_tibble, function(b1 = b, c1 = c, asterix = a) {
paste(b1, asterix, c1)
})
#> [[1]]
#> [1] "3 1 7"
#>
#> [[2]]
#> [1] "4 2 6"
# but when using a different initial letter
# ERROR
purrr::pmap(iter_tibble,
purrr::safely(
function(b1 = b, c1 = c, obelix = a) {
paste(b1, obelix, c1)
}
))[1]
#> [[1]]
#> [[1]]$result
#> NULL
#>
#> [[1]]$error
#> <simpleError in .f(...): unused argument (a = 1)>
这种行为与在常规 R 函数中调用参数的方式有很大不同,在常规 R 函数中可以使用缩写(但不好的做法)但不能进行扩展。
# regular function usage -----------------------------------------------------
# abbrevate arguments - no problem
sample(1:4, s = 5, repla = TRUE)
#> [1] 1 3 4 3 1
# extend arguments? nope
sample(1:4, size = 5, replaceeeee = TRUE)
#> Error in sample(1:4, size = 5, replaceeeee = TRUE): unused argument (replaceeeee = TRUE)
我猜答案是 pmap
对 C 的调用而不是 R 中发生的事情。
这里的误解是您的第三个和第四个选项没有“命名参数”,而是默认参数值。您正在为 pmap
的 .f
参数提供函数定义,而不是函数调用。
pmap
以与基本 R 相同的方式进行部分参数匹配。打开 options(warnPartialMatchArgs = TRUE)
可能会使这一点更清楚。在这里,我将以您的第三个示例为例,分解函数定义以使发生的事情更清楚:
iter_tibble <- tibble::tibble(
a = 1:2,
b = 3:4,
c = 7:6
)
f3 <- function(a1 = a, b1 = b, c1 = c) {
paste(a1, b1, c1)
}
purrr::pmap(iter_tibble, f3)
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'a' to 'a1'
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'b' to 'b1'
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'c' to 'c1'
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'a' to 'a1'
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'b' to 'b1'
#> Warning in .f(a = .l[[1L]][[i]], b = .l[[2L]][[i]], c = .l[[3L]][[i]], ...):
#> partial argument match of 'c' to 'c1'
#> [[1]]
#> [1] "1 3 7"
#>
#> [[2]]
#> [1] "2 4 6"
这与您描述的常规 R 函数的情况完全相同,其中提供的命名参数可以是函数参数的缩写。换句话说,对于table的第一行,pmap
基本上构造了调用f3(a = 1, b = 3, c = 7)
。 a
、b
和 c
来自列名,值来自行。
尝试评估此调用时,我们看到函数 f3
没有参数 a
,但它有参数 a1
。因此 调用 中的 命名参数 a = 1
与 函数定义中的 a1
部分匹配。这就是部分匹配警告在输出中描述的内容。没有“扩展”正在发生。参数 a1
的默认值为 a
这一事实与此处完全无关。
如果你想调用一个函数并将 tibble 中的值传递给不同命名的参数,使用它周围的包装器来更改接口。您可以使用单独的命名函数或(这很常见)使用 ~
匿名函数语法来执行此操作。使用您的第 5 个示例:
iter_tibble <- tibble::tibble(
a = 1:2,
b = 3:4,
c = 7:6
)
f5 <- function(b1, obelix, c1) {
paste(b1, obelix, c1)
}
f5_wrapper <- function(a, b, c) {
f5(b1 = b, obelix = a, c1 = c)
}
purrr::pmap(iter_tibble, f5_wrapper)
#> [[1]]
#> [1] "3 1 7"
#>
#> [[2]]
#> [1] "4 2 6"
purrr::pmap(iter_tibble, ~ f5(b1 = ..2, obelix = ..1, c1 = ..3))
#> [[1]]
#> [1] "3 1 7"
#>
#> [[2]]
#> [1] "4 2 6"