如何在Pandas中合并含父子关系且多子项的行
合并Pandas中父案件与多个子案件的实现方案
需求说明
需要将案件数据中的父案件与所有关联子案件合并,每个父案件保留一行,遵循以下规则:
- A、B、C、D字段:优先级为
Yes>No>Unknown,只要父/子项中有Yes则取Yes,无Yes但有No则取No,否则为Unknown Demanded和Paid字段:取父案件与所有子案件中的最大值Notes字段:拼接父案件与所有非空子案件的备注内容- 新增
Child_Case #字段,存储所有关联子案件的编号列表
原始数据
import pandas as pd import numpy as np d = [{'Case #': 'CHI22-01234', 'Parent Case #': np.nan, 'Name': 'Abraham Lincoln', 'Demanded': 5000.0, 'Paid': 2000.0, 'A': 'Yes', 'B': 'No', 'C': 'Yes', 'D': 'Yes', 'Notes': 'Honest Abe with his stovepipe hat'}, {'Case #': 'CHI22-01237', 'Parent Case #': np.nan, 'Name': 'Ebenezer Scrooge', 'Demanded': 10000.0, 'Paid': 0.0, 'A': 'Yes', 'B': 'Yes', 'C': 'Unknown', 'D': 'No', 'Notes': 'What a scrooge!'}, {'Case #': 'NYC21-02222', 'Parent Case #': 'CHI22-01237', 'Name': 'Ebenezer Scrooge', 'Demanded': 0.0, 'Paid': 9500.0, 'A': 'Unknown', 'B': 'Unknown', 'C': 'No', 'D': 'Yes', 'Notes': 'Scrooge has an apartment in NYC'}, {'Case #': 'NYC22-03333', 'Parent Case #': np.nan, 'Name': 'Bob Marley', 'Demanded': 1000000.0, 'Paid': 0.0, 'A': 'No', 'B': 'Unknown', 'C': 'Unknown', 'D': 'Unknown', 'Notes': 'He will send three ghosts'}, {'Case #': 'PHX21-01234', 'Parent Case #': np.nan, 'Name': 'Bob Cratchit', 'Demanded': 0.0, 'Paid': 0.0, 'A': 'Unknown', 'B': 'Unknown', 'C': 'Unknown', 'D': 'Unknown', 'Notes': 'Jr. accountant'}, {'Case #': 'PHX21-01238', 'Parent Case #': 'PHX21-01234', 'Name': 'Bob Cratchit', 'Demanded': 500000.0, 'Paid': 22000.0, 'A': 'No', 'B': 'Yes', 'C': 'No', 'D': 'No', 'Notes': 'Promoted to Sr. accountant 12/14/2021'}, {'Case #': 'LAX22-12345', 'Parent Case #': np.nan, 'Name': 'Kim Kardashian', 'Demanded': 50000.0, 'Paid': 0.0, 'A': 'Yes', 'B': 'No', 'C': 'Unknown', 'D': 'Unknown', 'Notes': 'Kim & Kanye for president'}, {'Case #': 'LAX22-12349', 'Parent Case #': 'LAX22-12345', 'Name': 'Kim Kardashian', 'Demanded': 50000.0, 'Paid': 30000.0, 'A': 'Yes', 'B': 'Yes', 'C': 'Unknown', 'D': 'Unknown', 'Notes': 'Updated data'}, {'Case #': 'NYC23-21456', 'Parent Case #': 'NYC22-03333', 'Name': 'Bob Marley Mon', 'Demanded': 0.0, 'Paid': 0.0, 'A': 'Yes', 'B': 'Yes', 'C': 'Unknown', 'D': 'Unknown', 'Notes': np.nan}, {'Case #': 'NYC23-21457', 'Parent Case #': 'NYC22-03333', 'Name': 'Bob Marley Jamaican Mon', 'Demanded': 0.0, 'Paid': 0.0, 'A': 'Unknown', 'B': 'Unknown', 'C': 'Unknown', 'D': 'Unknown', 'Notes': 'Can it handle multiple children?'}] df = pd.DataFrame(d)
原始代码的问题
原始代码仅支持父案件关联0-1个子案件,当父案件有2个及以上子案件时,会因索引重复导致合并失败,无法正确聚合所有子案件数据。
修改后的实现代码
import pandas as pd import numpy as np def get_priority_value(values): # 按Yes>No>Unknown的优先级取值 if 'Yes' in values: return 'Yes' elif 'No' in values: return 'No' else: return 'Unknown' # 1. 分离父案件和子案件 parent_df = df[df['Parent Case #'].isna()].copy().set_index('Case #') child_df = df[df['Parent Case #'].notna()].copy() # 2. 聚合子案件数据:按Parent Case #分组处理 child_agg = child_df.groupby('Parent Case #').agg( Child_Case_List=('Case #', list), Max_Demanded=('Demanded', max), Max_Paid=('Paid', max), A_Values=('A', list), B_Values=('B', list), C_Values=('C', list), D_Values=('D', list), Notes_Combined=('Notes', lambda x: '; '.join([str(note) for note in x if pd.notna(note)])) ).reset_index().rename(columns={'Parent Case #': 'Case #'}) # 3. 合并父案件与聚合后的子案件数据 merged_df = parent_df.merge(child_agg, on='Case #', how='left') # 4. 应用规则计算最终字段 merged_df['Demanded'] = merged_df.apply(lambda row: max(row['Demanded'], row['Max_Demanded']) if pd.notna(row['Max_Demanded']) else row['Demanded'], axis=1) merged_df['Paid'] = merged_df.apply(lambda row: max(row['Paid'], row['Max_Paid']) if pd.notna(row['Max_Paid']) else row['Paid'], axis=1) merged_df['A'] = merged_df.apply(lambda row: get_priority_value([row['A']] + (row['A_Values'] if pd.notna(row['A_Values']) else [])), axis=1) merged_df['B'] = merged_df.apply(lambda row: get_priority_value([row['B']] + (row['B_Values'] if pd.notna(row['B_Values']) else [])), axis=1) merged_df['C'] = merged_df.apply(lambda row: get_priority_value([row['C']] + (row['C_Values'] if pd.notna(row['C_Values']) else [])), axis=1) merged_df['D'] = merged_df.apply(lambda row: get_priority_value([row['D']] + (row['D_Values'] if pd.notna(row['D_Values']) else [])), axis=1) merged_df['Notes'] = merged_df.apply(lambda row: f"{row['Notes']}; {row['Notes_Combined']}" if pd.notna(row['Notes_Combined']) else f"{row['Notes']}; ", axis=1) merged_df['Child_Case #'] = merged_df['Child_Case_List'].fillna(value=np.nan).apply(lambda x: x if isinstance(x, list) else []) # 5. 清理不必要的列,调整顺序 final_df = merged_df.drop(columns=['Max_Demanded', 'Max_Paid', 'A_Values', 'B_Values', 'C_Values', 'D_Values', 'Notes_Combined', 'Child_Case_List']).reset_index() # 输出结果 print(final_df.to_dict('records'))
代码逻辑说明
- 分离父/子案件:将无
Parent Case #的行作为父案件,有值的作为子案件。 - 子案件聚合:按
Parent Case #分组,聚合子案件编号为列表,计算Demanded/Paid的最大值,收集A/B/C/D的所有取值,拼接非空Notes。 - 合并数据:将父案件与聚合后的子案件数据按
Case #左连接,确保所有父案件都保留。 - 规则计算:
- Demanded/Paid:取父案件值与子案件最大值的较大者
- A/B/C/D:将父案件值与子案件所有取值合并后,按优先级返回结果
- Notes:父案件Notes拼接子案件聚合后的Notes,无子案件则添加分号结尾
- Child_Case #:无子案件时设为空列表
- 清理列:移除中间计算用的列,整理最终输出格式。
输出结果
[{'Case #': 'CHI22-01234', 'Parent Case #': nan, 'Name': 'Abraham Lincoln', 'Demanded': 5000.0, 'Paid': 2000.0, 'A': 'Yes', 'B': 'No', 'C': 'Yes', 'D': 'Yes', 'Notes': 'Honest Abe with his stovepipe hat; ', 'Child_Case #': []}, {'Case #': 'CHI22-01237', 'Parent Case #': nan, 'Name': 'Ebenezer Scrooge', 'Demanded': 10000.0, 'Paid': 9500.0, 'A': 'Yes', 'B': 'Yes', 'C': 'No', 'D': 'Yes', 'Notes': 'What a scrooge!; Scrooge has an apartment in NYC', 'Child_Case #': ['NYC21-02222']}, {'Case #': 'NYC22-03333', 'Parent Case #': nan, 'Name': 'Bob Marley', 'Demanded': 1000000.0, 'Paid': 0.0, 'A': 'Yes', 'B': 'Yes', 'C': 'Unknown', 'D': 'Unknown', 'Notes': 'He will send three ghosts; Can it handle multiple children?', 'Child_Case #': ['NYC23-21456', 'NYC23-21457']}, {'Case #': 'PHX21-01234', 'Parent Case #': nan, 'Name': 'Bob Cratchit', 'Demanded': 500000.0, 'Paid': 22000.0, 'A': 'No', 'B': 'Yes', 'C': 'No', 'D': 'No', 'Notes': 'Jr. accountant; Promoted to Sr. accountant 12/14/2021', 'Child_Case #': ['PHX21-01238']}, {'Case #': 'LAX22-12345', 'Parent Case #': nan, 'Name': 'Kim Kardashian', 'Demanded': 50000.0, 'Paid': 30000.0, 'A': 'Yes', 'B': 'Yes', 'C': 'Unknown', 'D': 'Unknown', 'Notes': 'Kim & Kanye for president; Updated data', 'Child_Case #': ['LAX22-12349']}]
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

