【问题标题】:Rolling sum with a conditioned window带条件窗口的滚动总和
【发布时间】:2017-12-20 09:05:08
【问题描述】:

这是我的数据示例

> d
   customer       date revenue
1:        A 2016-01-01      32
2:        A 2016-01-03      88
3:        A 2016-01-04      80
4:        A 2016-02-01      38
5:        B 2016-01-13      44
6:        B 2016-01-24      11
7:        B 2016-01-25      50
8:        B 2016-02-26      46
> dput(d)
structure(list(customer = c("A", "A", "A", "A", "B", "B", "B", 
"B"), date = structure(c(16801, 16803, 16804, 16832, 16813, 16824, 
16825, 16857), class = "Date"), revenue = c(32, 88, 80, 38, 44, 
11, 50, 46)), .Names = c("customer", "date", "revenue"), row.names = c(NA, 
-8L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x0000000002a60788>)

我想做的是,我想创建一个列,我们称之为roll_sum_3days。 此列是之后发生的收入的滚动总和。窗口大小取决于日期列。在这种情况下,roll_sum_3days 是之后发生的收入总和,不得迟于 3 天。

预期的结果是这样的

   customer       date revenue    roll_sum_3days
1:        A 2016-01-01      32                168
2:        A 2016-01-03      88                 80
3:        A 2016-01-04      80                 0
4:        A 2016-02-01      38                 0
5:        B 2016-01-13      44                 0
6:        B 2016-01-24      11                 96
7:        B 2016-01-25      50                 46
8:        B 2016-01-26      46                 0

【问题讨论】:

  • 不清楚你的意思。也许分解你的总和(为什么是 113?,为什么是 97?...)。另外,窗口大小如何以日期为条件?
  • @Sotos 我的错,我打错了号码。刚刚编辑了帖子。所以具体的例子是,让我们看看客户 A 的第一行,168 来自 88+80,因为 2016-01-03 和 2016-01-04 是在 2016-01-01 之后的 3 天内,而 2016-02-01 是不是
  • @Sotos 也是,我的英语还不是很完美,所以标题建议非常受欢迎:)
  • 那么如果2016-02-01 不在3天之内,那你为什么要在前80天加上呢?
  • 最后三个滚动总和值应为50, 0, 0,因为最后一行的日期不在前两行的三天内。

标签: r dataframe data.table


【解决方案1】:

一个可能的解决方案:

library(lubridate) # for the '%m+%'-function

d[, roll_sum_3d := .SD[.SD[, .(date, date2 = date %m+% days(3), revenue)]
                       , on = .(date > date, date <= date2)
                       ][, sum(revenue, na.rm = TRUE), by = date]$V1
  , by = customer][]

给出:

   customer       date revenue roll_sum_3d
1:        A 2016-01-01      32         168
2:        A 2016-01-03      88          80
3:        A 2016-01-04      80           0
4:        A 2016-02-01      38           0
5:        B 2016-01-13      44           0
6:        B 2016-01-24      11          96
7:        B 2016-01-25      50          46
8:        B 2016-01-26      46           0

这是做什么的:

  • d by customer withby = customer` 分组。
  • 通过引用:=添加roll_sum_3d
  • 通过加入.SDSData 的子集)为每个具有该组的日期窗口的组计算roll_sum_3d.SD[, .(date, date2 = date %m+% days(3), revenue)] 和non-equi join on = .(date &gt; date, date &lt;= date2),汇总每个日期的收入并返还。

基于@Arun 评论的替代方案:

d[, roll_sum_3d := d[d[, .(customer, date, date2 = date %m+% days(3), revenue)]
                     , on = .(customer, date > date, date <= date2)
                     , sum(revenue, na.rm = TRUE), by=.EACHI]$V1][]

【讨论】:

  • 谢谢!看起来这段代码就像我想要的那样工作。我仍然需要一些时间来消化代码。干杯!
  • @SmileyBcc 添加了简短的解释并更新了结果;如果您还有其他问题:请告诉我
  • 似乎有点复杂..您可以在连接中使用customerby=.EACHI,如下所示:d[d, on=.(customer, date &gt; date, date &lt;= date2), sum(revenue), by=.EACHI]$V1。这将返回列 roll_sum_3d。然后,您可以通过引用将其简单地添加到 d
  • @Arun Thx,我还是不太习惯使用by = .EACHI 的概念。起初你的建议没有用,但现在稍微调整一下就可以了:-)。查看更新。
  • 不用担心。它需要将date2 添加到d 才能工作。
【解决方案2】:

您好,我想您的示例中还有另一个错误:第 8 次观察不会像从 2 月开始那样添加到前两次观察的计数中。没关系,如果你想使用 apply()POSIXct() 函数,我有一个解决方案

df <- data.frame(customer = c("A", "A", "A", "A", "B", "B", "B", "B"),
       date = structure(c(16801, 16803, 16804, 16832, 16813, 16824, 
                          16825, 16857), class = "Date"), 
       revenue = c(32, 88, 80, 38, 44, 11, 50, 46))

df$date <- as.POSIXct(df$date)

calc <- function(x){
   date <- as.POSIXct(unlist(x["date"]),origin = "1970-01-01")
   customer <- unlist(x["customer"])
   # There you choose what you want to sum (here conditions are between the day and 3 days later and same customer)
   # 86400 is the number of second in a day!
   output <- sum(df[df$date > date & df$date <= (date+86400*3) & df$customer==customer,"revenue"])
   return(output)
   }

df$sum <- apply(df,1,calc)
# if you want to come back with your date format.
df$date <- as.Date(df$date)
df
  customer       date revenue sum
1        A 2016-01-01      32 168
2        A 2016-01-03      88  80
3        A 2016-01-04      80   0
4        A 2016-02-01      38   0
5        B 2016-01-13      44   0
6        B 2016-01-24      11  50
7        B 2016-01-25      50   0
8        B 2016-02-26      46   0

我无法保留您的日期格式,因为运算符 &gt; 无法使用它。

【讨论】:

  • 感谢您指出错误和您的回答:)
猜你喜欢
  • 1970-01-01
  • 2017-07-12
  • 1970-01-01
  • 2020-05-14
  • 2013-01-21
  • 2020-02-22
  • 2021-05-12
  • 2021-11-05
  • 2011-10-27
相关资源
最近更新 更多