【问题标题】:How to ggplot using dtplyr / data.table without converting it into dataframe or tibble?如何使用 dtplyr / data.table ggplot 而不将其转换为数据框或小标题?
【发布时间】:2021-10-21 11:44:36
【问题描述】:

我第一次尝试 dtplyrdata.table 在我现有的 dplyr 中进行一些时间优化 > 代码。

问题: 如果我使用 data.table / dtplyr 数据对象,则无法使用 ggplot 进行绘图。在绘制 pipe/chain 命令之前,如果我只是将 data.table / dtplyr 对象转换为 tibble 那么它可以与 ggplot 但这比完全使用 data.frame/tibble 需要更多的时间,这将在本文后面显示。

library(tidyverse)
library(dtplyr)
library(data.table)
library(scale)
library(lubridate)
library(bench)

我的代码尝试次数和时间基准:

数据:

data.frame 对象

df_ind_stacked_daily <- read.csv(url("https://raw.githubusercontent.com/johnsnow09/covid19-df_stack-code/main/df_ind_stacked_daily.csv")) %>% 
  mutate(Date = ymd(Date))

data.table 对象

df_ind_stacked_daily2 <- setDT(df_ind_stacked_daily)

使用 data.table/dtplyr 对象绘图:

 df_ind_stacked_daily2 %>% 
    
    filter(Daily_cases_type == "Daily_confirmed",
           Date >= max(Date) - 6 & Date <= max(Date),
           State.UnionTerritory != "India"
    ) %>%
    
    group_by( Date) %>%
    slice_max(order_by = Daily_cases_counts, n = 10) %>% 
    ungroup() %>% 
    # as.tibble() %>%
    
            ggplot(aes(x = Daily_cases_counts, 
                       y = reorder_within(State.UnionTerritory, 
                                          by = Daily_cases_counts, within = Date),
                       fill = State.UnionTerritory)) +
            geom_col(show.legend = FALSE) +
            facet_wrap(~Date, scales = "free_y") +
            
            geom_text(aes(label = Daily_cases_counts), size=3, color="white", 
                       # position = "dodge", 
                      hjust = 1.2) + 
            
            # theme_minimal() +
            theme(legend.position = "none") +
            scale_x_continuous(labels = comma) + # unit_format(scale = 1e-3, unit = "k")
            scale_fill_tableau(palette = "Tableau 20") +
            scale_y_reordered() +
            coord_cartesian(clip = "off")

错误:data 必须是数据框,或由fortify() 强制的其他对象,而不是具有类 dtplyr_step_group/dtplyr_step 的 S3 对象。

P.S - 如果我在上面的代码块中取消注释 as.tibble(),那么 ggplot 可以工作。


代码时间基准:

  1. data.table/dtplyr 对象而不转换为 tibble
library(bench)

bench::mark(
  df_ind_stacked_daily2 %>% 
    
    filter(Daily_cases_type == "Daily_confirmed",
           Date >= max(Date) - 6 & Date <= max(Date),
           State.UnionTerritory != "India"
    ) %>%
    
    group_by( Date) %>%
    
    slice_max(order_by = Daily_cases_counts, n = 10) %>% 
    ungroup() 
    # as.tibble() %>%
)
expression       min    median itr/sec
<S3: bench_expr> 2.45ms 2.75ms 320.3396
  1. 转换为 tibble 后的 data.table/dtplyr 对象
library(bench)

bench::mark(
  df_ind_stacked_daily2 %>% 
    
    filter(Daily_cases_type == "Daily_confirmed",
           Date >= max(Date) - 6 & Date <= max(Date),
           State.UnionTerritory != "India"
    ) %>%
    
    group_by( Date) %>%
    
    slice_max(order_by = Daily_cases_counts, n = 10) %>% 
    ungroup() %>%
    as.tibble()
)
expression       min    median itr/sec
<S3: bench_expr> 12.7ms 14ms   65.41098
  1. data.frametibble 对象
library(bench)

bench::mark(
  df_ind_stacked_daily %>% 
    
    filter(Daily_cases_type == "Daily_confirmed",
           Date >= max(Date) - 6 & Date <= max(Date),
           State.UnionTerritory != "India"
    ) %>%
    
    group_by( Date) %>%
    
    slice_max(order_by = Daily_cases_counts, n = 10) %>% 
    ungroup()
)
expression       min    median itr/sec
<S3: bench_expr> 6.71ms 7.97ms   120.3636

问题:那么我怎样才能使 ggplotdata.table / dtplyr 一起工作而不将其转换为 data.frame /小标题 ??


                               ############################

更新:对答案的回应)

感谢@teunbrand,我主要使用下面的代码并添加了另一个功能,并将其用于 3 个场景:

我创建了两个函数:(1) 执行处理并且不对 tibble 进行强制,(2) 在处理后将其强制为 tibble。

我总共在 3 个场景中运行这些 - (1) data.table,(2) data.table 在处理后转换为 tibble,(3 ) 从头开始​​使用 tibble

# 1. function doesn't convert to tibble 
fun <- function(x) {
  x %>%
    filter(Daily_cases_type == "Daily_confirmed",
           Date >= max(Date) - 6 & Date <= max(Date),
           State.UnionTerritory != "India"
    ) %>%
    
    group_by( Date) %>%
    
    slice_max(order_by = Daily_cases_counts, n = 10) %>% 
    ungroup() #%>%
    # as_tibble() # Always coerce to tibble
}

# 2. function convert it to tibble after all processing
fun_to_tbl <- function(x) {
  x %>%
    filter(Daily_cases_type == "Daily_confirmed",
           Date >= max(Date) - 6 & Date <= max(Date),
           State.UnionTerritory != "India"
    ) %>%
    
    group_by( Date) %>%
    
    slice_max(order_by = Daily_cases_counts, n = 10) %>% 
    ungroup() %>%
    as_tibble() # Always coerce to tibble
}


