簡體   English   中英

使用分組計算過去和未來特定事件的發生次數

[英]Calculate the number of occurrences of a specific event in the past AND future with groupings

這個問題是我在這里發布的一個問題的修改,我在不同的日子出現了特定類型的事件,但這次它們被分配給多個用戶,例如:

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)取消的訂單的數目,每個客戶過去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提出了非常適合此目的的解決方案

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一個有效的解決方案,即使它根本不基於上述代碼或data.table

謝謝!

這是一種方法:

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

這使用“非對等”連接,采用不等式來定義日期范圍。 有關更多詳細信息,請參閱通過鍵入?data.table找到的文檔頁面。

我可能使這個解決方案有點復雜:

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

我建議使用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

功能文檔中的更多信息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM