
当向 pd.concat 传入字典时,字典的键会自动作为外层索引(列名或多级列索引或分层行索引),具体行为由 axis 参数决定:axis=1 时生成 MultiIndex 列,axis=0 时生成 MultiIndex 行。
当向 `pd.concat` 传入字典时,字典的键会自动作为外层索引(列名或多级列索引或分层行索引),具体行为由 `axis` 参数决定:`axis=1` 时生成 multiindex 列,`axis=0` 时生成 multiindex 行。
pandas.concat 在接收字典作为输入时,并非简单地“拼接数据”,而是将字典的 键(keys) 用作结构化索引的标识符,从而构建带层级的索引体系——这是其区别于列表/元组输入的核心机制。
✅ axis=1:字典键 → 多级列索引(MultiIndex columns)
当 axis=1(按列拼接)时,pd.concat({"z": df}) 中的 "z" 会成为新列索引的顶层标签,原 DataFrame 的列名('Name', 'Age')则作为第二层,共同构成 pd.MultiIndex。因此输出中出现的:
z Name Age 0 tom 10 1 nick 15 2 juli 14
本质上是一个两层列索引:第一层为 'z',第二层为 ['Name', 'Age']。你可以通过 z.columns 验证:
print(z.columns)
# MultiIndex([('z', 'Name'),
# ('z', 'Age')],
# names=[None, None])
? 这种设计在合并多个同构 DataFrame 时极具价值——例如分别处理“训练集”“验证集”“测试集”,可清晰区分来源:
train = df.copy().assign(Age=lambda x: x.Age + 1)
val = df.copy().assign(Age=lambda x: x.Age - 1)
test = df.copy()
result = pd.concat({
'train': train.add_prefix('train_'),
'val': val.add_prefix('val_'),
'test': test.add_prefix('test_')
}, axis=1)
print(result.columns)
# MultiIndex([('train', 'train_Name'), ('train', 'train_Age'),
# ('val', 'val_Name'), ('val', 'val_Age'),
# ('test', 'test_Name'), ('test', 'test_Age')],
# names=[None, None])
✅ axis=0:字典键 → 分层行索引(MultiIndex index)
当 axis=0(按行拼接)时,字典键会成为行索引的第一级,原始索引(如 0, 1, 2)降为第二级:
out = pd.concat({'top': df, 'bottom': df}, axis=0)
print(out.index)
# MultiIndex([('top', 0), ('top', 1), ('top', 2),
# ('bottom', 0), ('bottom', 1), ('bottom', 2)],
# names=[None, None])
这等价于显式使用 keys 参数:
pd.concat([df, df], keys=['top', 'bottom'], axis=0)
⚠️ 注意事项与最佳实践
- 键必须唯一:字典重复键会导致后值覆盖前值,且无警告;
- 避免空字典或含 None 值:pd.concat({}) 报错;含 None 值会引发 TypeError;
- 性能提示:字典输入本质是语法糖,底层仍转换为 keys + list(values) 形式,大数据量下无需刻意优化输入格式;
- 命名索引层级:可通过 names 参数为多级索引指定名称(推荐):
pd.concat({'A': df, 'B': df}, axis=1, names=['dataset', 'field'])
# 列索引变为:MultiIndex(..., names=['dataset', 'field'])
✅ 总结
pd.concat(dict) 的核心逻辑是:字典键驱动索引层级扩展。它不是“把键堆在上面”,而是系统性地将键注入索引结构,实现语义化、可追溯的数据整合。掌握这一机制,能让你更精准地构造分析所需的多维表结构,尤其适用于实验对比、数据版本管理、特征分组等场景。










