如何基于ID列交替删除分组内的连续行并保留指定行?
解决方案:分组内交替删除连续行并保留指定行
问题场景
原数据集示例:
group ID ABCDE 2 ABCDE 3 ABCDE 4 ABCDE 5 ABCDE 6 ABCDE 22
预期输出:
group ID result ABCDE 3 Keep ABCDE 5 Keep ABCDE 22 Keep
核心需求:在同一分组内,对连续ID构成的序列执行交替删除(删除第1、3、5...位的行),非连续的单独行直接保留。
Python(Pandas)实现方案
import pandas as pd # 构造示例数据 data = { 'group': ['ABCDE'] * 6, 'ID': [2, 3, 4, 5, 6, 22] } df = pd.DataFrame(data) def process_group(group_df): # 标记当前ID与上一个是否连续 group_df['is_consecutive'] = group_df['ID'].diff().fillna(1) == 1 # 生成连续块的唯一标识(不连续处分割) group_df['block'] = (~group_df['is_consecutive']).cumsum() # 计算每行在所属连续块内的位置(从0开始计数) group_df['block_pos'] = group_df.groupby('block').cumcount() # 标记保留/删除:连续块长度>1时,保留块内位置为奇数的行;单独行直接保留 group_df['result'] = 'Keep' drop_mask = (group_df.groupby('block')['ID'].transform('size') > 1) & (group_df['block_pos'] % 2 == 0) group_df.loc[drop_mask, 'result'] = 'Drop' return group_df # 分组处理并筛选结果 processed_df = df.groupby('group', group_keys=False).apply(process_group) final_df = processed_df[processed_df['result'] == 'Keep'].drop(columns=['is_consecutive', 'block', 'block_pos']) print(final_df)
关键逻辑说明
- 连续块识别:通过
ID.diff()计算相邻ID的差值,判断是否连续;用cumsum()将不连续的位置作为分割点,生成连续块的编号。 - 块内位置标记:用
cumcount()统计每行在所属连续块内的索引位置。 - 保留规则:连续块中,删除位置为偶数(从0开始)的行(对应原序列的第1、3、5...位);长度为1的单独块直接保留。
R(dplyr)实现方案
library(dplyr) # 构造示例数据 df <- tibble( group = rep("ABCDE", 6), ID = c(2, 3, 4, 5, 6, 22) ) final_df <- df %>% group_by(group) %>% mutate( # 标记是否连续(第一行默认连续) is_consecutive = c(TRUE, diff(ID) == 1), # 生成连续块标识 block = cumsum(!is_consecutive), # 计算每行在块内的位置(从1开始计数) block_pos = row_number() - match(block, block) + 1 ) %>% group_by(group, block) %>% mutate( # 连续块内删除奇数位置的行,其余保留 result = ifelse(n() > 1 & block_pos %% 2 == 1, "Drop", "Keep") ) %>% ungroup() %>% filter(result == "Keep") %>% select(-is_consecutive, -block, -block_pos) print(final_df)
关键逻辑说明
- 连续块识别:用
diff(ID)判断相邻ID是否连续,cumsum(!is_consecutive)生成块编号。 - 块内位置计算:通过
row_number()和match()计算每行在块内的位置(从1开始)。 - 保留规则:连续块中删除位置为奇数的行,单独块直接保留。
内容的提问来源于stack exchange,提问作者Shahrukh Khalidi
相关产品推荐
相关产品推荐

