我需要根据其他三个变量创建一个计数器变量。
这是此问题的扩展问题。extension question 考虑多个消费者在亚马逊下订单的情况。我想计算每个用户的成功订单时间。如果您已成功下订单,则计数器变量self加1;如果是失败订单,则计数器保持不变。显然,计数器变量将取决于时间,订单状态和用户。
请考虑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
答案 0 :(得分:4)
这里的主要挑战是将time, user, order_status=='t'
的每个组合的第一次出现设置为1.然后它是按user
分组的简单累积总和。
以下是使用data.table
完成此操作的两种方法:
方法1:
DT[, id := 0L
][order_status == "t", id := c(1L, rep(0L, .N-1L)), by=names(DT)
][, id := cumsum(id), by=user]
此处的第二行仅标记1
时order_status == "t"
的第一次出现。
我的一个评论很多的生产代码看起来像这样:
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的加入+更新:
DT[, id := 0L
][DT, id := as.integer(order_status == "t"), mult="first", on=names(DT)
][, id := cumsum(id), by=user]
此处的第2步与方法1中的相同,但它直接标识第一个匹配项,并通过对基于连接的子集执行更新,将1
更新为order_status == "t"
。您可以使用DT
替换内部的unique(DT)
,以便删除冗余。
如果我愿意,我会说第一种方法效率更高,因为为每个组创建rep()
应该非常快,而不是加入+更新。但是我发现第二种方法更容易理解实际操作是什么,如果你在几周之后查看你的代码,我认为这更为重要。
答案 1 :(得分:2)
使用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)
。
答案 2 :(得分: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))
NB使用tidyr
,您可以将mutate
替换为replace_na(list(output = 0))
。