
本文旨在深入探讨如何利用Pandas库对DataFrame进行多列堆叠和重塑操作,以实现从宽格式到长格式的转换。我们将介绍三种主要方法:基于MultiIndex的自定义重塑、结合`melt`和`pivot`函数,以及使用`janitor`库中的`pivot_longer`功能。通过示例代码和详细解释,读者将掌握在处理具有特定命名模式(如`*_count`, `*_sum`)的列时,如何高效地将数据结构化,从而提升数据分析的灵活性和可读性。
在数据分析和处理中,我们经常会遇到需要将DataFrame从“宽”格式转换为“长”格式的场景。这种转换对于数据可视化、统计分析以及与特定工具集成至关重要。特别是当DataFrame中的列名遵循某种模式,例如side_metric(如left_count,right_sum),我们希望将这些模式分解,并将side和metric作为新的列。
考虑以下初始DataFrame,它包含日期、其他标识符以及左右两侧的计数和总和数据:
import pandas as pd
df = pd.DataFrame({
'date': ['2023-12-01', '2023-12-05', '2023-12-07'],
'other_col': ['a', 'b', 'c'],
'right_count': [4, 7, 9],
'right_sum': [2, 3, 5],
'left_count': [1, 8, 5],
'left_sum': [0, 8, 4]
})
print("原始DataFrame:")
print(df)原始DataFrame结构如下:
date other_col right_count right_sum left_count left_sum 0 2023-12-01 a 4 2 1 0 1 2023-12-05 b 7 3 8 8 2 2023-12-07 c 9 5 5 4
我们的目标是将其重塑为以下“长”格式,其中side列表示“left”或“right”,count和sum则作为单独的度量列:
date other_col side count sum 0 2023-12-01 a right 4 2 1 2023-12-05 b right 7 3 2 2023-12-07 c right 9 5 3 2023-12-01 a left 1 0 4 2023-12-05 b left 8 8 5 2023-12-07 c left 5 4
下面将介绍几种实现此目标的方法。
方法一:利用MultiIndex进行自定义重塑
这种方法通过创建临时的MultiIndex列来实现灵活的数据重塑,步骤相对复杂但非常灵活。
- 设置索引: 首先,将不需要堆叠的列(如date, other_col)设置为DataFrame的索引。
- 创建MultiIndex列: 使用columns.str.split('_', expand=True)将列名(如right_count)拆分为多级索引(right, count)。
- 重命名轴: rename_axis(columns=['side', None])为新的多级列索引的第一级命名为side,第二级保持匿名。
- 堆叠: stack('side')将名为side的列索引级别转换为行索引。
- 重置索引: reset_index()将所有索引级别转换回普通列。
out_multiindex = (df
.set_index(['date', 'other_col'])
.pipe(lambda x: x.set_axis(x.columns.str.split('_', expand=True), axis=1))
.rename_axis(columns=['side', None])
.stack('side')
.reset_index()
)
print("\n方法一:MultiIndex重塑结果")
print(out_multiindex)输出示例(顺序可能不同):
date other_col side count sum 0 2023-12-01 a left 1 0 1 2023-12-01 a right 4 2 2 2023-12-05 b left 8 8 3 2023-12-05 b right 7 3 4 2023-12-07 c left 5 4 5 2023-12-07 c right 9 5
方法二:结合melt和pivot函数
这种方法分两步进行:首先使用melt将宽格式数据“融化”为长格式,然后使用pivot将其重新组织为所需的结构。
- melt操作: df.melt(['date', 'other_col'], var_name='side')将除date和other_col之外的所有列转换为两列:side(包含原始列名)和value(包含对应的值)。
- 拆分列名: tmp['side'].str.split('_', n=1, expand=True)将side列中的原始列名(如right_count)拆分为side和col两部分。
- pivot操作: tmp.pivot(...)根据date, other_col, side作为新索引,col作为新列,value作为对应值进行透视。
- 清理: reset_index().rename_axis(columns=None)重置索引并清理透视产生的列名轴。
tmp = df.melt(['date', 'other_col'], var_name='temp_col_name')
tmp[['side', 'col']] = tmp['temp_col_name'].str.split('_', n=1, expand=True)
out_melt_pivot = (tmp.pivot(index=['date', 'other_col', 'side'],
columns='col', values='value')
.reset_index()
.rename_axis(columns=None)
)
print("\n方法二:melt + pivot重塑结果")
print(out_melt_pivot)输出示例(顺序可能不同):
date other_col side count sum 0 2023-12-01 a left 1 0 1 2023-12-01 a right 4 2 2 2023-12-05 b left 8 8 3 2023-12-05 b right 7 3 4 2023-12-07 c left 5 4 5 2023-12-07 c right 9 5
方法三:使用janitor库的pivot_longer
janitor是一个强大的Python库,提供了许多便捷的数据清洗和处理功能,其中包括pivot_longer,它旨在简化类似R语言tidyr包中的pivot_longer操作。这种方法通常更简洁、易读。
首先,确保安装了pyjanitor库:
pip install pyjanitor
然后,导入并使用pivot_longer:
import janitor
out_janitor = df.pivot_longer(
index=['date', 'other_col'],
names_to=('side', '.value'),
names_pattern=r'([^_]+)_([^_]+)'
)
print("\n方法三:janitor.pivot_longer重塑结果")
print(out_janitor)关键参数解释:
- index: 指定作为新DataFrame索引的列(不参与堆叠的列)。
- names_to: 一个元组,用于指定如何将原始列名解析为新的列。
- 'side':匹配正则表达式捕获组中的第一个部分,并将其命名为side列。
- '.value':一个特殊关键字,表示匹配正则表达式捕获组中的剩余部分,并将其作为新的列名(如count和sum)。
- names_pattern: 一个正则表达式,用于从原始列名中提取信息。r'([^_]+)_([^_]+)'表示匹配一个或多个非下划线字符(第一个捕获组),后跟一个下划线,再后跟一个或多个非下划线字符(第二个捕获组)。
输出示例:
date other_col side count sum 0 2023-12-01 a right 4 2 1 2023-12-05 b right 7 3 2 2023-12-07 c right 9 5 3 2023-12-01 a left 1 0 4 2023-12-05 b left 8 8 5 2023-12-07 c left 5 4
总结与注意事项
-
选择方法:
- MultiIndex方法:提供最大的灵活性,适用于列名模式复杂或需要精细控制中间步骤的场景。但代码可读性相对较低。
- melt + pivot方法:是Pandas内置的经典重塑组合,逻辑清晰,适用于大多数通用场景。理解melt和pivot的工作原理是掌握Pandas数据重塑的关键。
- janitor.pivot_longer方法:对于有规律的列名模式,此方法最为简洁和直观,大大提高了代码的可读性和编写效率。强烈推荐在日常数据处理中使用。
- 性能考量: 对于非常大的DataFrame,不同的重塑方法可能在性能上有所差异。通常,Pandas的内置函数经过高度优化,而外部库如janitor也会利用这些优化。在极端性能敏感的场景下,可以进行基准测试。
- 列名模式: 所有方法都依赖于对原始列名模式的理解和解析。确保正则表达式(在janitor方法中)或str.split(在Pandas内置方法中)能够准确地从列名中提取所需的部分。
- 数据类型: 重塑操作可能会改变列的数据类型,特别是当melt操作将不同类型的列值合并到value列时。在后续分析前,可能需要进行数据类型转换。
掌握这些Pandas数据重塑技术,将使您能够更有效地组织和分析数据,为后续的数据探索、建模和可视化奠定坚实基础。










