【问题标题】:purrr::pmap for functions with multiple inputs and multiple return valuespurrr::pmap 用于具有多个输入和多个返回值的函数
【发布时间】:2019-03-23 04:02:48
【问题描述】:

我正在尝试设置具有多个输入和多个返回值的自定义函数,并在数据帧上将此函数与 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

首先,如果我的函数只有一个返回值(在这种情况下为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)
  }

我 cam 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_2purrr::map 映射并将返回列添加到test_data,就像上一步只有一个返回值一样?

我还想先输出结果(使用以下代码),然后是join/bindtest_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

【问题讨论】:

    标签: r function dataframe purrr pmap


    【解决方案1】:

    更好的选择是在函数中将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, .)
    

    【讨论】:

      【解决方案2】:

      一种选择是从函数中返回一个向量

      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
      

      【讨论】:

      • 返回一个向量并转置是一个很好的解决方案。
      【解决方案3】:

      在您的示例中,计算是矢量化的,因此您不需要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))
      

      【讨论】:

      • 您介意澄清“矢量化操作”和“非矢量化操作”之间的区别吗? (如果这是一个愚蠢的问题,请原谅我)
      • 这根本不是一个愚蠢的问题,SO 上的一些答案对此进行了更深入的研究,并且可以得到技术性的:参见stackoverflow.com/a/29006276/2270475。简而言之,在 R 中,您可以在不使用显式循环或 mapapply 系列函数的情况下对向量进行操作,它通常会更快,因为资源消耗部分将被转发到 C,即更高效。
      • 在您的示例中,在 R 中执行 vec1 + vec2 比通过执行 pmap(vec1, vec2,`+`) 在元素对上循环添加它们要快得多,因为在第一种情况下,循环是在 C 中完成的,并且不在R 中,+ 在大卫的回答中属于第 4 类,而pmap 属于第 2 类。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-13
      • 2018-07-07
      • 2014-06-23
      相关资源
      最近更新 更多