如何基于ID及Bxx数值增减变化对DataFrame数据分组?
实现Pandas DataFrame按A前缀+B数值升降转向分组
给定含ID列的DataFrame,ID格式为Axx-Bxx,需新增GROUP列,分组规则:
- 先按
Axx前缀分组 - 每个
Axx组内,按Bxx数值的升降转向(趋势变化点)划分小组
示例输入
import pandas as pd df = pd.DataFrame({ 'ID': ['A01-B01', 'A01-B03', 'A01-B02', 'A01-B04', 'A02-B05', 'A02-B04', 'A02-B03'] })
期望输出
| ID | GROUP |
|---|---|
| A01-B01 | A01-1 |
| A01-B03 | A01-1 |
| A01-B02 | A01-2 |
| A01-B04 | A01-3 |
| A02-B05 | A02-1 |
| A02-B04 | A02-1 |
| A02-B03 | A02-1 |
实现步骤
1. 提取分组所需字段
拆分ID得到Axx前缀和Bxx的数值:
# 拆分A前缀和B部分 df[['A_group', 'B_part']] = df['ID'].str.split('-', expand=True) # 提取B部分的数值并转为整数 df['B_num'] = df['B_part'].str.extract(r'(\d+)').astype(int)
2. 计算每个A组内的趋势转向点
通过计算B_num的差值符号,判断趋势变化(上升/下降),然后标记转向点并累加得到组内序号:
import numpy as np # 计算每个A组内B_num的差值,得到变化量 df['diff'] = df.groupby('A_group')['B_num'].diff() # 将差值转为符号:1=上升,-1=下降,0=不变 df['trend_sign'] = df['diff'].apply(lambda x: np.sign(x) if pd.notna(x) else 0) # 标记趋势转向点:当前趋势符号与前一行不同则为1,否则为0 df['trend_change'] = df.groupby('A_group')['trend_sign'].apply(lambda x: (x != x.shift()).astype(int)).reset_index(level=0, drop=True) # 累加转向点得到组内分组序号 df['group_in_A'] = df.groupby('A_group')['trend_change'].cumsum()
3. 生成最终GROUP列
将A_group和组内序号拼接:
df['GROUP'] = df['A_group'] + '-' + df['group_in_A'].astype(str) # 可选:删除中间辅助列 df = df.drop(['A_group', 'B_part', 'B_num', 'diff', 'trend_sign', 'trend_change', 'group_in_A'], axis=1)
完整代码
import pandas as pd import numpy as np # 示例输入 df = pd.DataFrame({ 'ID': ['A01-B01', 'A01-B03', 'A01-B02', 'A01-B04', 'A02-B05', 'A02-B04', 'A02-B03'] }) # 提取字段 df[['A_group', 'B_part']] = df['ID'].str.split('-', expand=True) df['B_num'] = df['B_part'].str.extract(r'(\d+)').astype(int) # 计算趋势转向与组内序号 df['diff'] = df.groupby('A_group')['B_num'].diff() df['trend_sign'] = df['diff'].apply(lambda x: np.sign(x) if pd.notna(x) else 0) df['trend_change'] = df.groupby('A_group')['trend_sign'].apply(lambda x: (x != x.shift()).astype(int)).reset_index(level=0, drop=True) df['group_in_A'] = df.groupby('A_group')['trend_change'].cumsum() # 生成GROUP列并清理 df['GROUP'] = df['A_group'] + '-' + df['group_in_A'].astype(str) df = df.drop(['A_group', 'B_part', 'B_num', 'diff', 'trend_sign', 'trend_change', 'group_in_A'], axis=1) print(df)
运行后输出与期望完全一致。
内容的提问来源于stack exchange,提问作者VERBOSE
相关产品推荐
相关产品推荐

