这个问题是对我发布的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
完全!
谢谢!
答案 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