【问题标题】:Conditional row wise aggregation using "or" in data.table在 data.table 中使用“或”进行条件行聚合
【发布时间】:2020-07-01 03:43:31
【问题描述】:

我有一个相当大(300 万行)的 data.table,其中包含来自许多客户的发票和付款,其中每个客户都有许多(唯一的)文档,每个文档都有一个创建日期,以及文档存放日期已付款。如果还没有付款日期,则 payment_date 列会列出 NA。数据看起来像这样:

dt = data.table(
  customer_id = c(rep(1,4), rep(2,4)),
  document_id = c(1:8),
  creation_date = as.Date(c("2005-03-01", "2005-03-03", "2005-03-10", "2005-03-25", "2006-03-01", "2006-03-04", "2006-03-10", "2006-03-12"), "%Y-%m-%d"),
  payment_date = as.Date(c("2005-03-05", "2005-03-07", NA, "2005-03-28", "2006-03-05", NA, "2006-03-15", "2006-03-16"), "%Y-%m-%d"),
  open_docs_10 = c(0,1,2,1,0,1,2,3),
  percentage_open_10 = c(0.0,0.20,0.70,1.0,0.0,0.3,1.0,1.0)
)

每个文档(即每行),我希望(理想情况下)计算两个特征:

1) Open_docs_10,这是当前文档的 customer_id 在 document_id 创建日期之前的某个时间窗口(例如 10 天)内拥有的未付费或“打开”文档的数量。 “开放”是指payment_date为NA,在时间间隔之后或之内,而creation_date在时间间隔之内或之前。

2) Percentage_open_10,这是客户打开文档的时间窗口的天数百分比。文件的数量并不重要。该数字表示“在创建此新文档时,该客户在前 10 天中有 4 天有未结付款”。

对于 1),我尝试了类似的方法:


open_docs_10 = dt[,c("customer_id", "document_id", "creation_date", "payment_date")] %>% 
  .[, open_docs_10 := .[.(customer_id = customer_id, upper = creation_date, lower = creation_date - days(10)), 
                       on = .(customer_id, payment_date >= lower, creation_date > lower), uniqueN(document_id), by=.EACHI
                       ]$V1
    ]

但这还没有给出正确的结果,因为真正/正确的连接条件必须类似于

payment_date >= lower OR upper >= creation_date >= lower

似乎我不能在“on”子句中使用和/或语句。但是如何使用 data.table 来实现呢?

对于 2),我不知道如何解决这个问题。

我并不受任何实际意义上的使用 data.table 的约束;当另一个 R 包会提供一种更智能的处理方式时,也许我正试图以一种困难的方式解决我的问题?任何帮助将不胜感激!

【问题讨论】:

    标签: r dplyr data.table sqldf


    【解决方案1】:

    我认为您在计算 percentage_open_10 时并未始终包含或排除结束日期。如果我们包含结束日期,您可以使用以下内容:

    ndays <- 10L
    setnafill(dt, fill=as.IDate("9999-12-31"), cols="payment_date")
    
    dt[, cd10 := creation_date - ndays + 1L]
    
    dt[, c("open_docs_10", "percentage_open_10") := 
        .SD[.SD, on=.(customer_id, creation_date<=creation_date, payment_date>=cd10), 
            allow.cartesian=TRUE, by=.EACHI, {
            ix <- x.document_id != i.document_id
            p <- 0
            if (any(ix)) {
                lastd <- min(c(i.creation_date, max(x.payment_date[ix]))) 
                firstd <- if (any(ix)) max(c(i.cd10, min(x.creation_date[ix]))) 
                p <- (lastd - firstd + 1) / 10
            }
            .(.N - 1L, p)
        }][, (1L:3L) := NULL]
    ]
    

    输出:

       customer_id document_id creation_date payment_date       cd10 open_docs_10 percentage_open_10
    1:           1           1    2005-03-01   2005-03-05 2005-02-20            0                0.0
    2:           1           2    2005-03-03   2005-03-07 2005-02-22            1                0.3
    3:           1           3    2005-03-10   9999-12-31 2005-03-01            2                0.7
    4:           1           4    2005-03-25   2005-03-28 2005-03-16            1                1.0
    5:           2           5    2006-03-01   2006-03-05 2006-02-20            0                0.0
    6:           2           6    2006-03-04   9999-12-31 2006-02-23            1                0.4
    7:           2           7    2006-03-10   2006-03-15 2006-03-01            2                1.0
    8:           2           8    2006-03-12   2006-03-16 2006-03-03            3                1.0
    

    但是,有 300 万行,我不希望这可以在几秒钟内完成。

    【讨论】:

    • 非常感谢您的帮助!您是正确的,我与在间隔中包含/排除结束/开始日期不一致。我还没有在完整的数据集上运行代码,但是当我这样做时,我会告诉你花了多长时间!
    • 作为一个小更新,在 i7-7700k 上运行完整数据集上的代码大约需要 6-7 分钟。它不是超级快(在实时意义上),但考虑到正在查询的数据量,结果非常令人满意!
    • 您的文档 ID 是否处于运行状态?也许可以节省一两分钟
    • 我刚刚将此建议添加到脚本中,这确实加快了速度。现在缩短到4分钟左右!我使用脚本创建了几个变量,每个变量都回顾了越来越长的时间段,所以这个添加最终总共节省了大约 10 分钟,太棒了!非常感谢!
    猜你喜欢
    • 2013-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-22
    • 2018-06-18
    相关资源
    最近更新 更多