使用分组

时间:2017-01-12 14:43:44

标签: r group-by data.table dplyr

这个问题是对我发布的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),不包括当前 1和

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

完全适合此目的的solution由@ joel.wilson使用data.table提供

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完全!

谢谢!

3 个答案:

答案 0 :(得分:3)

这是一种方式:

nf start -p 3003 -f Procfile.dev

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

工作原理

每个列都可以单独运行,如

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

这可以通过简化来分解为:

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

这使用“非equi”连接,使用不等式来定义日期范围。有关详细信息,请参阅键入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 找到的文档页面。

答案 1 :(得分:1)

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

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天)

答案 2 :(得分:1)

我建议使用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文档中的更多信息。