【问题标题】:Efficient & faster way to merge & group in R在 R 中合并和分组的有效和更快的方法
【发布时间】:2021-03-20 22:53:59
【问题描述】:

我有两个数据集,想要进行 inner join 然后 group 但由于我的数据有 >1 亿行,inner join 是 笛卡尔合并 进一步增加了最终数据集。我想了解如何以一种高效、快捷的方式来做到这一点。我当前的合并代码运行了很长时间。

示例数据集(此示例没有笛卡尔数据,但我的原始数据集有)

library("data.table")
library("sqldf")
library("purrr")

orders <- data.table(date = as.POSIXct(c('2012-08-28','2012-08-29','2012-09-01', '2012-08-30')),
                     first_name = as.character(c('John','George','Henry', 'Markel')),
                     last_name = as.character(c('Doe','Smith','Smith', 'Markel')),
                     qty = c(10,50,6, 0))

dates <- data.table(date = seq(from = as.POSIXct('2012-08-28'),
                               to = as.POSIXct('2012-09-07'), by = 'day'),
                    week = seq(from = 1, to = 11, by = 1))

我拥有的等效 sqldf 代码:这要慢得多

final_data <- sqldf("select first_name,
       last_name,
       week,
       sum(qty) as total_qty 
from orders a inner join dates b
on a.date = b.date
where a.first_name = a.last_name
group by first_name,
         last_name,
         week
having sum(qty) = 0;")

等效的 data.table 代码(必须匹配 sqldf 输出)

final_data_2 <- merge(
    x = orders[ first_name == last_name,]
  , y = dates
  , all = FALSE
  , allow.cartesian = TRUE) %>%
  .[, total_qty := sum(qty), by = .(first_name, last_name, week) ] %>% 
  .[total_qty == 0, .(first_name, last_name, week, total_qty)]

@manoftheshark 的备用代码

orders[dates, on = 'date', allow.cartesian = TRUE][, total_qty := sum(qty), by = .(first_name, last_name, week)][total_qty == 0, .(first_name, last_name, week, total_qty)]

【问题讨论】:

  • 您确定这是笛卡尔合并吗?样本数据似乎没有显示这一点。还不清楚应该如何处理week,因为它不是聚合函数的一部分,也不是分组依据。
  • 是的,我的原始数据是笛卡尔合并,这只是一个例子。 week 是选择的一部分。希望我在这里有意义
  • 如果有2012-09-06, Markel, Markel, 0这样的记录怎么办? first_namelast_name 被分组,qty 被求和,但是week 是如何处理的呢?它会成为一个独特的记录吗?还是应该返回一条记录?
  • @manotheshark 感谢您指出这一点。我已将星期添加到分组依据
  • 在 SO 上发布问题时,我建议删除 %&gt;%. 以使其更易于阅读,并通过减少额外的依赖项使其更易于重现。数据处理管道可以通过链接[操作符来表示。

标签: r postgresql data.table sqldf


【解决方案1】:

不确定这将如何扩展到完整的数据集,但 microbenchmark 的测试数据显示出 15-30% 的改进

orders[dates, on = 'date', allow.cartesian = TRUE][, total_qty := sum(qty), by = .(first_name, last_name, week)][total_qty == 0, .(first_name, last_name, week, total_qty)]

【讨论】:

  • @manotheshark ...只是一个不同的问题..如果“on”变量在订单和日期中有不同的名称(我的意思是顺序名称是“日期”,而日期中的名称是“日期') 。那么如何进行第一步呢?
  • 以下语法应该可以使用orders[dates, on = 'dates==date'],但可能需要颠倒on的顺序
  • 或者使用明确的setkey,不提供on
  • 这个方法运行起来也太费时间了。如果可能,考虑优化 sqldf 代码。
【解决方案2】:

由于数量是非负数,您可以先使用反连接删除所有带有一定数量的 first_name 和 last_name。然后,从dates 表中查找星期。最后,确定 first_name、last_name 和 week 的这些不同组合:

unique(
    orders[!orders[qty>0L], on=.(first_name, last_name)][
        dates, on=.(date), week := week],
    by=c("first_name", "last_name", "week"))

【讨论】:

  • 谢谢!我还必须将订单条件设为 first_name == last_name。所以,我认为可以从 orders[(qty == 0L) & (first_name == last_name)][
  • 名与姓相同?少校少校?哈哈
  • haaa...这只是一个例子
  • week:= week 是做什么的?当我在我的实际数据上尝试它时,它说Error in eval(bysub, x, parent.frame()) : object 'week' not found
猜你喜欢
  • 2020-07-25
  • 1970-01-01
  • 1970-01-01
  • 2021-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多