R语言使用lag函数时,如何跳过与当前行日期相同的历史行?
问题
现有包含Person、NoShow和date列的数据框:
Person NoShow date 1 334 1 2019-07-07 2 334 1 2019-10-11 3 334 1 2020-07-07 4 334 1 2021-01-04 5 999 1 2021-06-03 6 999 1 2022-04-17 7 999 1 2022-05-03 8 999 0 2022-05-03
创建该数据框的代码:
df <- data.frame(Person = c('334','334','334','334','999','999','999','999'), NoShow = c(1,1,1,1,1,1,1,0), date = c('2019-07-07','2019-10-11','2020-07-07','2021-01-04','2021-06-03','2022-04-17','2022-05-03','2022-05-03') )
需要生成三个新列:
prior_noshow:用户是否有过早于当前行日期的历史失约记录prior_noshow_f:用户早于当前行日期的历史失约次数last_noshow:用户最近一次早于当前行日期的失约日期
原代码运行后,第8行的prior_noshow_f为3、last_noshow为2022-05-03,不符合需求——因为第7行和第8行日期相同,不应计入历史统计,期望第8行的prior_noshow_f为2、last_noshow为2022-04-17。
解决方案
核心思路是按用户分组后,针对每行单独筛选日期更早的历史失约记录,而非简单使用累计求和或滞后函数。
修改后的代码如下:
library(tidyverse) df <- df %>% # 转换日期格式,确保比较逻辑准确 mutate(date = as.Date(date)) %>% group_by(Person) %>% mutate( # 统计当前行之前、日期更早的失约次数 prior_noshow_f = map_dbl(row_number(), ~ sum(NoShow[1:(.x-1)][date[1:(.x-1)] < date[.x]])), # 判断是否存在符合条件的历史失约 prior_noshow = ifelse(prior_noshow_f > 0, 1, NA), # 获取最近一次早于当前日期的失约日期 last_noshow = map_chr(row_number(), ~ { valid_dates = date[1:(.x-1)][NoShow[1:(.x-1)] == 1 & date[1:(.x-1)] < date[.x]] if (length(valid_dates) > 0) max(valid_dates) else NA_character_ }) ) %>% # 转回原字符格式(可选操作) mutate(date = as.character(date), last_noshow = as.character(last_noshow)) %>% ungroup()
代码说明
- 先将
date转为Date类型,避免字符格式导致的日期比较错误; prior_noshow_f:通过map_dbl遍历每行,精准筛选当前行之前、日期更早且NoShow=1的记录并计数;prior_noshow:基于失约次数判断是否存在有效历史失约;last_noshow:筛选当前行之前的有效失约日期,取最大值即为最近一次的失约日期,无有效记录则返回NA;- 最后将日期转回字符格式,保持与原数据格式一致。
运行结果
Person NoShow date prior_noshow_f prior_noshow last_noshow <chr> <dbl> <chr> <dbl> <dbl> <chr> 1 334 1 2019-07-07 0 NA NA 2 334 1 2019-10-11 1 1 2019-07-07 3 334 1 2020-07-07 2 1 2019-10-11 4 334 1 2021-01-04 3 1 2020-07-07 5 999 1 2021-06-03 0 NA NA 6 999 1 2022-04-17 1 1 2021-06-03 7 999 1 2022-05-03 2 1 2022-04-17 8 999 0 2022-05-03 2 1 2022-04-17
可见第8行的prior_noshow_f为2,last_noshow为2022-04-17,完全符合需求。
内容的提问来源于stack exchange,提问作者Ray
相关产品推荐
相关产品推荐

