【问题标题】:How to translate data.table code to collapse如何将 data.table 代码转换为折叠
【发布时间】:2021-06-09 22:12:33
【问题描述】:

我最近阅读了有关 collapse 包的信息,并尝试将以下 data.table 代码转换为 collapse 以查看它在实际示例中是否更快。

这是我的data.table 代码:

library(data.table)
library(nycflights13)

data("flights")
flights_DT <- as.data.table(flights)

val_var <- "arr_delay"
id_var <- "carrier"
by <- c("month", "day")

flights_DT[
  j = list(agg_val_var = sum(abs(get(val_var)), na.rm = TRUE)), 
  keyby = c(id_var, by)
][
  i = order(-agg_val_var), 
  j = list(value_share = cumsum(agg_val_var)/sum(agg_val_var)), 
  keyby = by
][
  j = .SD[2L],
  keyby = by
][
  order(-value_share)
]
#>      month day value_share
#>   1:    10   3   0.5263012
#>   2:     1  24   0.5045664
#>   3:     1  20   0.4885145
#>   4:    10  17   0.4870692
#>   5:     3   6   0.4867606
#>  ---                      
#> 361:     5   4   0.3220295
#> 362:     6  15   0.3205974
#> 363:     1  28   0.3197260
#> 364:    11  25   0.3161550
#> 365:     6  14   0.3128286

reprex package (v1.0.0) 于 2021-03-11 创建

我设法翻译了第一个data.table 电话,但后来遇到了困难。

很高兴看到collapse 将如何用于处理此用例。

