如何定位Pandas DataFrame中符合条件的最后连续序列首元素并获取该行?
问题与解决方案
问题描述
给定如下DataFrame:
import pandas as pd df = pd.DataFrame({'a': [1, 0, 0, 1, 0, -1, -1, -1, 0, -1, -1, -1]})
期望得到如下输出:
a b 0 1 NaN 1 0 NaN 2 0 NaN 3 1 NaN 4 0 NaN 5 -1 NaN 6 -1 NaN 7 -1 NaN 8 0 NaN 9 -1 x 10 -1 NaN 11 -1 NaN
需要找到数据列中的最后一个连续序列(示例中为-1),并标记该序列的第一个元素,同时需满足以下四个条件:
- 排除由0组成的连续序列;
- 连续序列长度至少为2行;
- 若最后一个连续序列是0,则不标记任何内容;
- 若最后一个非0连续序列长度不足2行,也不进行标记。
用户尝试了以下代码但未成功:
mask = (df.a == -1) df.loc[(mask.cumsum() == mask.sum() - 1) & mask, 'b'] = 'x'
另外,用户还想知道:找到目标行后,如何在不创建新列(如上述代码中的b列)的情况下获取该行?目前习惯是创建新列后筛选非NaN值的行。
解决方案
1. 实现标记目标行的逻辑
要满足所有条件,我们可以通过分组连续相同值的方式来处理:
import pandas as pd df = pd.DataFrame({'a': [1, 0, 0, 1, 0, -1, -1, -1, 0, -1, -1, -1]}) # 生成连续相同值的分组标签 df['group'] = (df['a'] != df['a'].shift()).cumsum() # 计算每个分组的长度、值 group_info = df.groupby('group')['a'].agg( value='first', length='count' ).reset_index() # 筛选符合条件的目标分组:非0、长度≥2、是最后一个非0分组 target_group = group_info[ (group_info['value'] != 0) & (group_info['length'] >= 2) ].tail(1) # 标记该分组的第一行 if not target_group.empty: df['b'] = pd.NA target_idx = df[df['group'] == target_group['group'].iloc[0]].index[0] df.loc[target_idx, 'b'] = 'x' # 删除临时分组列 df = df.drop('group', axis=1) print(df)
核心逻辑说明:
- 用
shift()对比前后值,生成连续相同值的分组ID; - 统计每个分组的基本信息(值、长度);
- 筛选出最后一个满足「非0、长度≥2」的分组;
- 标记该分组的第一行为
x,其余行设为NaN。
2. 不创建新列直接获取目标行
如果不需要标记列,直接获取目标行,可以跳过创建b列的步骤,直接提取索引:
import pandas as pd df = pd.DataFrame({'a': [1, 0, 0, 1, 0, -1, -1, -1, 0, -1, -1, -1]}) # 生成分组标签 df['group'] = (df['a'] != df['a'].shift()).cumsum() # 分组统计 group_info = df.groupby('group')['a'].agg( value='first', length='count' ).reset_index() # 筛选目标分组 target_group = group_info[ (group_info['value'] != 0) & (group_info['length'] >= 2) ].tail(1) # 获取目标行(如果存在) if not target_group.empty: target_row = df[df['group'] == target_group['group'].iloc[0]].iloc[0] print("目标行:") print(target_row) else: print("没有符合条件的目标行") # 清理临时列 df = df.drop('group', axis=1)
这样就能直接得到目标行,无需创建额外的标记列。
内容的提问来源于stack exchange,提问作者AmirX
相关产品推荐
相关产品推荐

