为每个用户创建具有多个条件的累积计数器变量
Create cumulative counter variable per-user, with multiple conditions
我需要根据其他三个变量创建一个计数器变量。
这是本题的扩展题。
考虑多个消费者在亚马逊下订单的情况。我想统计每个用户的订单成功次数。如果下单成功,计数器变量self加一;如果下单失败,计数器保持不变。显然,计数器变量将取决于时间、订单状态和用户。
请考虑当t相同但订单状态不同时的场景,这并不意味着该行重复,它有其他列不同。
DT <- data.table(time=c(1,2,2,2,1,1,2,3,1,1),user=c(1,1,1,1,2,3,3,3,4,4), order_status=c('f','f','t','t','f','f','t','t','t','t'))
DT
期望的计数器输出如下。 'output' 是计数器变量。
time user order_status output
1: 1 1 f 0
2: 2 1 f 0
3: 2 1 t 1
4: 2 1 t 1
5: 1 2 f 0
6: 1 3 f 0
7: 2 3 t 1
8: 3 3 t 2
9: 1 4 t 1
10: 1 4 t 1
最易读的方式可能是子查询。
library(data.table)
library(dplyr)
DT <- data.table(time=c(1,2,2,2,1,1,2,3,1,1),user=c(1,1,1,1,2,3,3,3,4,4), order_status=c('f','f','t','t','f','f','t','t','t','t'))
DT %>% left_join(
DT %>%
filter(order_status == "t") %>%
group_by(user, time) %>%
summarise() %>%
arrange(time) %>%
mutate(output = row_number()),
by = c("user", "time")) %>%
mutate(output = ifelse(is.na(output), 0, output))
注意使用 tidyr
你可以用 replace_na(list(output = 0))
替换最后一个 mutate
。
使用 data.table
的简单方法是:
DT[,output := cumsum(order_status=="t" & !duplicated(cbind(time,user,order_status)))
,by=.(user)]
time user order_status output
1: 1 1 f 0
2: 2 1 f 0
3: 2 1 t 1
4: 2 1 t 1
5: 1 2 f 0
6: 1 3 f 0
7: 2 3 t 1
8: 3 3 t 2
9: 1 4 t 1
10: 1 4 t 1
这种方法基本上会为任何 "f" 值填充最后一个 "t" 值。如果你想让所有 "f" 的值都为 0,那也很简单 - 只需将 by=...
更改为 by=.(user,order_status)
.
这里的主要挑战是将 time, user, order_status=='t'
的每个组合的 第一次出现 设置为 1。然后它是按 user
分组的简单累加和.
这里有两种使用 data.table
完成此操作的方法:
方法一:
DT[, id := 0L
][order_status == "t", id := c(1L, rep(0L, .N-1L)), by=names(DT)
][, id := cumsum(id), by=user]
此处的第二行仅在 order_status == "t"
.
时标记 1
的第一次出现
我的大量注释的生产代码看起来像这样:
DT[, id := 0L # set entire id col to 0
][order_status == "t", # then, where order status is true
id := c(1L, rep(0L, .N-1L)), # set (or update) first value to 1
by = names(DT) # for every time,user,order_status
][, id := cumsum(id), # then, get cumulative sum of id
by = user] # for every user
方法 2: 使用 data.table 的 join+update:
DT[, id := 0L
][DT, id := as.integer(order_status == "t"), mult="first", on=names(DT)
][, id := cumsum(id), by=user]
此处的第二步与方法 1 中的操作相同,但它直接识别第一次出现并将其更新为 1
if order_status == "t"
通过对基于连接的子集执行更新。您可以将里面的 DT
替换为 unique(DT)
以去除冗余。
如果必须的话,我会说第一种方法更有效,因为为每个组创建 rep()
应该非常快,而不是加入+更新。但我发现第二种方法更容易识别 实际操作 是什么,我认为如果您在几周后查看您的代码,这更重要。
我需要根据其他三个变量创建一个计数器变量。
这是本题的扩展题。
请考虑当t相同但订单状态不同时的场景,这并不意味着该行重复,它有其他列不同。
DT <- data.table(time=c(1,2,2,2,1,1,2,3,1,1),user=c(1,1,1,1,2,3,3,3,4,4), order_status=c('f','f','t','t','f','f','t','t','t','t'))
DT
期望的计数器输出如下。 'output' 是计数器变量。
time user order_status output
1: 1 1 f 0
2: 2 1 f 0
3: 2 1 t 1
4: 2 1 t 1
5: 1 2 f 0
6: 1 3 f 0
7: 2 3 t 1
8: 3 3 t 2
9: 1 4 t 1
10: 1 4 t 1
最易读的方式可能是子查询。
library(data.table)
library(dplyr)
DT <- data.table(time=c(1,2,2,2,1,1,2,3,1,1),user=c(1,1,1,1,2,3,3,3,4,4), order_status=c('f','f','t','t','f','f','t','t','t','t'))
DT %>% left_join(
DT %>%
filter(order_status == "t") %>%
group_by(user, time) %>%
summarise() %>%
arrange(time) %>%
mutate(output = row_number()),
by = c("user", "time")) %>%
mutate(output = ifelse(is.na(output), 0, output))
注意使用 tidyr
你可以用 replace_na(list(output = 0))
替换最后一个 mutate
。
使用 data.table
的简单方法是:
DT[,output := cumsum(order_status=="t" & !duplicated(cbind(time,user,order_status)))
,by=.(user)]
time user order_status output
1: 1 1 f 0
2: 2 1 f 0
3: 2 1 t 1
4: 2 1 t 1
5: 1 2 f 0
6: 1 3 f 0
7: 2 3 t 1
8: 3 3 t 2
9: 1 4 t 1
10: 1 4 t 1
这种方法基本上会为任何 "f" 值填充最后一个 "t" 值。如果你想让所有 "f" 的值都为 0,那也很简单 - 只需将 by=...
更改为 by=.(user,order_status)
.
这里的主要挑战是将 time, user, order_status=='t'
的每个组合的 第一次出现 设置为 1。然后它是按 user
分组的简单累加和.
这里有两种使用 data.table
完成此操作的方法:
方法一:
DT[, id := 0L
][order_status == "t", id := c(1L, rep(0L, .N-1L)), by=names(DT)
][, id := cumsum(id), by=user]
此处的第二行仅在 order_status == "t"
.
1
的第一次出现
我的大量注释的生产代码看起来像这样:
DT[, id := 0L # set entire id col to 0
][order_status == "t", # then, where order status is true
id := c(1L, rep(0L, .N-1L)), # set (or update) first value to 1
by = names(DT) # for every time,user,order_status
][, id := cumsum(id), # then, get cumulative sum of id
by = user] # for every user
方法 2: 使用 data.table 的 join+update:
DT[, id := 0L
][DT, id := as.integer(order_status == "t"), mult="first", on=names(DT)
][, id := cumsum(id), by=user]
此处的第二步与方法 1 中的操作相同,但它直接识别第一次出现并将其更新为 1
if order_status == "t"
通过对基于连接的子集执行更新。您可以将里面的 DT
替换为 unique(DT)
以去除冗余。
如果必须的话,我会说第一种方法更有效,因为为每个组创建 rep()
应该非常快,而不是加入+更新。但我发现第二种方法更容易识别 实际操作 是什么,我认为如果您在几周后查看您的代码,这更重要。