
本教程探讨如何将水平宽度过大的pandas dataframe重塑为更易读的垂直长格式。针对列数能被目标组数整除和不能整除的两种情况,文章详细介绍了使用numpy `reshape`方法进行高效转换,以及利用pandas `multiindex`结合`stack`操作处理复杂场景的专业技巧,旨在帮助用户优化数据结构,提升数据分析效率。
在数据处理和分析中,我们经常会遇到数据以“宽”格式存储的情况,即一个实体的信息横向铺开,占据了大量的列。例如,一个CSV文件可能有数百甚至数千列,这使得数据难以直观阅读和分析。为了更好地进行数据操作和可视化,通常需要将这种宽格式数据转换为“长”格式,使其更符合关系型数据库或某些数据分析工具的要求。本文将详细介绍两种在Pandas中实现这种数据重塑的专业方法:利用NumPy的reshape功能以及结合Pandas MultiIndex和stack操作。
方法一:使用NumPy reshape进行高效重塑
当原始DataFrame的列数能够被目标组的列数整除时,NumPy的reshape方法提供了一种非常高效且简洁的解决方案。这种方法直接作用于底层NumPy数组,避免了Pandas层面的一些开销。
适用场景
- 原始DataFrame的列数是目标每组列数的整数倍。
- 数据类型一致,可以直接转换为NumPy数组。
- 需要将每N列作为一个新的行组。
实现步骤与代码
- 将DataFrame转换为NumPy数组。
- 使用reshape方法将数组重塑为新的形状。其中,-1表示该维度的大小将由NumPy自动计算,以确保总元素数量不变;另一个参数则指定目标每组的列数。
- 将重塑后的NumPy数组转换回Pandas DataFrame,并指定新的列名。
以下是一个具体的示例:
import pandas as pd
import numpy as np
# 假设我们有一个宽格式的DataFrame
# 模拟一个3行12列的DataFrame,列数12可以被6整除
np.random.seed(123)
df = pd.DataFrame(np.random.randint(10, size=(3, 12)))
print("原始DataFrame:")
print(df)
# 原始DataFrame:
# 0 1 2 3 4 5 6 7 8 9 10 11
# 0 2 2 6 1 3 9 6 1 0 1 9 0
# 1 0 9 3 4 0 0 4 1 7 3 2 4
# 2 7 2 4 8 0 7 9 3 4 6 1 5
# 目标列名
target_columns = ['GroupA', 'GroupB', 'GroupC', 'GroupD', 'GroupE', 'GroupF']
group_size = len(target_columns) # 每组6列
# 检查列数是否可被整除
print(f"\n原始DataFrame列数: {len(df.columns)}")
print(f"列数 % {group_size} = {len(df.columns) % group_size}")
if len(df.columns) % group_size == 0:
# 使用NumPy的reshape方法
df_target = pd.DataFrame(df.to_numpy().reshape(-1, group_size),
columns=target_columns)
print("\n重塑后的DataFrame:")
print(df_target)
else:
print("\n列数不能被目标组大小整除,请考虑使用Pandas MultiIndex和stack方法。")
# 重塑后的DataFrame:
# GroupA GroupB GroupC GroupD GroupE GroupF
# 0 2 2 6 1 3 9
# 1 6 1 0 1 9 0
# 2 0 9 3 4 0 0
# 3 4 1 7 3 2 4
# 4 7 2 4 8 0 7
# 5 9 3 4 6 1 5注意事项
- 此方法要求原始DataFrame的列数必须是目标每组列数的整数倍。如果不是,reshape操作将失败,并抛出ValueError: cannot reshape array of size X into shape (Y, Z)错误。
- reshape(-1, group_size)中的-1表示NumPy会自动计算行数,以确保总元素数量不变。
- 这种方法会丢失原始列名信息,因此需要重新指定新的列名。
方法二:利用Pandas MultiIndex和stack处理复杂场景
当原始DataFrame的列数不能被目标组的列数整除时,或者需要更灵活地处理列分组时,结合使用Pandas的MultiIndex和stack操作是一个强大的解决方案。这种方法能够优雅地处理不规则的列分组,并在必要时引入NaN值。
立即学习“Python免费学习笔记(深入)”;
适用场景
- 原始DataFrame的列数不能被目标每组列数整除。
- 需要更灵活的列分组逻辑。
- 可以接受在数据不完整时自动填充NaN。
实现步骤与代码
- 创建两个索引数组:一个用于标识每组内的位置(a % group_size),另一个用于标识组的序号(a // group_size)。
- 使用set_axis将这两个索引数组作为DataFrame的列MultiIndex。
- 使用stack()方法将最内层(即每组内的位置)的列堆叠为行,从而将宽格式转换为长格式。
- 再次使用set_axis重命名转换后的列。
- 使用reset_index(drop=True)清理索引,使其成为默认的整数索引。
以下是一个具体的示例:
import pandas as pd
import numpy as np
# 模拟一个3行10列的DataFrame,列数10不能被6整除
np.random.seed(123)
df = pd.DataFrame(np.random.randint(10, size=(3, 10)))
print("原始DataFrame:")
print(df)
# 原始DataFrame:
# 0 1 2 3 4 5 6 7 8 9
# 0 2 2 6 1 3 9 6 1 0 1
# 1 9 0 0 9 3 4 0 0 4 1
# 2 7 3 2 4 7 2 4 8 0 7
# 目标列名
target_columns = ['GroupA', 'GroupB', 'GroupC', 'GroupD', 'GroupE', 'GroupF']
group_size = len(target_columns) # 每组6列
print(f"\n原始DataFrame列数: {len(df.columns)}")
print(f"列数 % {group_size} = {len(df.columns) % group_size}") # 结果为2,不能整除
# 创建用于MultiIndex的索引数组
# a % group_size: [0, 1, 2, 3, 4, 5, 0, 1, 2, 3] (表示在组内的位置)
# a // group_size: [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] (表示组的序号)
a = np.arange(len(df.columns))
# 设置MultiIndex,然后stack
df_target_multiindex = (df.set_axis([a % group_size, a // group_size], axis=1)
.stack() # 堆叠最内层索引 (即a % group_size)
.set_axis(target_columns, axis=1) # 重命名列
.reset_index(drop=True)) # 重置索引
print("\n重塑后的DataFrame (使用MultiIndex和stack):")
print(df_target_multiindex)
# 重塑后的DataFrame (使用MultiIndex和stack):
# GroupA GroupB GroupC GroupD GroupE GroupF
# 0 2 2 6 1 3.0 9.0
# 1 6 1 0 1 NaN NaN
# 2 9 0 0 9 3.0 4.0
# 3 0 0 4 1 NaN NaN
# 4 7 3 2 4 7.0 2.0
# 5 4 8 0 7 NaN NaN注意事项
- 当原始列数不能被目标组大小整除时,stack操作会自动用NaN填充缺失的值。例如,上述示例中,原始DataFrame有10列,每组6列,那么第一组有6列,第二组有4列。在重塑后,第二组的最后两列(GroupE, GroupF)将填充NaN。
- set_axis用于设置或修改DataFrame的轴标签。在这里,它用于创建列的MultiIndex。
- stack()操作默认会堆叠最内层的列索引。
- 由于stack()操作可能导致数据类型从整数变为浮点数(因为引入了NaN),如果需要,可能需要后续进行数据类型转换。
选择合适的重塑策略
- 优先考虑NumPy reshape: 如果你的数据列数总是目标组大小的精确倍数,并且性能是关键因素,那么NumPy的reshape方法是最佳选择,因为它直接操作底层数组,效率最高。
- 灵活选择Pandas MultiIndex和stack: 如果你的数据列数不总是精确倍数,或者你需要更灵活地处理列分组,那么Pandas的MultiIndex结合stack方法是更健壮的选择。它能够优雅地处理不完整的数据组,并通过NaN值进行标记。
总结
将宽格式的DataFrame重塑为长格式是数据预处理中的常见任务。本文介绍了两种核心策略:当列数完美匹配时,利用NumPy的reshape方法可以实现高效的转换;而当列数不规则时,结合Pandas的MultiIndex和stack操作则提供了更灵活和鲁棒的解决方案。掌握这些技巧将极大地提升你在处理复杂数据结构时的效率和能力,使你的数据分析工作更加顺畅。在实际应用中,根据数据的特性和具体需求选择最适合的方法至关重要。










