【问题标题】:How to speed up `expand.grid()` in R?如何在 R 中加速`expand.grid()`?
【发布时间】:2021-08-22 09:29:38
【问题描述】:

我正在尝试使用两个向量之间的所有可能组合来加速表的创建。当我们使用expand.grid() 时,我们可以从base R 获得此功能。但是,我想知道我们是否可以使用 {collapse} 包中的工具更快地完成相同的结果。

有一个 StackOverflow 线程关于此主题 here。但是,即使我们采用那里提供的最快解决方案,在以下情况下它也是最慢的。虽然tidyr::expand_grid() 比base R 更快,但我仍然希望利用collapse 包我们可以获得更快的处理时间。

#library(collapse)
#library(tidyr)
library(babynames)

year  <- collapse::funique(babynames$year, sort = TRUE)
names <- collapse::funique(babynames$name)

expand.grid.jc <- function(seq1,seq2) { ## from https://stackoverflow.com/a/10407457/6105259
  as.data.frame(cbind(Var1 = rep.int(seq1, length(seq2)), 
                      Var2 = rep.int(seq2, rep.int(length(seq1),length(seq2)))))
}

my_benchmarking <- 
  bench::mark(base = expand.grid(year, names),
              jc = expand.grid.jc(year, names),
              tidyr = tidyr::expand_grid(year, names), check = FALSE, iterations = 10)
#> Warning: Some expressions had a GC in every iteration; so filtering is disabled.

my_benchmarking
#> # A tibble: 3 x 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 base        965.3ms    1.06s    0.938      701MB    2.35 
#> 2 jc            13.1s   13.39s    0.0747     820MB    0.120
#> 3 tidyr       541.2ms 656.71ms    1.55       316MB    1.24

reprex package (v2.0.0) 于 2021 年 8 月 22 日创建

很高兴了解这项任务是否可以更快地计算。

【问题讨论】:

  • 永远不要像.data.frame(cbind(...))那样做。使用 data.frame(...)。
  • 不知道collapse有没有类似的功能,但是你可以试试data.table::CJ也就是fast。此外,使用更大的数据(例如V1 = 1:1e4V2 = 1:1e4)发现base::expand.gridtidyr::expand_grid 快(亚秒级数据的基准测试很少相关)。
  • @Henrik,谢谢。我对您对基准测试时使用的数据类型的评论很感兴趣。我真实情况下的数据更类似于babynames而不是1:1e4,因为我有字符数据而不是整数。
  • @Emman 感谢您的反馈。我理解我的评论是否会让人觉得不清楚/不相关。我的观点(如果有的话)更多的是关于用于计时的两个输入向量的组合总数,而不是字符与整数。很可能是我使用的尺寸不太能代表您的数据。干杯。

标签: r performance


【解决方案1】:

你可以试试data.table::CJ函数。

bench::mark(base = expand.grid(year, names),
            jc = expand.grid.jc(year, names),
            tidyr1 = tidyr::expand_grid(year, names), 
            tidyr2 = tidyr::crossing(year, names), 
            dt = data.table::CJ(year, names),
            check = FALSE, iterations = 10)

#  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> <lis>
#1 base       635.48ms 715.02ms     1.25      699MB    2.00     10    16      8.02s <NULL> <Rprof… <benc… <tib…
#2 jc            5.66s    5.76s     0.172     820MB    0.275    10    16     58.13s <NULL> <Rprof… <benc… <tib…
#3 tidyr1     195.03ms 268.97ms     4.01      308MB    2.00     10     5       2.5s <NULL> <Rprof… <benc… <tib…
#4 tidyr2     590.91ms 748.35ms     1.31      312MB    0.656    10     5      7.62s <NULL> <Rprof… <benc… <tib…
#5 dt          318.1ms 384.21ms     2.47      206MB    0.986    10     4      4.06s <NULL> <Rprof… <benc… <tib…

PS - 还包括 tidyr::crossing 用于比较,因为它做同样的事情。

【讨论】:

    猜你喜欢
    • 2014-02-12
    • 1970-01-01
    • 2012-08-21
    • 2015-07-17
    • 1970-01-01
    • 2018-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多