Pandas DataFrame组间Placement序列识别与跨行计算实现方案
问题描述
现有一个Pandas DataFrame,数据按Group分组,每组是Placement值相同的连续行;Placement包含low、lowmed、highmed、high四个层级,且连续组的Placement值不会重复。需求如下:
- 识别组间Placement变化跨度≥2且无反向中断的情况
- 对符合条件的情况,计算前一组最后一行与当前组第一行的
Value差值 - 将当前组第一行存入新DataFrame,并新增
Diff(差值)和Measured_From(前一组最后一行的Order值)列
输入示例
import pandas as pd df = pd.DataFrame({ 'Placement': ['high', 'high', 'high', 'highmed', 'highmed', 'lowmed', 'low', 'low', 'lowmed', 'lowmed', 'highmed', 'lowmed', 'high', 'highmed', 'low'], 'Value': [10, 8, 9, 7, 6, 4, 1, 2, 4, 5, 7, 5, 8, 5, 1], 'Order': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 'Group': [1, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10] })
输出示例
Placement Value Order Group Diff Measured_From 5 lowmed 4 6 3 5 2 6 low 1 7 4 5 4 6 low 1 7 4 8 2 10 highmed 7 11 6 5 7 12 high 8 13 8 3 11 14 low 1 15 10 4 13 14 low 1 15 10 7 12
用户原有思路:将Placement转为数值,检查当前组的前3个组:先检查组N-1,若与组N的Placement差值≥2则计算并记录;再检查组N-2,若差值小于N-1或方向反向则停止。但原代码因新增Placement值和多行组不再适用,寻求高效实现方案。
解决方案
步骤1:映射Placement到数值
给四个层级赋值,方便计算跨度:
placement_map = {'low': 0, 'lowmed': 1, 'highmed': 2, 'high': 3}
步骤2:提取组的关键信息
聚合每个组的核心数据,减少后续计算量:
# 获取每个组的首行数据 group_first = df.groupby('Group').first().reset_index() # 获取每个组末行的Order和Value group_last = df.groupby('Group').agg({'Order': 'last', 'Value': 'last'}).reset_index() # 合并组的首末信息,添加Placement对应的数值 group_info = pd.merge(group_first, group_last, on='Group', suffixes=('', '_last')) group_info['Placement_num'] = group_info['Placement'].map(placement_map)
步骤3:遍历组,查找符合条件的前置组
对每个组向前遍历前置组,判断是否满足跨度≥2且无反向中断(反向中断指当前组与前置组的变化方向,和紧邻前置组的变化方向相反),一旦出现反向或跨度不足则停止查找:
result_rows = [] for i in range(1, len(group_info)): current_group = group_info.iloc[i] current_pnum = current_group['Placement_num'] # 提取当前组首行的基础数据 base_row = current_group[['Placement', 'Value', 'Order', 'Group']].to_dict() prev_dir = None # 记录上一个变化方向 for j in range(i-1, -1, -1): prev_group = group_info.iloc[j] prev_pnum = prev_group['Placement_num'] span = abs(current_pnum - prev_pnum) curr_dir = current_pnum - prev_pnum # 出现反向中断则停止向前查找 if prev_dir is not None and curr_dir * prev_dir < 0: break # 跨度≥2则记录结果 if span >= 2: diff = abs(current_group['Value'] - prev_group['Value_last']) row = base_row.copy() row['Diff'] = diff row['Measured_From'] = prev_group['Order_last'] result_rows.append(row) prev_dir = curr_dir # 转换为DataFrame并按Order排序 result_df = pd.DataFrame(result_rows).sort_values('Order').reset_index(drop=True)
效率说明
- 先通过聚合将数据压缩到组级别,避免逐行处理的冗余计算
- 前置组遍历遇到反向中断立即停止,减少不必要的循环
内容的提问来源于stack exchange,提问作者Jordan
相关产品推荐
相关产品推荐

