purrr :: pmap для функций с несколькими входами и несколькими возвращаемыми значениями - PullRequest
2 голосов
/ 23 марта 2019

Я пытаюсь настроить пользовательскую функцию с несколькими входами и несколькими возвращаемыми значениями и использую эту функцию с purrr::map для фрейма данных.

мой пример данных:

test_data <- 
      tibble(x1 = 1:10, 
             x2 = 2:11,
             x3 = 3:12,
             x4 = x1 + x2 + x3)

это test_data выглядит так:

# A tibble: 10 x 4
      x1    x2    x3    x4
   <int> <int> <int> <int>
 1     1     2     3     6
 2     2     3     4     9
 3     3     4     5    12
 4     4     5     6    15
 5     5     6     7    18
 6     6     7     8    21
 7     7     8     9    24
 8     8     9    10    27
 9     9    10    11    30
10    10    11    12    33

Во-первых, если моя функция имеет только one возвращаемое значение (output_3 в данном случае):

my_function_1 <- 
  function(var1, var2, var3, var4){
    output_1 <- var1 + var2
    output_2 <- var2 + var3 
    output_3 <- var1 + var2 + var3
    output_4 <- var1 + var2 + var4
    return(output_3)
  }

Я использую pmap эту функцию, используя

my_results <-
  dplyr::as.tbl(test_data) %>% 
  dplyr::mutate(output = purrr::pmap(list(var1 = x1, var2 = x2, var3 = x3, var4 = x4),
                                     my_function_1)) %>% 
  tidyr::unnest()

результаты выглядят так:

 my_results 
# A tibble: 10 x 5
      x1    x2    x3    x4 output
   <int> <int> <int> <int>  <int>
 1     1     2     3     6      6
 2     2     3     4     9      9
 3     3     4     5    12     12
 4     4     5     6    15     15
 5     5     6     7    18     18
 6     6     7     8    21     21
 7     7     8     9    24     24
 8     8     9    10    27     27
 9     9    10    11    30     30
10    10    11    12    33     33

Теперь, если моя функция имеет более одного возвращаемых значений, например

my_function_2 <- 
  function(var1, var2, var3, var4){
    output_1 <- var1 + var2
    output_2 <- var2 + var3 
    output_3 <- var1 + var2 + var3
    output_4 <- var1 + var2 + var4
    return(list(output_1, output_2, output_3, output_4))
  }

Как мне сопоставить это my_function_2 с purrr::map и добавить возвращаемые столбцы к test_data, как и в предыдущем шаге с одним возвращаемым значением?

Я также думаю сначала получить результатов (используя следующий код), а затем join/bind с test_data:

pmap(list(test_data$x1,
              test_data$x2, 
              test_data$x3, 
              test_data$x4),
             my_function_2) %>% 
  flatten()

Но результаты не в нужном формате, например:

[[1]]
[1] 3

[[2]]
[1] 5

[[3]]
[1] 6

[[4]]
[1] 9

[[5]]
[1] 5
... ...

Может кто-нибудь напомнить мне какое-нибудь потенциальное решение для форматирования выходов и объединения с оригинальным test_data?

Ответы [ 3 ]

2 голосов
/ 23 марта 2019

Лучшим вариантом было бы создать значение return как tibble в функции, а затем просто применить pmap

library(purrr)
library(dplyr)
my_function_2 <- 
  function(var1, var2, var3, var4){
    output_1 <- var1 + var2
    output_2 <- var2 + var3 
    output_3 <- var1 + var2 + var3
    output_4 <- var1 + var2 + var4
    tibble::tibble(output_1, output_2, output_3, output_4)
  }

pmap_dfr(list(test_data$x1,
               test_data$x2, 
               test_data$x3, 
               test_data$x4),
              my_function_2) %>%
    bind_cols(test_data, .)
# A tibble: 10 x 8
#      x1    x2    x3    x4 output_1 output_2 output_3 output_4
#   <int> <int> <int> <int>    <int>    <int>    <int>    <int>
# 1     1     2     3     6        3        5        6        9
# 2     2     3     4     9        5        7        9       14
# 3     3     4     5    12        7        9       12       19
# 4     4     5     6    15        9       11       15       24
# 5     5     6     7    18       11       13       18       29
# 6     6     7     8    21       13       15       21       34
# 7     7     8     9    24       15       17       24       39
# 8     8     9    10    27       17       19       27       44
# 9     9    10    11    30       19       21       30       49
#10    10    11    12    33       21       23       33       54

