Pandas多级索引降级:移除DataFrame层级的实用指南
2025-03-23 04:01:41
Pandas 多级索引降级:如何移除 DataFrame 中的层级?
遇到多层级列索引,想要砍掉其中一层,咋整?就像下面这种情况,我有个 DataFrame:
import pandas as pd
cols = pd.MultiIndex.from_tuples([("a", "b"), ("a", "c")])
df = pd.DataFrame([[1,2], [3,4]], columns=cols)
print(df)
输出长这样:
a
---+---
b | c
--+---+---
0 | 1 | 2
1 | 3 | 4
我想把 'a' 这一层给去掉,变成下面这样:
b | c
--+---+---
0 | 1 | 2
1 | 3 | 4
该怎么操作呢?这问题的关键是,理解多级索引的工作机制,并找到合适的方法进行操作。
为啥会产生多级索引?
Pandas 搞出多级索引(MultiIndex),主要是为了在二维的 DataFrame 里也能表示更高维度的数据。 啥意思?就是用不同的层级来代表数据的不同分组或者类别。常见场景比如:
- 数据聚合: 使用
groupby()
后再unstack()
,或者用pivot_table()
,都可能搞出多级索引。 - 多维数据: 数据本身就有多个维度,比如时间序列数据,可能有“年”、“月”、“日”等多级。
- 手动创建: 就像开头给的例子那样,直接用
pd.MultiIndex.from_tuples()
或其他方法创建。
知道咋来的,就好办了。下面说怎么解决。
解决方案:砍掉那一层!
好几种方法都能实现,根据具体情况选择顺手的就行。
1. droplevel()
方法:简单粗暴
Pandas DataFrame 和 Index 对象都有 droplevel()
方法,专门用来移除层级。
原理: droplevel()
接收一个参数,指定要移除的层级。可以按层级名称(字符串)或层级编号(整数,从 0 开始)来指定。
代码示例:
import pandas as pd
# 假设 df 是开头那个有多级索引的 DataFrame
new_df = df.droplevel(level=0, axis=1) # 移除列索引的第 0 层
print(new_df)
# 或者用层级名称: new_df = df.droplevel(level='a', axis=1)
说明:
level=0
: 表示要移除第 0 层(最上面那层)。axis=1
: 表示是对列索引操作 (如果是行索引,axis就得=0)。
2. 直接重新赋值 columns: 暴力美学
如果降级后的索引正好是剩下的那一层,可以直接把 df.columns
赋值给它。
原理: 直接覆盖原来的多级索引。
代码示例:
import pandas as pd
#假设df同上。
df.columns = df.columns.droplevel(0)
print(df)
或者:
import pandas as pd
#假设df同上。
df.columns = df.columns.get_level_values(1) # 获取第1层('b'和'c')作为新的列索引
print(df)
说明:
- 第一段代码的意思是, 将df的columns用
droplevel(0)
去除掉第一层。 - 第二段代码
df.columns.get_level_values(1)
更直接,取第1层的值,直接当做columns。
3. reset_index()
+ drop()
: 行索引也想动?
如果多层索引出现在行索引上,或者想把某一列变成普通列,可以用这个组合拳。
原理: reset_index()
把索引变成普通列,drop()
负责丢弃不要的列。
代码示例:
假设我们有一个行索引多层级的DataFrame:
index = pd.MultiIndex.from_tuples([('x', 'one'), ('x', 'two'), ('y', 'one'), ('y', 'two')], names=['first', 'second'])
df_multi_row = pd.DataFrame({'A': [1, 2, 3, 4], 'B': [5, 6, 7, 8]}, index=index)
print(df_multi_row)
输出为:
A B
first second
x one 1 5
two 2 6
y one 3 7
two 4 8
要去掉'first'层级,变成这样:
A B
second
one 1 5
two 2 6
one 3 7
two 4 8
可以这样做:
df_reset = df_multi_row.reset_index(level='first', col_level=1) # 将'first'层级转成列,并且给这列也创建一个顶层列索引,随便起个名字.
df_reset.columns = ['' if col[0] == 'first' else col[1] for col in df_reset.columns ] #把刚创建的顶层索引列名都改成空字符串。
print(df_reset)
或者直接这样, 会自动丢弃顶层列索引:
df_reset = df_multi_row.reset_index(level='first').drop(columns='first')
print(df_reset)
说明:
- 对于列索引的操作,可以在重置索引时设置参数。
- 通过修改
columns
或者增加drop
来实现删除的效果.
4. 进阶:get_level_values()
+ 自定义
get_level_values()
可以获取指定层级的值。想要更灵活的组合,可以用它。
原理: 提取特定层级的值,再进行组合或修改,最后赋值给 df.columns
。
代码示例:
import pandas as pd
# 假设 df 是开头那个有多级索引的 DataFrame
new_columns = df.columns.get_level_values(1) # 获取第 1 层的索引值 ('b', 'c')
df.columns = new_columns
print(df)
假如你想做更复杂的列操作:
假设你有如下DataFrame:
value
first second third
bar one A 1.45
B 6.23
two A 2.33
B 9.01
baz one A 7.89
B 3.14
two A 5.27
B 1.78
你想将second 和 third 两层合并为一层,并用'_' 连接:
import pandas as pd
index = pd.MultiIndex.from_tuples([('bar', 'one', 'A'),
('bar', 'one', 'B'),
('bar', 'two', 'A'),
('bar', 'two', 'B'),
('baz', 'one', 'A'),
('baz', 'one', 'B'),
('baz', 'two', 'A'),
('baz', 'two', 'B')],
names=['first', 'second', 'third'])
df = pd.DataFrame({'value': [1.45, 6.23,2.33,9.01,7.89,3.14,5.27,1.78]}, index=index)
new_index = df.index.get_level_values(1) + '_' + df.index.get_level_values(2)
df.index = new_index
df = df.reset_index() #必须重置,不然会变成新的多层索引
df = df.rename(columns = {'index':'second_third'})
print(df)
结果如下:
second_third value
0 one_A 1.45
1 one_B 6.23
2 two_A 2.33
3 two_B 9.01
4 one_A 7.89
5 one_B 3.14
6 two_A 5.27
7 two_B 1.78
进阶技巧 :通过合理组合get_level_values
的结果进行复杂的列修改, 如上所示.
总结
Pandas 多级索引降级,方法不少。根据具体的需求和场景,灵活选择就好。一定要理解每种方法背后的原理,这样用起来才会稳。记住:Pandas 的强大之处就在于灵活!