按ID分组检查DataFrame中指定状态是否最终导向sold
问题与解决方案
问题背景
现有如下DataFrame:
id | status ____________ 1 | reserved 2 | signed 1 | waiting 1 | signed 3 | waiting 2 | sold 3 | reserved 1 | sold
需要验证假设:状态reserved、waiting、signed最终都会导向sold(允许跳过部分状态,比如id=2直接从signed到sold)。目标是筛选出符合该规则的ID列表或DataFrame,期望输出如下:
id __ 1 2
本人尝试先筛选指定状态的数据,按ID分组获取唯一状态列表,但因可能的状态组合过多,无法逐一过滤,尝试的代码如下:
df = df[df.status.isin(['reserved', 'waiting', 'signed', 'sold'])] df1 = df.groupby('flat_id')['status'].unique() df1.where('status'== ['reserved', 'waiting', 'signed', 'sold'] or ['reserved', 'waiting', 'sold'] ... )
解决方案
核心逻辑是:每个符合规则的ID,其状态序列最终必须以sold结尾,且所有在sold之前的状态只能是reserved、waiting、signed这三类(允许跳过中间状态)。
方法一:分组后自定义验证函数
先确保数据按ID排序(保证状态顺序是时间先后),再对每个分组做规则校验:
import pandas as pd # 初始化示例数据 df = pd.DataFrame({ 'id': [1,2,1,1,3,2,3,1], 'status': ['reserved','signed','waiting','signed','waiting','sold','reserved','sold'] }) # 按id排序,确保状态顺序符合时间逻辑 df = df.sort_values('id').reset_index(drop=True) def validate_status(group): status_seq = group['status'].tolist() # 最后一个状态必须是sold if status_seq[-1] != 'sold': return False # 检查所有sold之前的状态都是允许的前置状态 allowed_pre_states = {'reserved', 'waiting', 'signed'} return all(state in allowed_pre_states for state in status_seq[:-1]) # 分组验证,筛选出符合条件的id valid_ids = df.groupby('id').apply(validate_status) result = valid_ids[valid_ids].index.to_frame(name='id') print(result)
运行输出:
id 0 1 1 2
方法二:利用分组聚合简化代码
通过last()获取每个ID的最终状态,同时校验所有非最终状态的合法性:
# 筛选最终状态为sold的ID final_is_sold = df.groupby('id')['status'].last() == 'sold' # 筛选所有非sold状态都属于允许列表的ID pre_states_valid = df[df['status'] != 'sold'].groupby('id')['status'].apply(lambda x: x.isin(['reserved', 'waiting', 'signed']).all()) # 取两个条件的交集,得到符合规则的ID valid_ids = final_is_sold & pre_states_valid result = valid_ids[valid_ids].index.to_frame(name='id') print(result)
这个方法代码更简洁,结果和方法一完全一致。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

