在R中筛选每个个体首次出现非连续年份前的所有行
问题描述
数据集
我在R中有如下数据集:
name = c("john", "john", "john", "john", "john", "alex", "alex", "alex", "alex", "alex" ) year = c(2010, 2011, 2012, 2015, 2017, 2014, 2015, 2016, 2017, 2018) my_data = data.frame(name, year)
输出结果:
> my_data name year 1 john 2010 2 john 2011 3 john 2012 4 john 2015 5 john 2017 6 alex 2014 7 alex 2015 8 alex 2016 9 alex 2017 10 alex 2018
需求
为数据集中的每个个体,筛选出首次出现非连续年份之前的所有行。期望输出如下:
# DESIRED OUTPUT > my_data name year 1 john 2010 2 john 2011 3 john 2012 6 alex 2014 7 alex 2015 8 alex 2016 9 alex 2017 10 alex 2018
尝试的方法
我尝试了以下方法:
agg <- aggregate(year ~ name, my_data, c) agg # name year.1 year.2 year.3 year.4 year.5 # 1 alex 2014 2015 2016 2017 2018 # 2 john 2010 2011 2012 2015 2017 library(stringr) agg = data.frame(as.matrix(agg)) agg$all_years = paste(agg$year.1 ,agg$year.2, agg$year.3, agg$year.4, agg$year.5) agg$y_2010 = str_detect(agg$all_years, "2010") agg$y_2011 = str_detect(agg$all_years, "2011") agg$y_2012 = str_detect(agg$all_years, "2012") agg$y_2013 = str_detect(agg$all_years, "2013") agg$y_2014 = str_detect(agg$all_years, "2014") agg$y_2015 = str_detect(agg$all_years, "2015") agg$y_2016 = str_detect(agg$all_years, "2016") agg$y_2017 = str_detect(agg$all_years, "2017") agg$y_2018 = str_detect(agg$all_years, "2018") agg$y_2019 = str_detect(agg$all_years, "2019")
输出结果:
name year.1 year.2 year.3 year.4 year.5 all_years y_2010 y_2011 y_2012 y_2013 y_2014 y_2015 y_2016 y_2017 y_2018 y_2019 1 alex 2014 2015 2016 2017 2018 2014 2015 2016 2017 2018 FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE FALSE 2 john 2010 2011 2012 2015 2017 2010 2011 2012 2015 2017 TRUE TRUE TRUE FALSE FALSE TRUE FALSE TRUE FALSE FALSE
我的思路是找出每行中首次出现“TRUE”后接“FALSE”的位置,但不确定后续如何操作,请求指导完成该需求。
解决方案
你的思路方向没错,但现有方法太繁琐,其实可以直接对每个个体的年份序列计算相邻差值,找到首次出现非1的位置,然后保留该位置之前的所有行。以下是两种更高效的实现方式:
方法1:基础R实现
# 按name分组处理 result <- do.call(rbind, lapply(split(my_data, my_data$name), function(df) { # 计算相邻年份的差值 diffs <- diff(df$year) # 找到第一个差值不等于1的位置,若全部连续则返回所有行 first_break <- which(diffs != 1)[1] if (is.na(first_break)) { df } else { df[1:first_break, ] } })) # 重置行名(可选) rownames(result) <- NULL result
输出结果:
name year 1 alex 2014 2 alex 2015 3 alex 2016 4 alex 2017 5 alex 2018 6 john 2010 7 john 2011 8 john 2012
方法2:dplyr包实现(更简洁)
如果你习惯用tidyverse工具链,可以用dplyr分组处理:
library(dplyr) result <- my_data %>% group_by(name) %>% # 标记当前年份与下一年份是否连续,最后一行设为TRUE(因为没有下一年) mutate(is_continuous = ifelse(row_number() == n(), TRUE, year + 1 == lead(year))) %>% # 找到首次不连续的位置,保留之前的所有行 filter(cumall(is_continuous)) %>% select(-is_continuous) %>% ungroup() result
输出结果和基础R方法一致。
方法说明
两种方法的核心逻辑都是:
- 按
name分组,获取每个个体的年份序列 - 检查年份是否连续(相邻年份差值为1)
- 找到首次出现不连续的位置,保留该位置及之前的所有行(如果全部连续则保留所有行)
这种方法比字符串检测方式更直接,不需要手动创建年份列,也能适配任意年份范围。
内容的提问来源于stack exchange,提问作者stats_noob
相关产品推荐
相关产品推荐

