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

Pandas按条件删除id分组下连续重复outcome行的实现方法

问题分析

你原有代码的核心问题有两个:

  1. 计算连续块的shift操作没有按id分组,会把上一个id的最后一条记录和下一个id的第一条记录做比较,导致跨id的块标签计算错误
  2. 没有实现规则中「yes后面第一个出现的no需要保留」的逻辑,所有连续块统一取尾行,和需求冲突

实现方案

这里提供两种符合pandas规范的实现方式,可根据数据量选择:

方案1:分组自定义函数(逻辑清晰易维护,适合中小数据量)

直接按id分组后逐块处理,完全匹配规则:

import pandas as pd

# 预处理:确保日期格式正确、按id和日期升序排序(已排序可跳过排序步骤)
df['date'] = pd.to_datetime(df['date'], dayfirst=True)
df = df.sort_values(['id', 'date']).reset_index(drop=True)

def filter_id_group(g):
    # 生成当前id下的连续outcome块标签
    g['block'] = (g['outcome'] != g['outcome'].shift()).cumsum()
    result_rows = []
    prev_block_outcome = None
    # 遍历每个连续块
    for _, block in g.groupby('block'):
        curr_outcome = block['outcome'].iloc[0]
        if curr_outcome == 'yes':
            # yes块取最后一条
            result_rows.append(block.iloc[-1])
        else:
            # no块判断前一个块是不是yes
            if prev_block_outcome == 'yes':
                # 前一个是yes,取当前no块的第一条
                result_rows.append(block.iloc[0])
            else:
                # 其他情况取no块最后一条
                result_rows.append(block.iloc[-1])
        prev_block_outcome = curr_outcome
    return pd.DataFrame(result_rows)

# 分组处理并去掉辅助列
updated_df = df.groupby('id', group_keys=False).apply(filter_id_group).drop(columns='block').reset_index(drop=True)
# 可选:把日期转回你需要的字符串格式
updated_df['date'] = updated_df['date'].dt.strftime('%d/%m/%Y')

方案2:向量化操作(性能更高,适合百万级以上大数据量)

完全用pandas原生向量化操作实现,避免循环:

import pandas as pd

# 预处理同上
df['date'] = pd.to_datetime(df['date'], dayfirst=True)
df = df.sort_values(['id', 'date']).reset_index(drop=True)

# 1. 按id生成连续outcome块标签
df['block'] = df.groupby('id')['outcome'].transform(lambda x: (x != x.shift()).cumsum())

# 2. 关联每个块的属性:当前块outcome、前一个块的outcome
block_meta = df.groupby(['id', 'block'])['outcome'].first().reset_index(name='curr_block_out')
block_meta['prev_block_out'] = block_meta.groupby('id')['curr_block_out'].shift()
df = df.merge(block_meta, on=['id', 'block'], how='left')

# 3. 按规则筛选行
# 条件1:yes块取最后一行
cond_yes = (df['curr_block_out'] == 'yes') & (df.groupby(['id', 'block']).cumcount(ascending=False) == 0)
# 条件2:前块是yes的no块取第一行
cond_no_after_yes = (df['curr_block_out'] == 'no') & (df['prev_block_out'] == 'yes') & (df.groupby(['id', 'block']).cumcount() == 0)
# 条件3:其他no块取最后一行
cond_normal_no = (df['curr_block_out'] == 'no') & (df['prev_block_out'] != 'yes') & (df.groupby(['id', 'block']).cumcount(ascending=False) == 0)

updated_df = df[cond_yes | cond_no_after_yes | cond_normal_no].drop(columns=['block', 'curr_block_out', 'prev_block_out']).reset_index(drop=True)
# 可选日期格式转换
updated_df['date'] = updated_df['date'].dt.strftime('%d/%m/%Y')

输出结果

两种方案得到的结果完全和预期一致:

id        date outcome
0   3  04/09/2019      no
1   3  30/10/2019     yes
2   3  03/05/2020      no
3   5  26/12/2019      no
4   5  03/06/2020     yes
5   6  27/10/2019      no
6   6  14/04/2020     yes
7   6  24/04/2020      no

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 06:06:04