# Make data larger
dt  <- do.call(rbind, rep(list(as.data.table(df_ind_stacked_daily)), 20))
tbl_df <- do.call(rbind, rep(list(as_tibble(df_ind_stacked_daily)), 20))

# Run data.table on single thread
setDTthreads(1)

由于未知原因,我的基准测试没有同时运行,所以我不得不一个接一个地运行它们。

(bm <- bench::mark(
  dt_res = fun(dt), # bench dt
  min_iterations = 20
))

expression       min    median itr/sec    mem_alloc
<S3: bench_expr> 4.35ms 6.05ms   148.1923 5.12KB
(bm <- bench::mark(
  dt_to_tbl_res = fun_to_tbl(dt), # bench dt converted to tibble at end
  min_iterations = 20
))

expression       min    median itr/sec    mem_alloc
<S3: bench_expr> 65.8ms 72.2ms   12.28566 47.6MB

(bm <- bench::mark(
  tbl_res =  fun(tbl_df),   # bench tbl
  min_iterations = 20
))

expression       min    median itr/sec  mem_alloc
<S3: bench_expr> 55ms 67.8ms   13.70603 47.4MB

目标:我的主要目标是将其整合到具有动态变量选择闪亮应用中,因此想用对其进行优化数据表。 但我想 ggplot 无法使用 s3 objects / data.table

我得到的唯一时差是当我使用 data.table 并将其作为 data.table 传递时,否则没有任何好处。

【问题讨论】:

    标签: r ggplot2 data.table dtplyr


    【解决方案1】:

    这里有一些观察:

    1. 据我了解 dtplyr,它沿着您的管道链累积未评估的操作,它们只是从 dplyr 转换为 data.table 语法。在你意识到你的管道是一个 data.frame、data.table 或 tibble 之前,你的计算机不会运行这些操作。这估计您的第一个基准测试的运行时间。

    2. 因为您使用setDT 将 data.frame 转换为 data.table,所以您作为 data.frame 进行基准测试的对象并不是 data.frame 的基准测试。如果您阅读?setDT 的文档,您会看到该对象被转换在内存中 并且没有复制,这意味着您的df_ind_stacked_daily 也是一个data.table .

    3. data.table 包默认使用多个线程。我们应该防止这种情况进行公平比较。

    4. 您的第一个过滤操作从中等大小的数据(75748 行)变为小数据(252 行)。在您的大部分管道中,您没有处理大量数据,而这正是 data.table 的亮点。

    调整了其中一些,我发现速度上没有区别。

    library(tidyverse)
    library(dtplyr)
    library(data.table)
    library(lubridate)
    library(bench)
    
    df <- read.csv(url("https://raw.githubusercontent.com/johnsnow09/covid19-df_stack-code/main/df_ind_stacked_daily.csv")) %>% 
      mutate(Date = ymd(Date))
    
    fun <- function(x) {
      x %>%
        filter(Daily_cases_type == "Daily_confirmed",
               Date >= max(Date) - 6 & Date <= max(Date),
               State.UnionTerritory != "India"
        ) %>%
        
        group_by( Date) %>%
        
        slice_max(order_by = Daily_cases_counts, n = 10) %>% 
        ungroup() %>%
        as_tibble() # Always coerce to tibble
    }
    
    # Make data larger
    dt  <- do.call(rbind, rep(list(as.data.table(df)), 20))
    tbl <- do.call(rbind, rep(list(as_tibble(df)), 20))
    
    # Run data.table on single thread
    setDTthreads(1)
    
    # Benchmark simultaneously
    (bm <- bench::mark(
      dt = fun(dt),
      tbl = fun(tbl),
      min_iterations = 20
    ))
    #> # A tibble: 2 x 6
    #>   expression      min   median `itr/sec` mem_alloc `gc/sec`
    #>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
    #> 1 dt           41.1ms   42.5ms      23.4    72.2MB     35.2
    #> 2 tbl          40.7ms   41.5ms      24.0      71MB     36.0
    plot(bm)
    

    reprex package (v1.0.0) 于 2021-08-19 创建

    【讨论】:

    • 感谢@teunbrand 付出了如此多的努力、细节和解释。我已经尝试了您的代码并在帖子(UPDATE: Response to Answer) 中添加了另一个functionupdated。基本上我想为我的动态shiny 绘图优化它,但如果ggplot 不接受data.table,那么重点就没有了。
    • ggplot2 确实接受常规 data.table 对象,只是 dtplyr 使用 lazy_dt() 将 data.tables 包装在另一个类中,它不会直接进入 ggplot2 ,因为它具有在强制执行时得到解决的未评估计算。 ?lazy_dt 文档解释了更多。
    • 哦,我明白了,这意味着完全在 data.table 中工作而不是在 lazy_dt() 中为 shiny 绘图对于时间优化非常有成效。
    • 我认为它不会对这种大小的数据产生太大影响:data.table 在数百万行上是benchmarked。您的代码中最耗时的步骤可能是过滤,您可能会在首先执行Daily_cases_type == "Daily_confirmed" 步骤而不是同时执行时获得一些较小的速度优势。
    • 是的,仅此单个图可能不会很多,但我在一个闪亮的页面上有多个图,这些图是从多个数据集创建的。因此,从所有这些中节省的时间可能会减少几秒钟的初始页面加载时间。而且我现在可能不会使用大数据,但将来我应该考虑将这种方法用于更大的数据集。而且我之前忘了说,我非常感谢您在学习这方面的帮助。
    猜你喜欢
    • 2014-09-21
    • 2013-12-19
    • 2018-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-23
    • 1970-01-01
    相关资源
    最近更新 更多