基于R语言按特定states模式筛选id行并生成多数据集
我来帮你解决这个基于R语言数据框分组筛选的问题,咱们一步步来实现你需要的多个筛选条件数据集:
R语言分组筛选states序列的解决方案
首先先还原示例数据:
byRow <- TRUE example.Matrix <- matrix(data = c(1, 0, 0,1, 1, 0,1, 2, 0,1, 3, 1,1, 4, 1,1, 5, 1,1, 6, 0,1, 7, 0,1, 8, 3,2, 0, 0,2, 1, 0, 2, 2, 0, 2, 3, 1,2, 4, 1,2, 5, 4, 3, 0, 0,3,1, 0,3, 2, 1,3, 3, 0,3, 4, 4,4, 0, 0, 4, 1, 1, 4, 2, 0,4, 3, 0,4, 4, 0,4, 5, 1,4, 6, 0,4, 7, 3, 5, 0, 0,5, 1, 1,5, 2, 1, 5, 3, 0, 5, 4, 0,5, 5, 4), byrow=TRUE,ncol=3) example.df<-as.data.frame(example.Matrix) colnames(example.df) <- c("id", "day", "states")
1. 筛选:仅出现1个单独的1,且该1下一行非1、后续无1
这个条件要求每个id的states序列里,只有1次等于1的记录,且这个1的下一行(如果存在)不是1,同时之后再也没有1出现。这里推荐用dplyr分组处理,也可以用Base R实现:
dplyr实现(更简洁)
library(dplyr) df_1 <- example.df %>% group_by(id) %>% mutate( has_single_1 = sum(states == 1) == 1, # 仅出现1次1 next_after_1 = ifelse(which(states == 1) == n(), NA, states[which(states == 1)+1]), # 1的下一行值 no_more_1_after = all(states[(which(states == 1)+1):n()] != 1) # 1之后无其他1 ) %>% filter(has_single_1 & (is.na(next_after_1) | next_after_1 != 1) & no_more_1_after) %>% select(id, day, states) %>% ungroup() # 查看结果 df_1
输出结果就是你要的id=3的所有行:
# A tibble: 3 × 3 id day states <dbl> <dbl> <dbl> 1 3 2 1 2 3 3 0 3 3 4 4
2. 筛选:连续2个1,之后接非1值且后续无1
这个条件要求每个id的states序列里,存在连续2个1的片段,且这个片段之后的第一个值不是1,同时整个序列在这两个1之后再也没有1出现:
df_2 <- example.df %>% group_by(id) %>% mutate( # 标记连续2个1的起始位置 consecutive_2_1 = (states == 1 & lead(states) == 1), end_consec_2 = which(consecutive_2_1) + 1, # 验证后续条件:连续2个1后无其他1,且下一个值非1 valid = ifelse(length(end_consec_2) == 1, (end_consec_2 < n() & states[end_consec_2 + 1] != 1) & all(states[(end_consec_2 + 1):n()] != 1), FALSE), total_ones = sum(states == 1), valid = valid & total_ones == 2 # 确保只有这2个1 ) %>% filter(valid) %>% select(id, day, states) %>% ungroup() # 查看结果 df_2
输出结果包含id=2和id=5的所有行,符合预期。
3. 筛选:连续3个1,之后接非1值且后续无1
逻辑和上面一致,只是把连续1的数量调整为3:
df_3 <- example.df %>% group_by(id) %>% mutate( # 标记连续3个1的起始位置 consecutive_3_1 = (states == 1 & lead(states) == 1 & lead(states, 2) == 1), end_consec_3 = which(consecutive_3_1) + 2, valid = ifelse(length(end_consec_3) == 1, (end_consec_3 < n() & states[end_consec_3 + 1] != 1) & all(states[(end_consec_3 + 1):n()] != 1), FALSE), total_ones = sum(states == 1), valid = valid & total_ones == 3 ) %>% filter(valid) %>% select(id, day, states) %>% ungroup() # 查看结果 df_3
输出结果是id=1的所有行,符合预期。
4. 通用函数:一键生成30个对应连续k个1的数据集
为了避免重复写代码,我们可以封装一个通用函数,输入连续1的数量k,就能返回符合条件的数据集,然后循环生成k=0到29的30个结果:
通用筛选函数
filter_consecutive_ones <- function(df, k) { if (k == 0) { # k=0:序列中完全没有1的情况 result <- df %>% group_by(id) %>% filter(all(states != 1)) %>% ungroup() return(result) } df %>% group_by(id) %>% mutate( # 标记连续k个1的起始行 consec_ones = rowSums(sapply(0:(k-1), function(i) lead(states, i) == 1), na.rm = TRUE) == k, end_consec = which(consec_ones) + (k-1), # 验证核心条件:只有一组连续k个1,后续无1且下一个值非1 valid = ifelse(length(end_consec) == 1, (end_consec < n() & states[end_consec + 1] != 1) & all(states[(end_consec + 1):n()] != 1), FALSE), total_ones = sum(states == 1), valid = valid & total_ones == k ) %>% filter(valid) %>% select(id, day, states) %>% ungroup() }
批量生成30个数据集
# 创建列表存储所有结果 result_list <- list() # 循环生成k=0到29的数据集 for (k in 0:29) { result_list[[paste0("df_", k)]] <- filter_consecutive_ones(example.df, k) } # 可以通过列表名调用对应结果,比如查看k=1的结果 result_list$df_1
这样你就得到了30个分别对应不同连续1数量的数据集,每个结果都存储在result_list中,命名为df_0到df_29。
内容的提问来源于stack exchange,提问作者Anthony O'Brien
相关产品推荐
相关产品推荐

