How to remove levels from a multi-indexed dataframe?

For example, I have:

In [1]: df = pd.DataFrame([8, 9],
index=pd.MultiIndex.from_tuples([(1, 1, 1),
(1, 3, 2)]),
columns=['A'])


In [2] df
Out[2]:
A
1 1 1  8
3 2  9

Is there a better way to remove the last level from the index than this:

In [3]: pd.DataFrame(df.values,
index=df.index.droplevel(2),
columns=df.columns)
Out[3]:
A
1 1  8
3  9
96023 次浏览

You don't need to create a new DataFrame instance! You can modify the index:

df.index = df.index.droplevel(2)
df


A
1 1  8
3  9

You can also specify negative indices, for selection from the end:

df.index = df.index.droplevel(-1)
df.reset_index(level=2, drop=True)
Out[29]:
A
1 1  8
3  9

If your index has names like

       A
X Y Z
1 1 1  8
3 2  9

Then you can also remove by specifying the index name

df.index = df.index.droplevel(Z)

From 0.24+, we can directly droplevel on df. So, to drop the last level of the index:

>>> df


col
1 5 1 4  foo
3 2 8  bar
2 4 3 7  saz


# `axis` defaults to `index` or equivalently 0


>>> df.droplevel(-1, axis="index")


col
1 5 1  foo
3 2  bar
2 4 3  saz

The axis whose levels are dropped can also be controlled with axis argument and it defaults to 0, i.e., over index. Multiple levels can be dropped at once via supplying a list and if any of the index has a name, those can be used, too (as exemplified in the linked doc).


Note: the argument to droplevel is tried to be first interpreted as a label; so if any of the levels happens to have an integer name, it will be dropped i.e., not positionally:

>>> df
col
this -1 other 0
1    5  1     4  foo
3  2     8  bar
2    4  3     7  saz




# literally drops `-1` level
>>> df.droplevel(-1)


col
this other 0
1    1     4  foo
2     8  bar
2    3     7  saz


# literally level `0` is dropped
>>> df.droplevel(0)


col
this -1 other
1    5  1      foo
3  2      bar
2    4  3      saz

To make sure a positional dropping happens, we can go for the names attribute and select positionally there:

>>> df
col
this -1 other 0
1    5  1     4  foo
3  2     8  bar
2    4  3     7  saz


# go get the name of the last level, drop whatever it is
>>> df.droplevel(df.index.names[-1])


col
this -1 other
1    5  1      foo
3  2      bar
2    4  3      saz


# similarly...
>>> df.droplevel(df.index.names[0])


col
-1 other 0
5  1     4  foo
3  2     8  bar
4  3     7  saz


Lastly, droplevel returns a new dataframe, so df = df.droplevel(...) is needed to see the change in df.