Кроме того, если имена столбцов совпадаютаргументы функции, нам не нужно вызывать каждый столбец отдельно

pmap_dfr(set_names(test_data, paste0("var", 1:4)), my_function_2) %>% 
           bind_cols(test_data, .)
1 голос
/ 24 марта 2019

В вашем примере вычисления векторизованы, поэтому вам не нужно pmap, и мы можем сделать следующее:

library(tidyverse)
test_data %>% 
  mutate(!!!setNames(invoke(my_function_2,unname(.)),paste0("output_",1:4)))
# # A tibble: 10 x 8
#       x1    x2    x3    x4 output_1 output_2 output_3 output_4
#    <int> <int> <int> <int>    <int>    <int>    <int>    <int>
#  1     1     2     3     6        3        5        6        9
#  2     2     3     4     9        5        7        9       14
#  3     3     4     5    12        7        9       12       19
#  4     4     5     6    15        9       11       15       24
#  5     5     6     7    18       11       13       18       29
#  6     6     7     8    21       13       15       21       34
#  7     7     8     9    24       15       17       24       39
#  8     8     9    10    27       17       19       27       44
#  9     9    10    11    30       19       21       30       49
# 10    10    11    12    33       21       23       33       54

Если вы называете свои элементы внутри my_function_2 (самый простой способиспользовать dplyr::lst вместо list еще проще:

my_function_2 <- 
  function(var1, var2, var3, var4){
    output_1 <- var1 + var2
    output_2 <- var2 + var3 
    output_3 <- var1 + var2 + var3
    output_4 <- var1 + var2 + var4
    return(lst(output_1, output_2, output_3, output_4))
  }


test_data %>% 
  mutate(!!!invoke(my_function_2,unname(.)))
# # A tibble: 10 x 8
#       x1    x2    x3    x4 output_1 output_2 output_3 output_4
#    <int> <int> <int> <int>    <int>    <int>    <int>    <int>
#  1     1     2     3     6        3        5        6        9
#  2     2     3     4     9        5        7        9       14
#  3     3     4     5    12        7        9       12       19
#  4     4     5     6    15        9       11       15       24
#  5     5     6     7    18       11       13       18       29
#  6     6     7     8    21       13       15       21       34
#  7     7     8     9    24       15       17       24       39
#  8     8     9    10    27       17       19       27       44
#  9     9    10    11    30       19       21       30       49
# 10    10    11    12    33       21       23       33       54

Или если вам нужно использовать pmap, потому что вы используете невекторизованные операции в вашем реальном случае:

test_data %>% 
  mutate(!!!pmap_dfr(unname(.),my_function_2))
1 голос
/ 23 марта 2019

Один из вариантов - вернуть вектор из функции

my_function_2 <- function(var1, var2, var3, var4){
    output_1 <- var1 + var2
    output_2 <- var2 + var3 
    output_3 <- var1 + var2 + var3
    output_4 <- var1 + var2 + var4
    return(c(output_1, output_2, output_3,  output_4))
}

, а затем используйте pmap_dfc и cbind к исходному фрейму данных

library(tidyverse)

bind_cols(test_data, 
 pmap_dfc(list(test_data$x1,
               test_data$x2, 
               test_data$x3, 
               test_data$x4),
               my_function_2) %>% t() %>% data.frame() %>%
 set_names(paste0("x", 5:8)))


# A tibble: 10 x 8
#      x1    x2    x3    x4    x5    x6    x7    x8
#   <int> <int> <int> <int> <int> <int> <int> <int>
# 1     1     2     3     6     3     5     6     9
# 2     2     3     4     9     5     7     9    14
# 3     3     4     5    12     7     9    12    19
# 4     4     5     6    15     9    11    15    24
# 5     5     6     7    18    11    13    18    29
# 6     6     7     8    21    13    15    21    34
# 7     7     8     9    24    15    17    24    39
# 8     8     9    10    27    17    19    27    44
# 9     9    10    11    30    19    21    30    49
#10    10    11    12    33    21    23    33    54
...