【问题讨论】:

    标签: r data.table collapse


    【解决方案1】:

    因此,我首先要注意的是collapse 不是,也可能永远不会是一个成熟的拆分应用组合计算工具,如dplyrdata.table。它的重点不是按组优化执行任意代码表达式,而是通过它提供的广泛的基于 C++ 的统计和数据转换函数来提供高级和高效的分组、加权、时间序列和面板数据计算。我参考了collapsedata.table 上的vignette,以进一步了解这些要点以及集成示例。

    因此,我认为只有将data.table 代码转换为collapse 才有意义,如果(1)你在data.table 中想出了一个神秘的表达式来做一些它不擅长的复杂统计(例如加权聚合、按组计算分位数或模式、滞后/区分不规则面板、分组居中或线性/多项式拟合)(2)您实际上不需要data.table 对象,但更愿意使用向量/ matrices / data.frame's / tibbles (3) 您想编写一个统计程序,并且更喜欢标准评估编程而不是 NS eval 和 data.table 语法或 (4) collapse 对于您的特定应用程序确实更快。

    现在转到您提供的特定代码。它混合了标准和非标准评估(例如通过使用get()),这是collapse 不太擅长的。我会给你 3 个解决方案,从完整的 NS eval 到完整的标准 eval base R 风格编程。

    library(data.table)
    library(nycflights13)
    library(magrittr)
    library(collapse)
    
    data("flights")
    flights_DT <- as.data.table(flights)
    
    # Defining a function for the second aggregation
    myFUN <- function(x) (cumsum(x[1:2])/sum(x))[2L]
    
    # Soluting 1: Non-Standard evaluation
    flights_DT %>%
      fgroup_by(carrier, month, day) %>% 
      fsummarise(agg_val_var = fsum(abs(arr_delay))) %>% 
      roworder(month, day, -agg_val_var, na.last = NA) %>%
      fgroup_by(month, day) %>%
      fsummarise(value_share = myFUN(agg_val_var)) %>% 
      roworder(-value_share)
    #>      month day value_share
    #>   1:    10   3   0.5263012
    #>   2:     1  24   0.5045664
    #>   3:     1  20   0.4885145
    #>   4:    10  17   0.4870692
    #>   5:     3   6   0.4867606
    #>  ---                      
    #> 361:     5   4   0.3220295
    #> 362:     6  15   0.3205974
    #> 363:     1  28   0.3197260
    #> 364:    11  25   0.3161550
    #> 365:     6  14   0.3128286
    

    reprex package (v0.3.0) 于 2021 年 3 月 12 日创建

    注意na.last = NA 的使用实际上消除了agg_val_var 缺失的情况。这里需要这是因为fsum(NA)NA 而不是0sum(NA, na.rm = TRUE)。现在混合示例可能接近您提供的代码:

    val_var <- "arr_delay"
    id_var <- "carrier"
    by <- c("month", "day")
    
    # Solution 2: Hybrid approach with standard eval and magrittr pipes
    flights_DT %>%
      get_vars(c(id_var, val_var, by)) %>%
      ftransformv(val_var, abs) %>% 
      collapv(c(id_var, by), fsum) %>%
      get_vars(c(by, val_var)) %>%
      roworderv(decreasing = c(FALSE, FALSE, TRUE), na.last = NA) %>%
      collapv(by, myFUN) %>%
      roworderv(val_var, decreasing = TRUE) %>%
      frename(replace, names(.) == val_var, "value_share")
    #>      month day value_share
    #>   1:    10   3   0.5263012
    #>   2:     1  24   0.5045664
    #>   3:     1  20   0.4885145
    #>   4:    10  17   0.4870692
    #>   5:     3   6   0.4867606
    #>  ---                      
    #> 361:     5   4   0.3220295
    #> 362:     6  15   0.3205974
    #> 363:     1  28   0.3197260
    #> 364:    11  25   0.3161550
    #> 365:     6  14   0.3128286
    

    reprex package (v0.3.0) 于 2021-03-12 创建

    请注意,我在末尾使用了frename 为结果列提供了您想要的名称,因为您不能在collapse 的同一函数中混合标准和非标准eval。最后,collapse 的一大优势是您可以将其用于非常底层的编程:

    # Solution 3: Programming
    data <- get_vars(flights_DT, c(id_var, val_var, by))
    data[[val_var]] <- abs(.subset2(data, val_var))
    g <- GRP(data, c(id_var, by))
    data <- add_vars(get_vars(g$groups, by), 
                     fsum(get_vars(data, val_var), g, use.g.names = FALSE))
    data <- roworderv(data, decreasing = c(FALSE, FALSE, TRUE), na.last = NA)
    g <- GRP(data, by)
    columns
    data <- add_vars(g$groups, list(value_share = BY(.subset2(data, val_var), g, myFUN, use.g.names = FALSE)))
    data <- roworderv(data, "value_share", decreasing = TRUE)
    data
    #>      month day value_share
    #>   1:    10   3   0.5263012
    #>   2:     1  24   0.5045664
    #>   3:     1  20   0.4885145
    #>   4:    10  17   0.4870692
    #>   5:     3   6   0.4867606
    #>  ---                      
    #> 361:     5   4   0.3220295
    #> 362:     6  15   0.3205974
    #> 363:     1  28   0.3197260
    #> 364:    11  25   0.3161550
    #> 365:     6  14   0.3128286
    

    reprex package (v0.3.0) 于 2021-03-12 创建

    请参阅 programmingcollapse 上的博客文章,了解有关这如何有助于统计代码开发的更有趣的示例。

    现在进行评估,我将这些解决方案包装在函数中,其中 DT() 是您提供的 data.table 代码,在 Windows 机器上使用 2 个线程运行。这会检查相等性:

    all_obj_equal(DT(), clp_NSE(), clp_Hybrid(), clp_Prog())
    #> TRUE
    
    

    现在是基准:

    library(microbenchmark)
    microbenchmark(DT(), clp_NSE(), clp_Hybrid(), clp_Prog())
    #> Unit: milliseconds
    #>          expr      min       lq     mean   median       uq       max neval cld
    #>          DT() 85.81079 87.80887 91.82032 89.47025 92.54601 132.26073   100   b
    #>     clp_NSE() 13.47535 14.15744 15.99264 14.80606 16.29140  28.16895   100  a 
    #>  clp_Hybrid() 13.79843 14.23508 16.61606 15.00196 16.83604  32.94648   100  a 
    #>    clp_Prog() 13.71320 14.17283 16.16281 14.94395 16.16935  39.24706   100  a
    
    

    如果您关心这些毫秒,请随意优化,但对于 340,000 obs,所有解决方案都非常快。

    【讨论】:

    • 感谢非常全面的回答,尤其是开头的解释性评论。基于此,我认为collapse 不适合我的用例。但我会记住这一点,希望以后能试一试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-26
    • 2017-03-10
    • 1970-01-01
    • 1970-01-01
    • 2018-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多