Python pandas如何按多条件删除分组DataFrame中满足规则的指定行
问题背景
现有按id字段分组的DataFrame如下:
id outcome 0 3 no 1 3 no 2 3 no 3 3 yes 4 3 no 5 5 no 6 5 no 7 5 yes 8 5 yes 9 6 no 10 6 no 11 6 yes 12 6 yes 13 6 yes 14 6 yes 15 6 yes 16 6 no 17 6 no 18 6 no 19 7 no 20 7 no 21 7 yes 22 7 yes 23 7 no 24 7 no 25 7 no 26 7 yes
需要按照以下规则完成行删除:
- 若当前行的下一行outcome取值与当前行一致,删除当前行
- 若某行outcome为yes,其后一行必须是序列中第一个出现的no
- 连续yes序列中仅保留最后一个yes行
- 保留yes序列上方最后一个no,连续no序列中保留第一个和最后一个no
- 删除每个id分组最后一行为yes的行
- 处理完成后若某个id仅剩下1行no记录,删除该id对应的所有行
预期输出
id outcome 2 3 no 3 3 yes 4 3 no 10 6 no 15 6 yes 16 6 no 20 7 no 22 7 yes 23 7 no 25 7 no 26 7 yes
现有问题
当前实现第一部分保留了连续yes序列的第一个行而非要求的最后一个,同时第2、3部分的写法较为冗余,需要更精简的方案一次性满足所有条件。
解决方案
通过分组内打连续块标签的方式一次性处理所有过滤规则,代码如下:
import pandas as pd # 构造原始DataFrame df = pd.DataFrame({ 'id': [3,3,3,3,3,5,5,5,5,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7], 'outcome': ['no','no','no','yes','no','no','no','yes','yes','no','no','yes','yes','yes','yes','yes','no','no','no','no','no','yes','yes','no','no','no','yes'] }) def filter_id_group(g): # 为连续相同的outcome序列打块标签 g['block'] = (g['outcome'] != g['outcome'].shift()).cumsum() # 按块过滤:no块保留首尾行,yes块仅保留最后一行 filtered = g.groupby('block', group_keys=False).apply( lambda x: x.iloc[[0, -1]] if x['outcome'].iloc[0] == 'no' else x.iloc[[-1]] ).drop_duplicates() # 处理长度为1的块时避免重复保留同一行 # 删除分组最后一行为yes的行 if filtered.iloc[-1]['outcome'] == 'yes': filtered = filtered.iloc[:-1] # 剩余行数<=1时直接返回空表删除整个分组 if len(filtered) <= 1: return pd.DataFrame(columns=filtered.columns) return filtered.drop('block', axis=1) # 按id分组处理后重置索引 result = df.groupby('id', group_keys=False).apply(filter_id_group).reset_index(drop=True) print(result)
代码逻辑完全匹配所有规则,不需要多阶段拼接过滤条件,可读性和维护性更高。
内容的提问来源于stack exchange,提问作者Ze0ruso
相关产品推荐
相关产品推荐

