【问题标题】:Calculate the number of occurrences of a specific event in the past AND future with groupings使用分组计算过去和未来特定事件的发生次数
【发布时间】:2017-05-27 17:24:00
【问题描述】:

这个问题是对我在here 发布的问题的修改,其中我在不同的日子出现了特定类型,但这次它们被分配给多个用户,例如:

df = data.frame(user_id = c(rep(1:2, each=5)),
            cancelled_order = c(rep(c(0,1,1,0,0), 2)),
            order_date = as.Date(c('2015-01-28', '2015-01-31', '2015-02-08', '2015-02-23',  '2015-03-23',
                                   '2015-01-25', '2015-01-28', '2015-02-06', '2015-02-21',  '2015-03-26')))


user_id cancelled_order order_date
      1               0 2015-01-28
      1               1 2015-01-31
      1               1 2015-02-08
      1               0 2015-02-23
      1               0 2015-03-23
      2               0 2015-01-25
      2               1 2015-01-28
      2               1 2015-02-06
      2               0 2015-02-21
      2               0 2015-03-26

我要计算

1) 每位客户在接下来的 x 天(例如 7、14 天)将要取消的订单数不包括当前一个和 p>

1) 每个客户在过去 x 天(例如 7、14 天)拥有的取消订单数量,不包括当前订单

所需的输出如下所示:

solution
user_id cancelled_order order_date plus14 minus14
      1               0 2015-01-28      2       0
      1               1 2015-01-31      1       0
      1               1 2015-02-08      0       1
      1               0 2015-02-23      0       0
      1               0 2015-03-23      0       0
      2               0 2015-01-25      2       0
      2               1 2015-01-28      1       0
      2               1 2015-02-06      0       1
      2               0 2015-02-21      0       0
      2               0 2015-03-26      0       0

@joel.wilson 使用data.table 提出了非常适合此目的的solution

library(data.table)
vec <- c(14, 30) # Specify desired ranges
setDT(df)[, paste0("x", vec) := 
        lapply(vec, function(i) sum(df$cancelled_order[between(df$order_date, 
                                                 order_date, 
                                                 order_date + i, # this part can be changed to reflect the past date ranges
                                                 incbounds = FALSE)])),
        by = order_date]

但是,它不考虑按user_id 分组。当我尝试通过将此分组添加为by = c("user_id", "order_date")by = list(user_id, order_date) 来修改公式时,它不起作用。似乎这是非常基本的东西,关于如何绕过这个细节的任何提示?

另外,请记住,我正在寻找一个可行的解决方案,即使它根本不是基于上述代码或data.table

谢谢!

【问题讨论】:

  • 嗨,@Frank,谢谢,现在我添加了所需的输出,希望对您有所帮助

标签: r group-by data.table dplyr


【解决方案1】:

这是一种方法:

library(data.table)
orderDT = with(df, data.table(id = user_id, completed = !cancelled_order, d = order_date))

vec = list(minus = 14L, plus = 14L)
orderDT[, c("dplus", "dminus") := .(
    orderDT[!(completed)][orderDT[, .(id, d_plus = d + vec$plus, d_tom = d + 1L)], on=.(id, d <= d_plus, d >= d_tom), .N, by=.EACHI]$N
    ,
    orderDT[!(completed)][orderDT[, .(id, d_minus = d - vec$minus, d_yest = d - 1L)], on=.(id, d >= d_minus, d <= d_yest), .N, by=.EACHI]$N
)]


    id completed          d dplus dminus
 1:  1      TRUE 2015-01-28     2      0
 2:  1     FALSE 2015-01-31     1      0
 3:  1     FALSE 2015-02-08     0      1
 4:  1      TRUE 2015-02-23     0      0
 5:  1      TRUE 2015-03-23     0      0
 6:  2      TRUE 2015-01-25     2      0
 7:  2     FALSE 2015-01-28     1      0
 8:  2     FALSE 2015-02-06     0      1
 9:  2      TRUE 2015-02-21     0      0
10:  2      TRUE 2015-03-26     0      0

(我发现 OP 的列名很麻烦,因此缩短了它们。)


它是如何工作的

每一列都可以单独运行,比如

orderDT[!(completed)][orderDT[, .(id, d_plus = d + vec$plus, d_tom = d + 1L)], on=.(id, d <= d_plus, d >= d_tom), .N, by=.EACHI]$N

这可以通过简化分解为几个步骤:

orderDT[!(completed)][
  orderDT[, .(id, d_plus = d + vec$plus, d_tom = d + 1L)], 
  on=.(id, d <= d_plus, d >= d_tom), 
  .N, 
  by=.EACHI]$N
# original version

orderDT[!(completed)][
  orderDT[, .(id, d_plus = d + vec$plus, d_tom = d + 1L)], 
  on=.(id, d <= d_plus, d >= d_tom), 
  .N, 
  by=.EACHI] 
# don't extract the N column of counts

