Pandas df.unstack() 函数
df.unstack() 是 DataFrame 的成员方法,用于将行索引转换为列索引。它将具有层次化索引的数据"展开",将行级别的一个或多个层级移动到列中。
这是数据重塑的重要操作,通常与 stack() 配合使用,实现数据的互转。
单词释义: unstack 意为"拆开、展开",在这里指将行索引"展开"为列,将长表转为宽表。
基本语法与参数
df.unstack() 是 DataFrame 的实例方法,通过点运算符调用。
语法格式
DataFrame.unstack(level=-1, fill_value=None)
参数说明
- 参数:
level- 类型: int、str、int 列表或 str 列表。
- 描述: 指定要展开到列的层级。对于多层索引,可以指定具体的层级编号(从0开始)或层级名称。默认为 -1,表示最内层。
- 参数:
fill_value- 类型: 标量或 None。
- 描述: 用于填充因 unstack 操作产生的缺失值的数值。默认为 None,保留 NaN。
函数说明
- 返回值: 返回一个 Series 或 DataFrame。如果原始 DataFrame 只有一个行层级被展开,返回 Series;否则返回 DataFrame。
- 效果: 将行索引的一个或多个层级移动到列索引,产生更宽的数据格式。展开后的数据是"宽格式",便于查看和比较。
实例
让我们通过一系列从简单到复杂的例子,彻底掌握 df.unstack() 的用法。
示例 1:基础用法 - 将行转为列
实例
import pandas as pd
# 1. 创建长格式数据
df = pd.DataFrame({
'product': ['A', 'A', 'B', 'B', 'C', 'C'],
'region': ['North', 'South', 'North', 'South', 'North', 'South'],
'sales': [100, 150, 200, 180, 220, 250]
})
print("=== 原始长格式数据 ===")
print(df)
# 2. 设置层次化索引
df_indexed = df.set_index(['product', 'region'])
print("n=== 设置层次化索引后 ===")
print(df_indexed)
# 3. 使用 df.unstack() 将内层索引转为列
unstacked = df_indexed.unstack()
print("n=== df.unstack() 结果 ===")
print(unstacked)
# 4. 简化查看
print("n=== 只看销售额 ===")
print(unstacked['sales'])
# 1. 创建长格式数据
df = pd.DataFrame({
'product': ['A', 'A', 'B', 'B', 'C', 'C'],
'region': ['North', 'South', 'North', 'South', 'North', 'South'],
'sales': [100, 150, 200, 180, 220, 250]
})
print("=== 原始长格式数据 ===")
print(df)
# 2. 设置层次化索引
df_indexed = df.set_index(['product', 'region'])
print("n=== 设置层次化索引后 ===")
print(df_indexed)
# 3. 使用 df.unstack() 将内层索引转为列
unstacked = df_indexed.unstack()
print("n=== df.unstack() 结果 ===")
print(unstacked)
# 4. 简化查看
print("n=== 只看销售额 ===")
print(unstacked['sales'])
运行结果预期:
=== 原始长格式数据 ===
product region sales
0 A North 100
1 A South 150
2 B North 200
3 B South 180
4 C North 220
5 C South 250
n=== 设置层次化索引后 ===
sales
product region
A North 100
South 150
B North 200
South 180
C North 220
South 250
n=== df.unstack() 结果 ===
sales
region North South
product
A 100 150
B 200 180
C 220 250
n=== 只看销售额 ===
region North South
product
A 100 150
B 200 180
C 220 250
代码解析:
- 原始数据是长格式,每行记录一个产品在某地区的销售额。
- 使用
set_index()创建层次化索引(product 为外层,region 为内层)。 unstack()将内层索引 region 移动到列位置。- 结果是宽格式,可以直观比较不同产品在不同地区的销售数据。
示例 2:多层索引的展开
当 DataFrame 有多层行索引时,可以指定要展开的层级。
实例
import pandas as pd
# 1. 创建具有多层行索引的数据
df = pd.DataFrame({
'year': [2023, 2023, 2023, 2023, 2024, 2024, 2024, 2024],
'quarter': ['Q1', 'Q1', 'Q2', 'Q2', 'Q1', 'Q1', 'Q2', 'Q2'],
'product': ['A', 'B', 'A', 'B', 'A', 'B', 'A', 'B'],
'sales': [100, 150, 200, 180, 220, 250, 230, 270]
})
print("=== 原始数据 ===")
print(df)
# 2. 设置多层索引
df_indexed = df.set_index(['year', 'quarter', 'product'])
print("n=== 设置多层索引 ===")
print(df_indexed)
# 3. 展开最内层(product,默认 level=-1)
print("n=== 展开 product 层(默认)===")
unstacked_inner = df_indexed.unstack()
print(unstacked_inner['sales'])
# 4. 展开中间层(quarter)
print("n=== 展开 quarter 层 ===")
unstacked_quarter = df_indexed.unstack(level='quarter')
print(unstacked_quarter['sales'])
# 5. 展开外层(year)
print("n=== 展开 year 层 ===")
unstacked_year = df_indexed.unstack(level='year')
print(unstacked_year['sales'])
# 6. 同时展开多个层级
print("n=== 同时展开 year 和 product ===")
unstacked_multi = df_indexed.unstack(level=['year', 'product'])
print(unstacked_multi['sales'])
# 1. 创建具有多层行索引的数据
df = pd.DataFrame({
'year': [2023, 2023, 2023, 2023, 2024, 2024, 2024, 2024],
'quarter': ['Q1', 'Q1', 'Q2', 'Q2', 'Q1', 'Q1', 'Q2', 'Q2'],
'product': ['A', 'B', 'A', 'B', 'A', 'B', 'A', 'B'],
'sales': [100, 150, 200, 180, 220, 250, 230, 270]
})
print("=== 原始数据 ===")
print(df)
# 2. 设置多层索引
df_indexed = df.set_index(['year', 'quarter', 'product'])
print("n=== 设置多层索引 ===")
print(df_indexed)
# 3. 展开最内层(product,默认 level=-1)
print("n=== 展开 product 层(默认)===")
unstacked_inner = df_indexed.unstack()
print(unstacked_inner['sales'])
# 4. 展开中间层(quarter)
print("n=== 展开 quarter 层 ===")
unstacked_quarter = df_indexed.unstack(level='quarter')
print(unstacked_quarter['sales'])
# 5. 展开外层(year)
print("n=== 展开 year 层 ===")
unstacked_year = df_indexed.unstack(level='year')
print(unstacked_year['sales'])
# 6. 同时展开多个层级
print("n=== 同时展开 year 和 product ===")
unstacked_multi = df_indexed.unstack(level=['year', 'product'])
print(unstacked_multi['sales'])
运行结果预期:
=== 原始数据 ===
year quarter product sales
0 2023 Q1 A 100
1 2023 Q1 B 150
2 2023 Q2 A 200
3 2023 Q2 B 180
4 2024 Q1 A 220
5 2024 Q1 B 250
6 2024 Q2 A 230
7 2024 Q2 B 270
n=== 设置多层索引 ===
sales
year quarter product
2023 Q1 A 100
B 150
Q2 A 200
B 180
2024 Q1 A 220
B 250
Q2 A 230
B 270
n=== 展开 product 层(默认)===
product A B
year quarter
2023 Q1 100 150
Q2 200 180
2024 Q1 220 250
Q2 230 270
n=== 展开 quarter 层 ===
quarter Q1 Q2
year product
2023 A 100 200
B 150 180
2024 A 220 230
B 250 270
n=== 展开 year 层 ===
year 2023 2024
quarter product
Q1 A 100 220
B 150 250
Q2 A 200 230
B 180 270
n=== 同时展开 year 和 product ===
year 2023 2024
product A B A B
quarter
Q1 100 150 220 250
Q2 200 180 230 270
代码解析:
- 多层索引的 DataFrame,可以使用
level参数指定展开哪个层级。 - 默认
level=-1展开最内层(product)。 - 展开不同层级会产生不同的数据布局,便于从不同角度分析数据。
示例 3:使用 fill_value 处理缺失值
使用 fill_value 参数可以填充 unstack 后产生的缺失值。
实例
import pandas as pd
import numpy as np
# 1. 创建不完整的数据(不是所有产品-地区组合都有数据)
df = pd.DataFrame({
'product': ['A', 'A', 'B', 'C'],
'region': ['North', 'South', 'North', 'East'],
'sales': [100, 150, 200, 220]
})
print("=== 原始数据(不完整)===")
print(df)
# 2. 创建层次化索引并 unstack
df_indexed = df.set_index(['product', 'region'])
print("n=== 层次化索引 ===")
print(df_indexed)
# 3. 不填充缺失值
print("n=== 不填充缺失值 ===")
unstacked = df_indexed.unstack()
print(unstacked)
print(f"缺失值数量: {unstacked['sales'].isna().sum().sum()}")
# 4. 使用 fill_value=0 填充缺失值
print("n=== fill_value=0 ===")
unstacked_fill = df_indexed.unstack(fill_value=0)
print(unstacked_fill)
print(f"缺失值数量: {unstacked_fill['sales'].isna().sum().sum()}")
import numpy as np
# 1. 创建不完整的数据(不是所有产品-地区组合都有数据)
df = pd.DataFrame({
'product': ['A', 'A', 'B', 'C'],
'region': ['North', 'South', 'North', 'East'],
'sales': [100, 150, 200, 220]
})
print("=== 原始数据(不完整)===")
print(df)
# 2. 创建层次化索引并 unstack
df_indexed = df.set_index(['product', 'region'])
print("n=== 层次化索引 ===")
print(df_indexed)
# 3. 不填充缺失值
print("n=== 不填充缺失值 ===")
unstacked = df_indexed.unstack()
print(unstacked)
print(f"缺失值数量: {unstacked['sales'].isna().sum().sum()}")
# 4. 使用 fill_value=0 填充缺失值
print("n=== fill_value=0 ===")
unstacked_fill = df_indexed.unstack(fill_value=0)
print(unstacked_fill)
print(f"缺失值数量: {unstacked_fill['sales'].isna().sum().sum()}")
运行结果预期:
=== 原始数据(不完整)===
product region sales
0 A North 100
1 A South 150
2 B North 200
3 C East 220
n=== 层次化索引 ===
sales
product region
A North 100
South 150
B North 200
C East 220
n=== 不填充缺失值 ===
sales
region East North South
product
A NaN 100.0 150.0
B NaN 200.0 NaN
C 220.0 NaN NaN
缺失值数量: 5
n=== fill_value=0 ===
sales
region East North South
product
A 0 100 150
B 0 200 0
C 220 0 0
缺失值数量: 0
代码解析:
- 当原始数据不完整时,unstack 会产生缺失值(NaN)。
- 使用
fill_value参数可以指定填充值,避免后续计算出错。 - 通常填充为 0 表示"无数据"或"不存在该组合"。
示例 4:unstack 与 stack 的配合使用
unstack() 和 stack() 是互逆操作,配合使用可以实现数据的灵活重塑。
实例
import pandas as pd
# 1. 创建初始宽格式数据
df = pd.DataFrame({
'product': ['A', 'B', 'C'],
'North': [100, 150, 200],
'South': [180, 170, 160],
'East': [190, 200, 210]
})
print("=== 原始宽格式数据 ===")
print(df)
# 2. 先 stack 转为长格式
print("n=== step 1: stack 转为长格式 ===")
stacked = df.set_index('product').stack()
print(stacked)
# 3. 再 unstack 回来(验证可逆性)
print("n=== step 2: unstack 转回宽格式 ===")
restored = stacked.unstack()
print(restored)
# 4. 实战:重新排列数据
print("n=== 实战:将 region 移到列 ===")
# 原始:product 为行索引,region 为列
# 目标:region 为行索引,product 为列
result = df.set_index('product').unstack().unstack(level=-1)
print(result)
# 5. 使用 reorder_levels 调整层级顺序
print("n=== 使用 reorder_levels 调整层级 ===")
# 创建双层索引
df_multi = pd.DataFrame(
[[100, 200], [150, 180], [190, 210]],
index=pd.MultiIndex.from_tuples([
('A', 'North'), ('B', 'North'), ('C', 'North')
], names=['product', 'region']),
columns=['2023', '2024']
)
print("原始数据:")
print(df_multi)
# 调整层级顺序
print("n调整后(年份在外层):")
print(df_multi.unstack(level='region'))
# 1. 创建初始宽格式数据
df = pd.DataFrame({
'product': ['A', 'B', 'C'],
'North': [100, 150, 200],
'South': [180, 170, 160],
'East': [190, 200, 210]
})
print("=== 原始宽格式数据 ===")
print(df)
# 2. 先 stack 转为长格式
print("n=== step 1: stack 转为长格式 ===")
stacked = df.set_index('product').stack()
print(stacked)
# 3. 再 unstack 回来(验证可逆性)
print("n=== step 2: unstack 转回宽格式 ===")
restored = stacked.unstack()
print(restored)
# 4. 实战:重新排列数据
print("n=== 实战:将 region 移到列 ===")
# 原始:product 为行索引,region 为列
# 目标:region 为行索引,product 为列
result = df.set_index('product').unstack().unstack(level=-1)
print(result)
# 5. 使用 reorder_levels 调整层级顺序
print("n=== 使用 reorder_levels 调整层级 ===")
# 创建双层索引
df_multi = pd.DataFrame(
[[100, 200], [150, 180], [190, 210]],
index=pd.MultiIndex.from_tuples([
('A', 'North'), ('B', 'North'), ('C', 'North')
], names=['product', 'region']),
columns=['2023', '2024']
)
print("原始数据:")
print(df_multi)
# 调整层级顺序
print("n调整后(年份在外层):")
print(df_multi.unstack(level='region'))
运行结果预期:
=== 原始宽格式数据 === product North South East 0 A 100 180 190 1 B 150 170 ...
代码解析:
stack()和unstack()是一对互逆操作。- 先 stack 再 unstack,理论上可以恢复到原始形状(忽略填充值差异)。
- 可以通过不同的 level 组合实现不同的数据布局。
- 在实际数据分析中,经常需要反复调整数据形状以适应不同的分析需求。
提示:
unstack()和stack()是数据重塑的核心工具。unstack()将行"拆"到列中(长转宽),stack()将列"堆"到行中(宽转长)。

Pandas 常用函数