使用 MultiIndex 进行 stack 和 unstack
使用 stack() 将最内层列级别旋转到行索引中,并使用 unstack() 还原。
使用 MultiIndex 进行 stack 和 unstack 是 CoddyKit 上的免费 Pandas & NumPy Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Pandas & NumPy Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Pandas & NumPy Academy 课程共包含 4 节课。
stack 和 unstack 简介
stack() 和 unstack() 是 Pandas 中用于重塑数据的工具,可以在行索引和列索引之间移动数据。当 DataFrames 的任一轴上包含 MultiIndex 时,它们尤其有用。stack() 会将最内层的列级别旋转到最内层的行级别,而 unstack() 执行相反的操作。
stack():列转行
DataFrame.stack() 会将列标签的最内层级别转入行索引的最内层级别,从而生成更长、更窄的结果。如果原始 DataFrame 使用简单的(非多级)列,结果将是带有 MultiIndex 的 Series。如果列包含多个级别,stack() 会使列级别减少一个。
import pandas as pd
df = pd.DataFrame({
'Math': [85, 90, 78],
'Science': [92, 88, 95]
}, index=['Alice', 'Bob', 'Carol'])
print(df)
# Math Science
# Alice 85 92
# Bob 90 88
# Carol 78 95
stacked = df.stack()
print(stacked)
# Alice Math 85
# Science 92
# Bob Math 90
# Science 88
# Carol Math 78
# Science 95
# dtype: int64unstack():行转列
Series.unstack()(或 DataFrame.unstack())是 stack() 的逆操作。它会将行索引的最内层级别旋转为新的列标签,从而生成更宽的结果。从功能上看,它与 pivot() 类似,但直接作用于索引,而不是列值。
stacked = df.stack()
print(type(stacked)) # Series with MultiIndex
# Restore the original wide format
df_restored = stacked.unstack()
print(df_restored)
# Math Science
# Alice 85 92
# Bob 90 88
# Carol 78 95
# Identical to the original df!选择要 stack 的级别
对于列上包含 MultiIndex 的 DataFrames,stack(level) 允许您选择要旋转的级别。您可以传入级别编号(0 表示最外层,-1 表示最内层,也是默认值),或传入级别名称。同样,unstack(level) 可以选择要旋转到列中的行索引级别。要处理复杂的层级数据结构,这种精细的控制必不可少。
# MultiIndex columns
arrays = [['Math', 'Math', 'Science', 'Science'],
['Q1', 'Q2', 'Q1', 'Q2']]
multi_cols = pd.MultiIndex.from_arrays(arrays, names=['subject', 'quarter'])
df_multi = pd.DataFrame([[85, 90, 92, 88], [78, 80, 95, 91]],
index=['Alice', 'Bob'], columns=multi_cols)
# Stack the 'quarter' level (innermost, level=-1)
print(df_multi.stack(level='quarter').head())对特定行级别使用 unstack()
当 DataFrame 的行上包含 MultiIndex 时(对多列执行 groupby() 后经常会出现这种情况),您可以对特定的行级别执行 unstack(),将其展开到各列中。这是构建交叉表式汇总结果的一种常见模式,无需显式调用 pivot_table()。
# GroupBy produces MultiIndex rows
df2 = pd.DataFrame({
'region': ['East', 'East', 'West', 'West'],
'product': ['A', 'B', 'A', 'B'],
'sales': [100, 150, 120, 200]
})
# MultiIndex result from groupby
multi = df2.groupby(['region', 'product'])['sales'].sum()
print(multi)
# Unstack the product level into columns
wide = multi.unstack('product')
print(wide)
# product A B
# region
# East 100 150
# West 120 200处理 stack/unstack 中的缺失值
调用 unstack() 时,如果并非每个列级别都存在所有行索引组合,Pandas 会使用 NaN 填充缺失单元格。相反,stack() 默认会删除全部为 NaN 的行。您可以通过 dropna 参数控制此行为:传入 stack(dropna=False) 可保留包含 NaN 的行。
# Incomplete data: West has no product B
df3 = df2[df2['product'] != 'B'].copy()
multi3 = df3.groupby(['region', 'product'])['sales'].sum()
wide3 = multi3.unstack('product', fill_value=0)
print(wide3)
# product A B
# region
# East 100 0 <- B filled with 0 instead of NaN
# West 120 0先 stack(),再对行进行计算
一种强大的模式是:先对宽格式 DataFrame 执行 stack(),将列转换为行;然后应用筛选或 groupby 等按行操作;最后执行 unstack(),恢复为宽格式。这样可以避免编写逐列循环,同时让代码保持向量化且易于阅读。对于同时对每一列应用相同转换的场景,这种方法尤其有用。
# Normalise each column by its column mean using stack/compute/unstack
normalised = (
df.stack()
.groupby(level=1)
.transform(lambda s: (s - s.mean()) / s.std())
.unstack()
)
print(normalised.round(2))
# Math Science
# Alice 0.0 0.39
# Bob 1.13 -1.09
# Carol -1.13 0.71使用 stack() 绘图
与 melt() 一样,stack() 会将数据转换为长格式,而 Seaborn 和许多 Matplotlib 辅助工具都需要这种格式。主要区别在于,stack() 作用于列索引并保留 MultiIndex 结构,而 melt() 会生成扁平的长格式 DataFrame。两者都能实现类似的绘图流程。
# Prepare data for line plot using stack
long = df.stack().reset_index()
long.columns = ['student', 'subject', 'score']
print(long)
# student subject score
# 0 Alice Math 85
# 1 Alice Science 92
# ...
# Ready for: sns.barplot(data=long, x='student', y='score', hue='subject')使用 swaplevel() 重新排列索引
执行 stack 后,最内层的行级别就是原始列名。有时,您会希望外层级别是变量名,内层级别是原始行索引。请对 MultiIndex 使用 swaplevel() 交换两个级别的顺序,然后使用 sort_index() 恢复整洁的排序。
stacked = df.stack() # MultiIndex: (student, subject)
swapped = stacked.swaplevel() # MultiIndex: (subject, student)
swapped = swapped.sort_index()
print(swapped)
# subject student
# Math Alice 85
# Bob 90
# Carol 78
# Science Alice 92
# Bob 88
# Carol 95何时使用 stack、melt 和 pivot
处理列包含 MultiIndex 的 DataFrames,并希望将列级别减少一个时,请选择 stack()。拥有扁平 DataFrame,并希望在控制哪些列转换为行的同时将宽格式转换为长格式时,请选择 melt()。要将长格式转换回宽格式,请选择 pivot()/pivot_table()。这三个工具涵盖了 Pandas 中所有宽格式与长格式的重塑需求。
# Decision guide (comment, not executable standalone):
# MultiIndex columns -> want fewer levels: use stack()
# Flat wide -> need long format for plotting/ML: use melt()
# Long -> need wide summary table: use pivot_table()
# Wide -> back to long: use melt() or stack()
# Example: stack when you have pivot_table output (MultiIndex cols)
tbl = df2.groupby(['region', 'product'])['sales'].sum().unstack()
long_again = tbl.stack().rename('sales').reset_index()
print(long_again)实用总结:重塑速查表
可以这样简要理解:stack()——列合并到行中,DataFrame 变得更窄、更高。unstack()——行展开为列,DataFrame 变得更宽、更矮。melt()——命名列合并为键值对(两个新列)。pivot_table()——键值列展开为多个列。这四种操作都可以逆向执行;选择方向时,只需判断目标分析更适合宽格式还是长格式。
# stack/unstack cycle
df_wide = df.copy() # wide format
df_long = df_wide.stack() # long via stack
df_wide2 = df_long.unstack() # back to wide
# melt/pivot cycle
df_melted = df_wide.melt(id_vars=[]) # wide -> long
# df_pivoted = df_melted.pivot(...) # long -> wide
print('Original shape: ', df_wide.shape)
print('After stack: ', df_long.shape)
print('After unstack: ', df_wide2.shape)快速检查
请测试您对本课所讲使用 MultiIndex 的 stack 和 unstack 的理解。
课程回顾
本课中,您学习了以下内容:stack() 将列标签旋转到行索引中,生成更长、更窄的结果;unstack() 执行相反操作,将行索引级别旋转为列;两者都可以处理 groupby 操作生成的 MultiIndex 结构;swaplevel() 可以重新排列索引级别。接下来,我们将学习使用 pd.crosstab() 快速生成分类列之间的频数表。
用 AI 导师学习 Python — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 30
- 课程
- 120
常见问题解答
「使用 MultiIndex 进行 stack 和 unstack」课时是免费的吗?
是的 — 「使用 MultiIndex 进行 stack 和 unstack」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Pandas & NumPy Academy 课程的其余内容,请升级到 CoddyKit PRO。 Pandas & NumPy Academy 课程共包含 4 节课。
「使用 MultiIndex 进行 stack 和 unstack」这节课中我会学到什么?
使用 stack() 将最内层列级别旋转到行索引中,并使用 unstack() 还原。 你通过在浏览器中直接运行的动手代码来练习 Pandas & NumPy Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Pandas & NumPy Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Pandas & NumPy Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「使用 MultiIndex 进行 stack 和 unstack」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Pandas & NumPy Academy 课中编写并运行代码吗?
能。每节 Pandas & NumPy Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- pivot_table:交叉表
- melt:从宽格式转换为长格式
- 使用 MultiIndex 进行 stack 和 unstack
- 使用 crosstab 创建频数表