在数据表中使用动物园的Rollsum处理时间戳交易

5
这个问题这个问题有些相关,我在计算滚动总和时遇到了困难。与那些问题不同的是,我想尝试使用zoo:rollsum来类比于这里rollapply答案。(但如果有更多data.table的方法,请务必告诉我。)
让我们从一些数据开始:
set.seed(123)
some_dates <- function(){as.Date('1980-01-01') + sort(sample.int(1e4,100))}
d <- data.table(cust_id = c(rep(123,100),rep(456,100)),
                purch_dt = c(some_dates(), some_dates()),
                purch_amt = round(runif(200, 1, 100),2) )

head(d)
#    cust_id   purch_dt purch_amt
# 1:     123 1980-01-08     24.63
# 2:     123 1980-09-03     96.27
# 3:     123 1981-02-24     60.54

我想要对每个客户在每一天的购买金额进行滚动365天求和,即在每个交易日计算。

这里的答案提出以下方法:

首先使用交叉连接(cross join)为所有客户-日期对创建虚拟行,例如:

setkey(d, cust_id, purch_dt)
dummy <- d[ CJ(unique(cust_id), seq(min(purch_dt), max(purch_dt), by='day') ) ]
#    cust_id   purch_dt purch_amt
# 1:     123 1980-01-08     24.63
# 2:     123 1980-01-09        NA
# 3:     123 1980-01-10        NA

目前为止,情况不错(虽然我相信有一种方法可以将该虚拟表格收紧到客户级别的最小/最大购买日期)。

我的问题是如何使用rollsumr计算过去365天的滚动总和。

我尝试过:

dummy[, purch_365 := rollsumr(x=purch_amt, k=365, na.rm=TRUE) , by=cust_id]

但这会创建所有NApurch_365变量,并产生两个警告信息,如下:

Warning messages:
1: In `[.data.table`(dummy, , `:=`(purch_365, rollsumr(x = purch_amt,  :
  Supplied 9550 items to be assigned to group 1 of size 9914 in column 'purch_365' (recycled leaving remainder of 364 items).

我理解364 = k-1,还有2个cust_id的警告。除此之外,我对此一无所知。

# Desired output:
#    cust_id   purch_dt purch_amt purch_365
# 1:     123 1980-01-08     24.63     24.63
# 2:     123 1980-09-03     96.27    120.90
# 3:     123 1981-02-24     60.54    156.81

感谢您先行一步!

你尝试过使用 fill = NA 吗?在 ?rollsum 中有提到。你应该在向量上进行调试,而不是在 data.table 上进行调试,这样会更容易捕捉到问题。 - jangorecki
@jangorecki 谢谢,但我尝试了 fill = NA,有趣的是它停止了警告,但结果是同一列的 NA - C8H10N4O2
2个回答

7

以下是一种方法。首先,添加一列包含您关心的最后日期以及一个索引来跟踪事物:

d[, old.date := purch_dt - 365]
d[, idx := .I]

然后在该日期上进行滚动连接(假定版本为1.9.5+),并提取每个匹配的索引范围(即通过 .EACHI):

res = d[d, .(idx = i.idx, seq = idx:i.idx), by = .EACHI, roll = -Inf,
        on = c(cust_id = 'cust_id', purch_dt = 'old.date')]

最后,通过合适的范围对原始data.table进行子集处理,并计算总和:
d[, purch_365 := d[res$seq, sum(purch_amt), by = res$idx]$V1][]
#     cust_id   purch_dt purch_amt idx   old.date purch_365
#  1:     123 1980-01-08     24.63   1 1979-01-08     24.63
#  2:     123 1980-09-03     96.27   2 1979-09-04    120.90
#  3:     123 1981-02-24     60.54   3 1980-02-25    156.81
#  4:     123 1981-04-01     51.99   4 1980-04-01    208.80
#  5:     123 1981-04-02     40.85   5 1980-04-02    249.65
# ---                                                      
#196:     456 2006-01-29     24.72 196 2005-01-29    187.81
#197:     456 2006-02-15     27.78 197 2005-02-15    215.59
#198:     456 2006-09-22     11.00 198 2005-09-22     74.94
#199:     456 2006-09-27     12.67 199 2005-09-27     87.61
#200:     456 2006-11-18     99.13 200 2005-11-18    186.74

这个可行,谢谢。看起来这个自连接应该比创建更大的交叉连接虚拟表要快,尽管我无法让 zoo::rollsum 方法工作,所以无法进行基准测试。只是好奇——你已经回答了我的一些 data.table 问题,我很想知道你是如何掌握 data.table 的开发版本的——你参与了开发吗?还是你是怎么学习的?我也想做到这一点... - C8H10N4O2
3
我有些参与,但比一两年前要少得多。我认为上面唯一与“dev”相关的部分是“on”参数。如果你想始终处于功能的最前沿,我建议你订阅 Github 渠道并阅读各种更新、建议、讨论等内容。 - eddi
所以我尝试了自己的一个简单想法,但似乎并不起作用,因为即使日期相同,“seq”列仍在增加。有什么建议吗? - SJDS
@SJDS,将上面的代码更改为seq = (idx+1):i.idx不就可以了吗? - eddi
1
@JonatasEduardo 我会从 github vignettes 开始。by 可以接受列名,但更一般地,它只接受值列表,并根据这些值进行分组。 - eddi
显示剩余4条评论

0
这可能是一个瞎猜,因为我不完全理解你的问题,但也许将 purch_amt 中的 NA 值转换为数字 0 可以解决问题?R 可能会将 NA 值相加,导致答案为 NA。
例如,NA + 1 = NA。尝试将这些 NA 值更改为零。

谢谢,我尝试将[is.na(purch_amt), purch_amt := 0]添加到虚拟表的末尾 - 没有帮助。 - C8H10N4O2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接