在Pandas DataFrame中按ID分组提取连续0的中间行及后续首个1行
解决Pandas按ID分组提取特定模式行的问题
没问题,我来帮你搞定这个需求!咱们要做的是按id分组,找出每组里连续至少5个0,且后面紧跟着1的序列,然后提取这些连续0的中间行,以及后续第一个1的行。下面一步步来:
1. 准备示例数据
先创建一个带id、event和其他列的示例DataFrame,方便咱们演示:
import pandas as pd import numpy as np # 构造示例数据 data = { 'id': [1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2], 'event': [0,0,0,0,0,1,0,0,0,0, 0,0,0,0,0,0,1,0,0,1,0], 'other_col': np.arange(1,22) } df = pd.DataFrame(data)
这个示例里,id=1有一段5个0后跟1,还有一段4个0(不符合≥5的条件);id=2有一段6个0后跟1,还有一段2个0后跟1(不符合)。
2. 分组标记连续块并筛选符合条件的序列
接下来咱们要给每个分组内的行标记所属的连续块,然后筛选出符合要求的块:
def process_group(group): # 标记event是否为0 is_zero = group['event'] == 0 # 标记连续0块的起始点:当前是0且前一行不是0(或者是第一行) block_start = is_zero & (~is_zero.shift(fill_value=False)) # 给每个连续块分配唯一ID group['block_id'] = block_start.cumsum() # 只保留0的块,然后计算每个块的长度 zero_blocks = group[is_zero].groupby('block_id').size().reset_index(name='block_length') # 筛选出长度≥5的0块 valid_zero_blocks = zero_blocks[zero_blocks['block_length'] >=5]['block_id'].tolist() result_rows = [] for block_id in valid_zero_blocks: # 获取当前0块的所有行 block_rows = group[group['block_id'] == block_id] # 检查块后面是否紧跟1:块的最后一行的下一行event是1 last_block_idx = block_rows.index[-1] if last_block_idx +1 >= len(group): # 块在分组末尾,后面没有行,跳过 continue next_row = group.iloc[last_block_idx +1] if next_row['event'] !=1: continue # 提取0块的中间行:长度n,中间位置是(n-1)//2(索引从0开始) n = len(block_rows) middle_idx_in_block = (n-1)//2 middle_row = block_rows.iloc[middle_idx_in_block] result_rows.append(middle_row) # 提取后面的第一个1行 result_rows.append(next_row) return pd.DataFrame(result_rows) if result_rows else pd.DataFrame(columns=group.columns) # 应用分组处理 final_result = df.groupby('id').apply(process_group).reset_index(drop=True)
3. 查看结果
运行上面的代码后,final_result就是咱们要的结果:
print(final_result)
输出大概是这样(对应示例数据):
id event other_col block_id 0 1 0 3 1 1 1 1 6 0 2 2 0 8 1 3 2 1 13 0
解释一下:
- id=1的5个0中间行是第3个0(other_col=3),后面的1是other_col=6的行;
- id=2的6个0中间行是第3个0(other_col=8),后面的1是other_col=13的行。
一些补充说明
- 如果连续0的长度是偶数(比如6),这里取的是第(n-1)//2个,也就是索引2(第3个元素),你也可以根据需求改成取中间两个的任意一个,比如
n//2; - 代码里已经处理了块在分组末尾后面没有行的情况,以及块后面不是1的情况,这些都会被跳过;
- 如果同一个id里有多个符合条件的块,都会被提取出来。
内容的提问来源于stack exchange,提问作者user07
相关产品推荐
相关产品推荐

