
本文介绍一种基于 `groupby().cumcount()` 辅助合并的技巧,将两个含重复分类标签的 dataframe 按“类别内行序”对齐拼接,生成结构清晰、适合 streamlit 等前端直接渲染的宽格式报告表。
在构建分析型报表(尤其是面向非技术用户的展示场景,如 Streamlit 应用)时,常需将多个来源的同类数据横向对齐呈现——但传统 merge(基于键值等值连接)或 concat(简单堆叠)均无法满足「按 class 分组、再按组内出现顺序逐行配对」的需求。例如:df1 和 df2 均含多条 class='A' 记录,我们希望 df1 的第 1 条 A 类与 df2 的第 1 条 A 类同处一行,第 2 条 A 类仅 df1 存在则右侧留空,以此类推。
核心思路是:为每个 DataFrame 的 'class' 列添加组内序号(即 cumcount()),将其作为联合键的一部分参与外连接,从而实现“分组对齐 + 行序对齐”的双重效果。
以下是完整可运行示例:
import pandas as pd
# 构造示例数据
data1 = {'class': ['A', 'A', 'B', 'X'],
'item': ['_1', '_2', '_3', '_4'],
'value': [10, 11, 12, 13]}
data2 = {'class': ['A', 'B', 'B', 'C'],
'item': ['_5', '_6', '_7', '_8'],
'value': [20, 21, 22, 23]}
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
# 关键步骤:使用 cumcount 生成组内序号,并作为 merge 键
out = (df1.merge(df2,
how='outer',
left_on=['class', df1.groupby('class').cumcount()],
right_on=['class', df2.groupby('class').cumcount()],
suffixes=('_1', '_2'))
.sort_values('class')
.drop(columns='key_1') # 自动产生的冗余键列(因 cumcount 结果被命名为 key_1/key_2)
.reset_index(drop=True))执行后得到目标结构:
class item_1 value_1 item_2 value_2 0 A _1 10.0 _5 20.0 1 A _2 11.0 NaN NaN 2 B _3 12.0 _6 21.0 3 B NaN NaN _7 22.0 4 C NaN NaN _8 23.0 5 X _4 13.0 NaN NaN
✅ 注意事项与优化建议:
- cumcount() 默认从 0 开始编号,确保两表组内顺序逻辑一致;若需从 1 起始,可改用 cumcount() + 1;
- how='outer' 保证所有 class 和所有行序组合均被保留(缺失项自动填充 NaN),符合报表完整性要求;
- 若原始数据未按 class 排序,建议提前 df1.sort_values(['class']).reset_index(drop=True),避免 cumcount 顺序错乱;
- 对于更多源 DataFrame(如 df3、df4),可链式 merge 或封装为函数循环处理,每次 merge 后重置 cumcount 逻辑;
- 最终列名可通过 rename() 进一步美化(如 'item_1' → 'Source_A_Item'),提升可读性。
该方法不依赖自定义循环或复杂索引操作,完全基于 Pandas 原生 API,兼具简洁性、可读性与工程鲁棒性,是构建高质量对齐报表的理想实践。










