【问题标题】:Filtering and summarising a dataframe based another search dataframe基于另一个搜索数据框过滤和汇总数据框
【发布时间】:2019-12-27 17:54:24
【问题描述】:

我已经多次发现这种问题,但我无法找到更好(更有效)的方法。

我们有一个数据框df,其值y按日期排序dt和一个或多个类别x。例如,许多工具代码的股票市场数据(“AAPL34”、“MSFT34”等)。

给定另一个数据框search,每行包含来自df$x 的某些category 的日期间隔(min_dtmax_dt),我想在此类别和间隔和输出摘要中过滤df search 的每一行的数量(如平均值、中位数或其他)。

我已经解决了下面的问题,但我觉得它非常很慢(实际数据通常涉及 df 的 10-1 亿行和 search 的数千行)。

library(dplyr)
library(lubridate)

df <- tibble(dt = seq(ymd("2019-01-01"), ymd("2019-12-26"), by = "day"),
             x = rep(letters[1:3], 120),
             y = rnorm(360))

search <- tibble(category = c("a", "a", "b", "c"),
                 min_dt = ymd(c("2019-02-02", "2019-06-06", "2019-08-08", "2019-12-12")),
                 max_dt = ymd(c("2019-04-04", "2019-07-07", "2019-11-11", "2019-12-30")))

# My current solution
filter_summarise <- function(category, min_dt, max_dt) {
  df %>% 
    filter(x == category, dt > min_dt, dt < max_dt) %>% 
    summarise(n = n(), mean_val = mean(y))
}

bind_cols(search, purrr::pmap_dfr(search, filter_summarise))


# A tibble: 4 x 5
  category min_dt     max_dt         n mean_val
  <chr>    <date>     <date>     <int>    <dbl>
1 a        2019-02-02 2019-04-04    20   0.0618
2 a        2019-06-06 2019-07-07    10   0.170 
3 b        2019-08-08 2019-11-11    32  -0.127 
4 c        2019-12-12 2019-12-30     5  -0.345 

我认为问题在于该函数会为每个purrr::map 迭代创建一个副本,但我看不到这样做的出路。任何想法都值得赞赏。

【问题讨论】:

  • 您可以按herehere 等间隔加入。我对data.table不是很精通,但是其他帖子使用它进行类似的操作,例如herehere
  • 另外,使用dt &gt; min_dt 意味着与最小日期相同的日期不会包含在内;这是故意的吗?如果不是,应该是&gt;=
  • @camille 感谢您的回复。我不习惯data.table,但也许这个问题需要它。我会看看这些问题。至于您的第二条评论,我仅在此示例中使用&gt;,尽管是的,使用&gt;= 更有意义。

标签: r dplyr


【解决方案1】:

我使用data.table 重现了相同的结果,但它实际上比 OPs 解决方案执行得更差。留在这里以防它帮助其他人回答:

library(data.table)
setDT(df)
setDT(search)

df[search,
   on = .(dt > min_dt, dt < max_dt, x = category),
   .(min_dt,max_dt,dt,x,y,category)][,list(.N, mean_val = mean(y)),
                                        by = list(min_dt,max_dt,category)]

基准测试:

dt_summ = function(df,search){
  setDT(df)
  setDT(search)

  setkeyv(df,c("dt","y"))

  df[search,
     on = .(dt > min_dt, dt < max_dt, x = category),
     .(min_dt,max_dt,dt,x,y,category)][,
                                          list(.N, mean_val = mean(y)),
                                          by = list(min_dt,max_dt,category)]
}


dplyr_summ = function(df,search){
  bind_cols(search, purrr::pmap_dfr(search, filter_summarise))
}

library(microbenchmark)
microbenchmark(
  dplyr = dplyr_summ(df,search),
  dt = dt_summ(df,search)
)

#Unit: milliseconds
#  expr    min     lq     mean  median     uq     max neval
# dplyr 4.0562 4.4588 5.580925 4.70385 5.0531 65.5202   100
#    dt 6.7754 7.5449 8.246862 7.97395 8.6485 15.8260   100

【讨论】:

  • 我用data.table 1.12.9 dev 测试了您的代码,在 6 核 i7 第 8 代 PC 上运行速度比 dplyr 0.8.99.9000 dev 快 3 倍。
  • 抱歉回复晚了。我刚刚测试了您的data.table 替代方案(适应了我的实际问题),它的运行速度比我的dplyr 代码快大约60 倍(从~40 分钟到40 秒)!我认为运行时差异随着dfsearch 中的行数而增加。太棒了,谢谢你。是时候学习data.table(或用dtplyr“作弊”了)。
  • 唯一的“问题”是.N 的摘要在没有观察时给出 1 而不是 0。这很容易纠正,但我不明白为什么会这样。
  • @Tung 好奇怪。我在家里和工作的电脑上测试过,结果是一样的。我的data.tabledplyr 版本比较旧。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-30
  • 2018-03-08
  • 2023-03-12
  • 2021-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多