You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

按分组筛选含连续1的行:生成两类目标数据集

问题描述

我有一个dataframe,需要筛选出仅包含连续值“1”的子集。具体数据结构如下:

library(tidyverse)
library(zoo)

df <- data.frame(matrix(ncol = 3, nrow = 17))
colnames(df) <- c("row_id","id", "k_yes")
df$row_id <- c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17)
df$id <- c("1_1","1_1","1_1","1_1","1_1","1_1","1_2","1_2","1_2","1_2","1_2","1_2","1_3","1_3","1_3","1_3","1_3")
df$k_yes <- c(1,1,1,0,1,1,0,0,0,1,1,0,1,0,1,0,1)
df
# 输出:
#    row_id    id  k_yes
# 1       1   1_1      1
# 2       2   1_1      1
# 3       3   1_1      1
# 4       4   1_1      0
# 5       5   1_1      1
# 6       6   1_1      1
# 7       7   1_2      0
# 8       8   1_2      0
# 9       9   1_2      0
# 10     10   1_2      1
# 11     11   1_2      1
# 12     12   1_2      0
# 13     13   1_3      1
# 14     14   1_3      0 
# 15     15   1_3      1
# 16     16   1_3      0
# 17     17   1_3      1

需求说明

需要生成两个数据集:

1) 仅保留至少两个连续“1”的序列

按id分组,仅保留至少两个连续的“1”;若“1”之间有“0”,则需舍弃后续的“1”序列。同时新增new_group列标记每个唯一序列,目标输出如下:

row_id    id k_yes  new_group
1      1   1_1     1      1_1_A
2      2   1_1     1      1_1_A
3      3   1_1     1      1_1_A
4      5   1_1     1      1_1_B
5      6   1_1     1      1_1_B
6     10   1_2     1      1_2_A
7     11   1_2     1      1_2_A

2) 允许“1”之间存在单个“0”(且“0”后必须有“1”)

按id分组,允许“1”之间存在单个“0”,但“0”后必须有“1”,不接受如0-1-1、1-1-0等序列,同样新增new_group列,目标输出如下:

row_id    id k_yes  new_group
1       1   1_1     1      1_1_A
2       2   1_1     1      1_1_A
3       3   1_1     1      1_1_A
4       4   1_1     0      1_1_A
5       5   1_1     1      1_1_A
6       6   1_1     1      1_1_A
7      10   1_2     1      1_2_A
8      11   1_2     1      1_2_A
9      13   1_3     1      1_3_A
10     14   1_3     0      1_3_A
11     15   1_3     1      1_3_A
12     16   1_3     0      1_3_A
13     17   1_3     1      1_3_A

尝试代码及错误

尝试参考方案使用以下代码:

df |>
  group_by(id) |> 
  mutate(b = c(first(k_yes) , zoo::rollsum(k_yes, 1))) |>
  summarise(groups_to_keep = id[which(b >= 2)]) -> gk

出现错误:

Error in `mutate()`:
ℹ In argument: `b = c(first(k_yes), zoo::rollsum(k_yes, 1))`.
ℹ In group 1: `id = "1_1"`.
Caused by error:
! `b` must be size 5 or 1, not 6.

推测是rollsum()函数使用问题,寻求解决帮助。

更新:新增数据集版本,会在第一个数据集中生成两个new_group值。

解决方案

针对需求1的解决代码

核心思路是按id分组识别连续的1序列,筛选长度≥2的序列后生成标记列:

df1 <- df %>%
  group_by(id) %>%
  # 标记连续的1序列:k_yes切换时新建分组
  mutate(seq_group = cumsum(c(TRUE, diff(k_yes) != 0))) %>%
  filter(k_yes == 1) %>%
  # 计算每个序列的长度
  add_count(seq_group, name = "seq_length") %>%
  # 保留长度≥2的序列
  filter(seq_length >= 2) %>%
  # 生成new_group标记
  mutate(new_group = paste0(id, "_", LETTERS[match(seq_group, unique(seq_group))])) %>%
  select(-seq_group, -seq_length) %>%
  ungroup()

print(df1)

针对需求2的解决代码

核心思路是先标记符合规则的行,再筛选掉开头/结尾为0的无效序列:

df2 <- df %>%
  group_by(id) %>%
  mutate(
    # 标记有效行:1直接有效,0需前后都是1
    valid = case_when(
      k_yes == 1 ~ TRUE,
      k_yes == 0 ~ lag(k_yes) == 1 & lead(k_yes) == 1,
      TRUE ~ FALSE
    ),
    # 标记连续的有效序列组
    seq_group = cumsum(c(TRUE, diff(valid) != 0))
  ) %>%
  filter(valid) %>%
  group_by(id, seq_group) %>%
  # 排除开头或结尾为0的序列
  filter(first(k_yes) != 0 & last(k_yes) != 0) %>%
  # 生成new_group标记
  mutate(new_group = paste0(id, "_", LETTERS[match(seq_group, unique(seq_group))])) %>%
  select(-valid, -seq_group) %>%
  ungroup()

print(df2)

原错误解释

原代码中zoo::rollsum(k_yes, 1)无实际意义(窗口大小1的滚动和等于原数据),且c(first(k_yes), rollsum(...))会导致列长度不匹配:以id=1_1分组为例,分组共6行,rollsum返回6个值,加上first(k_yes)后变成7个,与分组行数冲突从而报错。正确做法是用cumsum(diff(k_yes)!=0)标记连续序列,而非错误使用rollsum。

内容的提问来源于stack exchange,提问作者mto23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 03:56:19