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

如何对pandas dataframe应用多条件删除或选取特定行

Pandas DataFrame 按规则筛选实现问题

原始数据

我有如下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

该数据已按id分组,且按日期升序排列。

筛选要求

  • 1、若当前行与下一行的outcome值相同,则删除当前行;
  • 2、若某行outcome为yes,则其下一行必须是首个no;
  • 3、必须保留每个id分组下最后一个yes值;
  • 4、额外需要保留yes上方的最后一个no,若yes上方为连续no序列,则需保留该序列的第一个和最后一个no。

期望输出

id outcome
2    3      no
3    3     yes
4    3      no
6    5      no
8    5     yes
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上方连续no序列的第一个和最后一个值:

df = pd.DataFrame(data={'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], 
     'outcome': ['no','no','no','yes','no','no','no','yes','yes','no','no','yes','yes','yes','yes','yes','no','no','no', 'no', 'yes', 'no', 'no', 'yes']})


m1 = df['outcome'] # mask 1是outcome列
m2 = m1.groupby(df['id']).shift(-1)  # 按id分组后向上偏移1位
m3 = m1.groupby(df['id']).shift().eq('yes')&m1.eq('no') # 布尔校验

df2 = df[~m1.eq(m2)|m3]
m4 = df2['outcome']
m5 = m4.groupby(df2['id']).shift()
df3 = df2[~m4.eq(m5)]

解决方法

可以按id分组后,先为组内连续相同的outcome序列打块标签,再针对不同类型的块按规则筛选行:

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_single_group(g):
    # 为组内连续相同的outcome序列打块标签
    g['block_id'] = g['outcome'].ne(g['outcome'].shift()).cumsum()
    res_frames = []
    all_blocks = list(g.groupby('block_id'))
    block_count = len(all_blocks)
    
    for idx, (bid, block) in enumerate(all_blocks):
        block_val = block['outcome'].iloc[0]
        if block_val == 'yes':
            # yes块仅保留最后一行,满足保留每个id分组最后一个yes的要求
            res_frames.append(block.tail(1))
        else:
            # 判断当前no块下一个块是否是yes块
            next_is_yes = idx < block_count - 1 and all_blocks[idx+1][1]['outcome'].iloc[0] == 'yes'
            # 判断当前no块上一个块是否是yes块
            prev_is_yes = idx > 0 and all_blocks[idx-1][1]['outcome'].iloc[0] == 'yes'
            
            if next_is_yes:
                # 后续是yes的no块,保留首尾行
                res_frames.append(block.head(1))
                res_frames.append(block.tail(1))
            elif prev_is_yes:
                # 前序是yes的no块,保留首行(yes后的第一个no)
                res_frames.append(block.head(1))
    return pd.concat(res_frames).drop(columns='block_id')

# 按id分组应用筛选规则
result = df.groupby('id', group_keys=False).apply(filter_single_group)
print(result)

运行上述代码即可得到符合要求的输出结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 22:06:03