orderDT[!(completed)][
  orderDT[, .(id, d_plus = d + vec$plus, d_tom = d + 1L)], 
  on=.(id, d <= d_plus, d >= d_tom)]
# don't create the N column of counts

orderDT[!(completed)]
# don't do the join

orderDT[, .(id, d_plus = d + vec$plus, d_tom = d + 1L)]
# see the second table used in the join

这使用“non-equi”连接,采用不等式来定义日期范围。有关详细信息,请参阅通过键入 ?data.table 找到的文档页面。

【讨论】:

  • 嗨@Frank,感谢您的回答!为了简单起见,我尝试在不更改变量名称且未定义 completed = !cancelled 的情况下重现您的解决方案。另外,我明确定义了d_plus = order_date + 14L。结果根本不计算未来的订单,并在计算过去的订单时出错。我可以在哪里发布我用来验证的代码?再次感谢!
  • 也可以使用聊天室:chat.stackoverflow.com/rooms/133025/… 或者如果您更喜欢@Kasia,可以使用 github gist
【解决方案2】:

我可能使这个解决方案有点复杂:

library(dplyr)
library(tidyr)

vec <- c(7,14)

reslist <- lapply(vec, function(x){
df %>% merge(df %>% rename(cancelled_order2 = cancelled_order, order_date2 = order_date)) %>% 
  filter(abs(order_date-order_date2)<=x) %>%
  group_by(user_id, order_date) %>% arrange(order_date2) %>% mutate(cumcancel = cumsum(cancelled_order2)) %>%
  mutate(before = cumcancel - cancelled_order2,
         after = max(cumcancel) - cumcancel) %>%
  filter(order_date == order_date2) %>% 
    select(user_id, cancelled_order, order_date, before, after) %>% 
    mutate(within = x)})

do.call(rbind, reslist) %>% gather(key, value, -user_id, -cancelled_order, -order_date, -within) %>%
  mutate(col = paste0(key,"_",within)) %>% select(-within, - key) %>% spread(col, value) %>% arrange(user_id, order_date)

PS: 我确实在您的输出示例中发现了一个错误(user_id 1,order_date 2015-02-23,minus14 应该是 0,因为 02/08 和 02/23 之间有 15 天)

【讨论】:

  • 谢谢你,@Wie​​tze314,发现错字,我现在已经更正了。让我在更大的数据集上测试您的解决方案...
【解决方案3】:

我推荐使用runner 包。有一个函数runner 在滑动窗口内执行任何 R 函数。

要从当前 7 天窗口和 14 天窗口中获取总和,不包括当前元素,可以对每个窗口使用 sum(x[length(x)])

library(runner)
df %>%
  group_by(user_id) %>%
  mutate(
    minus_7 = runner(cancelled_order, k = 7, idx = order_date, 
                     f = function(x) sum(x[length(x)])),
    minus_14 = runner(cancelled_order, k = 14, idx = order_date, 
                      f = function(x) sum(x[length(x)])))


# A tibble: 10 x 5
# Groups:   user_id [2]
   user_id cancelled_order order_date minus_7 minus_14
     <int>           <dbl> <date>       <dbl>    <dbl>
 1       1               0 2015-01-28       0        0
 2       1               1 2015-01-31       1        1
 3       1               1 2015-02-08       1        1
 4       1               0 2015-02-23       0        0
 5       1               0 2015-03-23       0        0
 6       2               0 2015-01-25       0        0
 7       2               1 2015-01-28       1        1
 8       2               1 2015-02-06       1        1
 9       2               0 2015-02-21       0        0
10       2               0 2015-03-26       0        0

对于未来的元素,这有点棘手,因为它仍然是 7 天的窗口,但滞后了 -6 天(i:(i+6) = 7 天)。同样在这种情况下,每个窗口的第一个元素都被排除在 sum(x[-1]) 之外。

df %>%
  group_by(user_id) %>%
  mutate(
    plus_7   = runner(cancelled_order, k = 7, lag = -6, idx = order_date, 
                      f = function(x) sum(x[-1])),
    plus_14  = runner(cancelled_order, k = 14, lag = -13, idx = order_date, 
                      f = function(x) sum(x[-1]))
  )


# A tibble: 10 x 5
# Groups:   user_id [2]
   user_id cancelled_order order_date plus_7 plus_14
     <int>           <dbl> <date>      <dbl>   <dbl>
 1       1               0 2015-01-28      1       2
 2       1               1 2015-01-31      0       1
 3       1               1 2015-02-08      0       0
 4       1               0 2015-02-23      0       0
 5       1               0 2015-03-23      0       0
 6       2               0 2015-01-25      1       2
 7       2               1 2015-01-28      0       1
 8       2               1 2015-02-06      0       0
 9       2               0 2015-02-21      0       0
10       2               0 2015-03-26      0       0

packagefunction 文档中的更多信息。

【讨论】:

    猜你喜欢
    • 2017-05-26
    • 2016-12-08
    • 2022-08-21
    • 2020-03-28
    • 1970-01-01
    • 2017-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多