按组移除status列中'a'前的前置NA,保留'b'前的NA
解决方案:按id分组移除'a'前置NA,保留'b'前置NA
首先咱们先把原始数据框明确下来:
df <- data.frame( id = rep(1:4, each = 4), status = c(NA, "a", "c", "a", NA, "b", "c", "c", NA, NA, "a", "c", NA, NA, "b", "b"), stringsAsFactors = FALSE )
核心逻辑拆解
咱们要实现的是:
- 对每个
id分组,先检查组内是否存在status = 'a' - 如果存在,找到第一个
'a'的位置,只保留从这个位置开始的所有行(也就是删掉它之前的前置NA) - 如果不存在
'a',不管有没有前置NA或者'b',都保留所有行
方法一:用dplyr(推荐,代码更直观)
借助dplyr的分组、计算和过滤功能,一步到位:
library(dplyr) result_df <- df %>% # 按id分组 group_by(id) %>% # 计算每个组里第一个'a'的位置:如果没有'a'就设为Inf(无穷大) mutate( first_a_pos = ifelse(any(status == "a"), min(which(status == "a")), Inf) ) %>% # 过滤行:要么行号 >= 第一个'a'的位置,要么组里没有'a'(first_a_pos是Inf) filter(row_number() >= first_a_pos | is.infinite(first_a_pos)) %>% # 删掉临时计算的列 select(-first_a_pos) %>% # 取消分组 ungroup() # 查看结果 print(result_df)
运行后得到的结果就是你要的结构:
# A tibble: 13 × 2 id status <int> <chr> 1 1 a 2 1 c 3 1 a 4 2 NA 5 2 b 6 2 c 7 2 c 8 3 a 9 3 c 10 4 NA 11 4 NA 12 4 b 13 4 b
方法二:用base R(不需要额外包)
如果不想加载dplyr,用base R的split()和lapply()也能实现:
# 按id拆分数据框 grouped_list <- split(df, df$id) # 对每个分组处理 processed_list <- lapply(grouped_list, function(group) { # 找到组内所有'a'的位置 a_positions <- which(group$status == "a") if (length(a_positions) > 0) { # 有'a'的话,从第一个'a'开始保留行 group[min(a_positions):nrow(group), ] } else { # 没有'a'就保留全部行 group } }) # 合并回数据框,重置行名 result_df_base <- do.call(rbind, processed_list) rownames(result_df_base) <- NULL # 查看结果 print(result_df_base)
这个版本的结果和dplyr版本完全一致,适合不想依赖第三方包的场景。
内容的提问来源于stack exchange,提问作者Roccer
相关产品推荐
相关产品推荐

