R语言递归过滤数据:移除距上一条保留记录不足7天的条目
数据清洗需求:保留间隔≥7天的记录
需求规则:移除所有距离上一条保留记录不足7天的条目(已被移除的条目不计入判断依据)。
数据示例
library(dplyr) library(lubridate) df = data.frame(id = c(rep(1,5), rep(2,3)), date = c(ymd("2022-01-01"), ymd("2022-01-03"), ymd("2022-01-05"), ymd("2022-01-09"), ymd("2022-01-20"), ymd("2022-01-02"), ymd("2022-01-03"), ymd("2022-01-09"))) %>% arrange(id, date)
原始数据输出:
id date 1 1 2022-01-01 2 1 2022-01-03 3 1 2022-01-05 4 1 2022-01-09 5 1 2022-01-20 6 2 2022-01-02 7 2 2022-01-03 8 2 2022-01-09
期望结果
id date 1 1 2022-01-01 2 1 2022-01-09 3 1 2022-01-20 4 2 2022-01-02 5 2 2022-01-09
尝试的代码及问题
使用filter()和lag()函数无法满足需求,代码如下:
df %>% group_by(id) %>% mutate(prev = lag(date + days(7))) %>% ungroup() %>% filter(is.na(prev) | (date - prev >= 0))
得到的不符合预期的结果:
id date 1 1 2022-01-01 2 1 2022-01-20 3 2 2022-01-02
解决方案
问题核心是lag()仅能获取上一行的日期,无法跟踪上一条被保留记录的日期。需要通过累积计算或循环的方式,逐行判断当前日期是否与上一条保留日期间隔≥7天,满足则保留并更新基准日期,否则跳过。
方法1:dplyr结合purrr::accumulate实现累积判断
library(dplyr) library(lubridate) library(purrr) df_cleaned <- df %>% group_by(id) %>% mutate( # 累积更新上一条保留的日期 last_kept_date = accumulate(date, function(prev, curr) { if (curr - prev >= days(7)) curr else prev }, .init = first(date) - days(7)) %>% tail(-1), # 移除初始化的占位值 # 判断当前记录是否保留:第一条必留,或与上一条保留日期间隔≥7天 keep = row_number() == 1 | date >= last_kept_date + days(7) ) %>% filter(keep) %>% select(id, date) %>% ungroup() print(df_cleaned)
方法2:自定义循环函数(逻辑更直观)
clean_dates <- function(group_df) { kept_indices <- c(1) # 第一条记录默认保留 last_kept_date <- group_df$date[1] for (i in 2:nrow(group_df)) { if (group_df$date[i] - last_kept_date >= days(7)) { kept_indices <- c(kept_indices, i) last_kept_date <- group_df$date[i] } } return(group_df[kept_indices, ]) } df_cleaned <- df %>% group_by(id) %>% group_modify(~clean_dates(.x)) %>% ungroup() print(df_cleaned)
两种方法均可得到预期结果:
# A tibble: 5 × 2 id date <dbl> <date> 1 1 2022-01-01 2 1 2022-01-09 3 1 2022-01-20 4 2 2022-01-02 5 2 2022-01-09
内容的提问来源于stack exchange,提问作者Darmist
相关产品推荐
相关产品推荐

