аргументы функции purrr pmap

Функциональное программирование с использованием *apply или purrr - это мой хлеб с маслом, но я не понимаю, как pmap обрабатывает аргументы функции.

  1. Я могу использовать соответствующие им имена переменных (учитывая фрейм данных в качестве входных), и мне не нужно беспокоиться о порядке.
  2. Я могу быть более конкретным и использовать их как именованные аргументы
  3. Я могу использовать похожие имена аргументов
  4. Однако я не могу использовать совершенно другие имена.

Я просматривал несколько похожих вопросов, но не смог найти подходящий ответ на

А) что происходит и

Б) как я могу использовать произвольные имена аргументов функций?

# 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, где возможны сокращения (но плохая практика), но расширения невозможны.

# 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.


person mnist    schedule 25.03.2021    source источник


Ответы (1)


Недоразумение состоит в том, что ваши 3-й и 4-й варианты не имеют именованных аргументов, а имеют значения аргументов по умолчанию. Вы предоставляете определение функции для .f аргумента pmap, а не для вызова функции.

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, которые вы описываете, где предоставленные именованные аргументы могут быть сокращениями аргументов функции. Другими словами, для первой строки таблицы pmap в основном создает вызов f3(a = 1, b = 3, c = 7). a, b и c берутся из имен столбцов, значения берутся из строки.

Пытаясь оценить этот вызов, мы видим, что функция f3 не имеет аргумента a, но имеет аргумент a1. Таким образом, именованный аргумент a = 1 в вызове частично соответствует a1 в определении функции. Это то, что описывают предупреждения о частичном совпадении в выходных данных. Никаких расширений не происходит. Тот факт, что аргумент a1 имеет значение по умолчанию a, здесь совершенно не важен.

Если вы хотите вызвать функцию и передать значения в таблице аргументам с разными именами, используйте оболочку вокруг нее, чтобы изменить интерфейс. Вы можете сделать это с помощью отдельной именованной функции или (что очень часто) с помощью синтаксиса анонимной функции ~. Используя ваш 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"
person Calum You    schedule 25.03.2021
comment
Ах, в этом есть смысл! Конечно, речь идет скорее об аргументах по умолчанию и частичном сопоставлении, чем об установке аргументов, поскольку function определяет функцию, но не вызывает ее. Думаю, я бы предпочел использовать функцию-оболочку, поскольку - лично - нотация ..1, ..2 недостаточно ясна. Кроме того, такое представление множества аргументов может сбивать с толку. - person mnist; 25.03.2021