如何通过循环按客户订单规则动态生成summary列?
问题描述
我需要基于order_result和order_date列,为每个customer生成新的summary列(order列代表每个客户的订单数)。已构建数据框df1:
customer <- c("A", "A", "B", "B", "C", "C", "C", "D", "E", "E", "E", "F") order <- c("1", "2", "1", "2", "1", "2", "3", "1", "1", "2", "3", "1") order_result <- c("positive", "lost", "negative", "return", "negative", "lost", "negative", "lost", "lost", "return", "lost", "return") order_date <- c("2018-09-14", "2020-08-20", "2018-09-15", "2019-08-25", "2017-09-12", "2018-09-16", "2020-08-21", "2018-08-10", "2017-09-13", "2018-02-16", "2020-08-21", "2017-05-20") df1 <- data.frame(customer, order, order_result, order_date)
生成summary列的规则
按每个客户的订单日期从早到晚遍历,生成TRUE/FALSE的summary列:
- 客户的第一笔订单
summary始终为TRUE; - 若
order_result为"positive",该客户后续所有订单summary为FALSE; - 若
order_result为"negative",后续订单中order_date与当前订单日期差≤400天则summary为FALSE,>400天则为TRUE; - 若
order_result为"return"或"lost",下一笔订单summary为TRUE; - 需跳至下一个
summary为TRUE的订单作为新索引,重复上述流程,各客户独立处理。
预期结果
summary <- c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE)
遇到的问题
我尝试用dplyr的lag/lead函数未得到正确结果,现在有两个疑问:
- 如何用循环处理不确定数量的行填充FALSE?
- 如何按客户切换索引重复流程?
附我写的伪代码:
if (order == 1) { summary == 'TRUE'} #first order for a customer is always TRUE. if (order_result[row_number()] == 'positive') { #If result is positive summary[row_number()+ length(?)] == FALSE} #After a positive result, all subsequent rows in summary for that customer are FALSE. if (order_result[row_number()] == 'negative') { #If result is negative, there are 2 options based on time difference between that order date and subsequent order dates. if (diff_time(orderdate[row_number()], orderdate[row_number(?)]) <= 400 { summary[row_number()+ length(?)] == FALSE} #After a negative result, all subsequent rows under summary within 400 days of that order for that customer are FALSE else summary[row_number()+ length(?)] == TRUE} #Otherwise all subsequent rows in summary over 400 days for that customer are TRUE if (order_result[row_number()] == 'lost' | order_result[row_number()] == 'return') { summary[row_number() + 1] == TRUE} #If order result is lost or return, the next order for that customer is true under summary.
解决方案
由于规则需要按客户独立遍历、动态跳转到下一个TRUE索引,用循环逐客户处理是最直接的方式。以下是具体实现代码:
library(dplyr) library(lubridate) # 预处理:转日期格式、排序、给每个客户的订单编行号 df1 <- df1 %>% mutate(order_date = ymd(order_date)) %>% arrange(customer, order_date) %>% group_by(customer) %>% mutate(row_idx = row_number()) %>% ungroup() # 初始化summary列 df1$summary <- FALSE # 逐客户处理 customers <- unique(df1$customer) for(cust in customers) { cust_data <- df1 %>% filter(customer == cust) n_rows <- nrow(cust_data) current_idx <- 1 # 从第一行开始遍历 while(current_idx <= n_rows) { # 标记当前索引为TRUE df1$summary[df1$customer == cust & df1$row_idx == current_idx] <- TRUE current_result <- cust_data$order_result[current_idx] current_date <- cust_data$order_date[current_idx] if(current_result == "positive") { # 后续所有行设为FALSE,直接结束该客户处理 df1$summary[df1$customer == cust & df1$row_idx > current_idx] <- FALSE break } else if(current_result == "negative") { # 遍历后续行,按日期差设置状态 for(i in (current_idx + 1):n_rows) { date_diff <- as.numeric(difftime(cust_data$order_date[i], current_date, units = "days")) if(date_diff <= 400) { df1$summary[df1$customer == cust & df1$row_idx == i] <- FALSE } else { # 找到第一个超400天的行,设为下一个索引,跳出内层循环 current_idx <- i break } # 若所有后续行都≤400天,直接终止外层循环 if(i == n_rows) current_idx <- n_rows + 1 } } else if(current_result %in% c("lost", "return")) { # 跳转到下一行作为新索引 current_idx <- current_idx + 1 } } } # 查看最终结果 df1 %>% select(customer, order, order_result, order_date, summary)
代码说明
- 预处理步骤:将
order_date转为日期格式,按客户和日期排序,给每个客户的订单编行号,方便后续索引定位。 - 逐客户循环:对每个客户单独处理,初始化遍历索引为第一行。
- 动态遍历逻辑:
- 每次将当前索引对应的行设为TRUE;
- 根据当前行的
order_result执行对应规则:- 若为
positive,直接标记后续所有行为FALSE,结束该客户的处理; - 若为
negative,遍历后续行,日期差≤400天设为FALSE,找到第一个>400天的行作为新的遍历索引; - 若为
lost/return,直接将遍历索引跳转到下一行;
- 若为
- 终止条件:当遍历索引超过客户的订单行数时,结束该客户的处理。
运行后得到的summary列与预期结果完全一致。
内容的提问来源于stack exchange,提问作者pandas123
相关产品推荐
相关产品推荐

