【问题标题】:map_dbl returns a numeric matrixmap_dbl 返回一个数值矩阵
【发布时间】:2021-02-05 04:04:42
【问题描述】:

这是我遇到的示例代码。

我定义了一个函数。输入一个数字x,输出一个向量vec

fct <- function(x){
  vec = vector("numeric",3)
  vec = c(1, x, x^2)
  return(vec)
}

我有一个序列a = 1:10,然后我希望在a 上应用fct。理想情况下,它应该返回一个数字矩阵。我知道使用sapply(a,fct) 可以达到目的。但我很好奇如何使用map_*

我试过map(a,fct)。它返回一个列表。

我试过map_dbl(a,fct)。它返回了一个错误

Error: Result 1 must be a single double, not a double vector of length 3
Run `rlang::last_error()` to see where the error occurred.

那么如何使用map_* 来做到这一点?

【问题讨论】:

    标签: r tidyverse purrr


    【解决方案1】:

    map_* 函数不容易做到这一点。我建议使用效果最好的工具,并采用已知的格式/结构。

    如果您使用 map_dbl 的动机是类型安全(purrr::map* 函数的优势),那么您可以使用基本 R 实现相同的安全性:

    vapply(a, fct, numeric(3))
    #      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
    # [1,]    1    1    1    1    1    1    1    1    1     1
    # [2,]    1    2    3    4    5    6    7    8    9    10
    # [3,]    1    4    9   16   25   36   49   64   81   100
    

    当然,vapply 要求您先验地知道返回值的类和长度,但如果这不是问题,那么您就很好。此外,它通常非常快:

    a <- 1:10
    bench::mark(vapply(a, fct, numeric(3)), lapply(a, fct), sapply(a, fct), map(a, fct), check = FALSE)
    # # A tibble: 4 x 13
    #   expression                      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory                 time              gc                   
    #   <bch:expr>                 <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list>                 <list>            <list>               
    # 1 vapply(a, fct, numeric(3))   12.3us   18.1us    44793.      288B     0    10000     0      223ms <NULL> <Rprofmem[,3] [1 x 3]> <bch:tm [10,000]> <tibble [10,000 x 3]>
    # 2 lapply(a, fct)               11.6us   16.2us    52398.        0B     5.24  9999     1      191ms <NULL> <Rprofmem[,3] [0 x 3]> <bch:tm [10,000]> <tibble [10,000 x 3]>
    # 3 sapply(a, fct)               24.8us     36us    22943.      576B     2.29  9999     1      436ms <NULL> <Rprofmem[,3] [2 x 3]> <bch:tm [10,000]> <tibble [10,000 x 3]>
    # 4 map(a, fct)                  24.2us   32.4us    24356.        0B     2.44  9999     1      411ms <NULL> <Rprofmem[,3] [0 x 3]> <bch:tm [10,000]> <tibble [10,000 x 3]>
    

    (请注意,随着a 长度的增加,性能差异几乎会消失。)

    但是回到map*,你可以使用purrr,只需要一个小的外部函数,

    invoke(cbind, map(a, fct))
    #      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
    # [1,]    1    1    1    1    1    1    1    1    1     1
    # [2,]    1    2    3    4    5    6    7    8    9    10
    # [3,]    1    4    9   16   25   36   49   64   81   100
    

    而且它的性能虽然比较慢,但也不会慢很多,而且数据越大,差异就越小。

    a <- 1:10
    bench::mark(vapply(a, fct, numeric(3)), sapply(a, fct), do.call(cbind, lapply(a, fct)), do.call(cbind, map(a, fct)), invoke(cbind, map(a, fct)), matrix(flatten_dbl(map(a, fct)), ncol = length(a)))
    # # A tibble: 6 x 13
    #   expression                                              min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result             memory              time            gc                
    #   <bch:expr>                                         <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>             <list>              <list>          <list>            
    # 1 vapply(a, fct, numeric(3))                           12.3us     17us    51210.      288B     5.12  9999     1      195ms <dbl[,10] [3 x 10~ <Rprofmem[,3] [1 x~ <bch:tm [10,00~ <tibble [10,000 x~
    # 2 sapply(a, fct)                                       25.2us   36.4us    22276.      576B     2.23  9999     1      449ms <dbl[,10] [3 x 10~ <Rprofmem[,3] [2 x~ <bch:tm [10,00~ <tibble [10,000 x~
    # 3 do.call(cbind, lapply(a, fct))                       16.2us   22.7us    37430.      288B     3.74  9999     1      267ms <dbl[,10] [3 x 10~ <Rprofmem[,3] [1 x~ <bch:tm [10,00~ <tibble [10,000 x~
    # 4 do.call(cbind, map(a, fct))                          31.8us   43.6us    19856.      288B     4.40  9026     2      455ms <dbl[,10] [3 x 10~ <Rprofmem[,3] [1 x~ <bch:tm [9,028~ <tibble [9,028 x ~
    # 5 invoke(cbind, map(a, fct))                           35.8us   49.6us    17440.      288B     2.07  8408     1      482ms <dbl[,10] [3 x 10~ <Rprofmem[,3] [1 x~ <bch:tm [8,409~ <tibble [8,409 x ~
    # 6 matrix(flatten_dbl(map(a, fct)), ncol = length(a))   29.6us   45.2us    19309.      864B     2.08  9292     1      481ms <dbl[,10] [3 x 10~ <Rprofmem[,3] [3 x~ <bch:tm [9,293~ <tibble [9,293 x ~
    
    a <- 1:1000
    bench::mark(vapply(a, fct, numeric(3)), sapply(a, fct), do.call(cbind, lapply(a, fct)), do.call(cbind, map(a, fct)), invoke(cbind, map(a, fct)), matrix(flatten_dbl(map(a, fct)), ncol = length(a)))
    # # A tibble: 6 x 13
    #   expression                                              min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result                 memory               time         gc              
    #   <bch:expr>                                         <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>                 <list>               <list>       <list>          
    # 1 vapply(a, fct, numeric(3))                           1.05ms   1.35ms      714.    23.5KB     4.33   330     2    461.9ms <dbl[,1000] [3 x 1,00~ <Rprofmem[,3] [1 x ~ <bch:tm [33~ <tibble [332 x ~
    # 2 sapply(a, fct)                                       1.13ms   1.41ms      655.    70.8KB     4.31   304     2      464ms <dbl[,1000] [3 x 1,00~ <Rprofmem[,3] [6 x ~ <bch:tm [30~ <tibble [306 x ~
    # 3 do.call(cbind, lapply(a, fct))                        1.4ms    1.7ms      543.    31.3KB     4.33   251     2    462.1ms <dbl[,1000] [3 x 1,00~ <Rprofmem[,3] [2 x ~ <bch:tm [25~ <tibble [253 x ~
    # 4 do.call(cbind, map(a, fct))                          1.66ms   1.78ms      479.    31.3KB    10.2     47     1     98.1ms <dbl[,1000] [3 x 1,00~ <Rprofmem[,3] [2 x ~ <bch:tm [48~ <tibble [48 x 3~
    # 5 invoke(cbind, map(a, fct))                            1.3ms   1.73ms      546.    39.2KB     4.30   254     2    464.8ms <dbl[,1000] [3 x 1,00~ <Rprofmem[,3] [3 x ~ <bch:tm [25~ <tibble [256 x ~
    # 6 matrix(flatten_dbl(map(a, fct)), ncol = length(a))   1.13ms   1.49ms      551.    78.3KB     2.07   266     1    482.8ms <dbl[,1000] [3 x 1,00~ <Rprofmem[,3] [4 x ~ <bch:tm [26~ <tibble [267 x ~
    

    (六个命令的输出是一样的。)

    【讨论】:

    • 好的!我会调查vapply
    • 我还发现map_dfc 也可以解决。
    【解决方案2】:

    sapply 返回矩阵的原因是它有一个参数simplify,默认为TRUEsimplify 参数试图将结果简化为向量/矩阵。如果您将参数更改为FALSE,您将获得与lapplymap 相同的输出。

    sapply(a,fct, simplify = FALSE) 
    
    #[[1]]
    #[1] 1 1 1
    
    #[[2]]
    #[1] 1 2 4
    
    #[[3]]
    #[1] 1 3 9
    
    #[[4]]
    #[1]  1  4 16
    #...
    #...
    

    map 将始终返回一个列表,而 map_dbl 期望从函数返回一个数字,在您的情况下,该函数返回 3 个数字。所以一个选择是从map的输出构造一个矩阵。

    library(purrr)
    
    map(a, fct) %>% 
      flatten_dbl() %>% 
      matrix(ncol = length(a))
    
    #     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
    #[1,]    1    1    1    1    1    1    1    1    1     1
    #[2,]    1    2    3    4    5    6    7    8    9    10
    #[3,]    1    4    9   16   25   36   49   64   81   100
    

    【讨论】:

      猜你喜欢
      • 2018-07-31
      • 1970-01-01
      • 1970-01-01
      • 2021-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多