data.table用于慢速group_by()和case_when()函数的替代方法

data.table用于慢速group_by()和case_when()函数的替代方法,r,dplyr,data.table,R,Dplyr,Data.table,在我的数据中,我有客户ID、订单日期和一个指示器,如果订单包含一种产品类型。 我想给每个客户一个指标,如果他的第一份订单包含这种类型的产品。但是因为我的数据非常大,所以我不能在任何时候使用分组和案例,因为它太慢了。我想通过使用data.table,我可以大大加快速度 你能给我指出一个解决办法吗?到目前为止,我还没有接触过data.table # generate data id <- round(rnorm(3000, mean = 5000, 400),0) date <- seq

在我的数据中,我有客户ID、订单日期和一个指示器,如果订单包含一种产品类型。 我想给每个客户一个指标,如果他的第一份订单包含这种类型的产品。但是因为我的数据非常大,所以我不能在任何时候使用分组和案例,因为它太慢了。我想通过使用data.table,我可以大大加快速度

你能给我指出一个解决办法吗?到目前为止,我还没有接触过data.table

# generate data
id <- round(rnorm(3000, mean = 5000, 400),0)
date <- seq.Date(as.Date("2018-01-01"), as.Date("2018-12-31"), "day")
date <- sample(date, length(id), replace = TRUE)
indicator <-  rbinom(length(id), 1, 0.5)

df <- data.frame(id, date, indicator)
df$id <- as.factor(df$id)

# Does the first Order contain X?
df <- df %>% group_by(id) %>% mutate(First_Order_contains_x = case_when(
  date == min(date) & indicator == "1" ~ 1,
  TRUE ~ 0
)) %>% ungroup() 

# If first order > 1 ==> all orders get 1 // 
df <- df %>% group_by(id) %>% mutate(Customer_type = case_when(
  sum(First_Order_contains_x) > 0 ~ "Customer with X in first order",
  TRUE ~ "Customer without x in first order"
)) %>% ungroup() 
#生成数据
id 0~“第一批订单中有X的客户”,
TRUE~“第一批订单中没有x的客户”
))%%>%ungroup()

以下是如何更有效地使用dplyr改进现有代码:

lookup = data.frame(First_Order_contains_x = c(TRUE, FALSE), 
                    Customer_Type = c("Customer with X in first order", 
                                      "Customer without x in first order"))

df %>% 
  group_by(id) %>% 
  mutate(First_Order_contains_x = any(as.integer(date == min(date) & indicator == 1))) %>% 
  ungroup() %>% 
  left_join(lookup, by = "First_Order_contains_x")

# A tibble: 3,000 x 5
   id    date       indicator First_Order_contains_x Customer_Type                    
   <fct> <date>         <dbl> <lgl>                  <fct>                            
 1 5056  2018-03-10         1 TRUE                   Customer with X in first order   
 2 5291  2018-12-28         0 FALSE                  Customer without x in first order
 3 5173  2018-04-19         0 FALSE                  Customer without x in first order
 4 5159  2018-11-13         0 TRUE                   Customer with X in first order   
 5 5252  2018-05-30         0 TRUE                   Customer with X in first order   
 6 5200  2018-01-20         0 FALSE                  Customer without x in first order
 7 4578  2018-12-18         1 FALSE                  Customer without x in first order
 8 5308  2018-03-24         1 FALSE                  Customer without x in first order
 9 5234  2018-05-29         1 TRUE                   Customer with X in first order   
10 5760  2018-06-12         1 TRUE                   Customer with X in first order   
# … with 2,990 more rows
lookup=data.frame(第一顺序包含x=c(真、假),
客户类型=c(“第一批订单中有X的客户”,
“第一批订单中没有x的客户”))
df%>%
分组依据(id)%>%
mutate(第一个\u顺序\u包含\u x=any(as.integer(date==min(date)&indicator==1))%>%
解组()%>%
左联接(查找方式为=“第一个订单包含x”)
#一个tibble:3000 x 5
id日期指示器第一批订单包含客户类型
15056 2018-03-10第一批订单中有X的真正客户
2 5291 2018-12-28 0第一批订单中没有x的虚假客户
351732018-04-19 0第一批订单中没有x的虚假客户
4 5159 2018-11-13第一批订单中X的真实客户
5 5252 2018-05-30第一批订单中X的真实客户
6 5200 2018-01-20第一批订单中没有x的虚假客户
7 4578 2018-12-18 1第一批订单中没有x的虚假客户
85308 2018-03-24 1第一批订单中没有x的虚假客户
9 5234 2018-05-29第一批订单中有X的1个真正客户
10 5760 2018-06-12第一批订单中有X的1个真正客户
#…还有2990行
另一种方式:

library(data.table)
DT = data.table(df[, 1:3])

lookupDT = DT[, .(date = min(date)), by=id]
lookupDT[, fx := DT[copy(.SD), on=.(id, date), max(indicator), by=.EACHI]$V1]

DT[, v := "Customer without x in first order"]
DT[lookupDT[fx == 1L], on=.(id), v := "Customer with X in first order"]

# check results
fsetequal(DT[, .(id, v)], data.table(id = df$id, v = df$Customer_type))
# [1] TRUE
如果您想进一步提高速度,请参阅
?IDate


由于。

另一种
数据。表
方法,需要在
.SD
上复制
。首先对数据进行排序,使第一个日期是最早的日期,然后我们可以使用第一个指示器来测试条件。然后,将逻辑转换为整数(
FALSE
->
1
TRUE
->
2
),并使用字符向量映射到所需的输出

library(data.table)
setDT(df)
setorder(df, id, date)
map <- c("Customer without x in first order", "Customer with X in first order")
df[, idx := 1L+any(indicator[1L]==1L), by=.(id)][,
    First_Order_contains_x := map[idx]]

谢谢,我改了!当您有多个选择时,您应该使用
case\u。这里您只有两个,因此您可以尝试在
mutate
中使用
第一个\u Order\u contains\u x=as.integer(date==min(date)&indicator==1”)
。谢谢,我完成了这个任务,时间从739秒缩短到139秒。另一个答案的data.table方法甚至更快(19秒),但结果并不相同。
set.seed(0L)
id <- round(rnorm(3000, mean = 5000, 5),0)
date <- seq.Date(as.Date("2018-01-01"), as.Date("2018-12-31"), "day")
date <- sample(date, length(id), replace = TRUE)
indicator <-  rbinom(length(id), 1, 0.5)

df <- data.frame(id, date, indicator)
df$id <- as.factor(df$id)