返回

Pandas多级索引降级:移除DataFrame层级的实用指南

python

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 的强大之处就在于灵活!