Skip to content

Commit d47947a

Browse files
topper-123jreback
authored andcommitted
ENH: better MultiIndex.__repr__ (#22511)
1 parent 376a05e commit d47947a

File tree

9 files changed

+451
-146
lines changed

9 files changed

+451
-146
lines changed

doc/source/user_guide/advanced.rst

+5-4
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,15 @@ on a deeper level.
182182
Defined Levels
183183
~~~~~~~~~~~~~~
184184

185-
The repr of a ``MultiIndex`` shows all the defined levels of an index, even
185+
The :class:`MultiIndex` keeps all the defined levels of an index, even
186186
if they are not actually used. When slicing an index, you may notice this.
187187
For example:
188188

189189
.. ipython:: python
190190
191-
  df.columns # original MultiIndex
191+
  df.columns.levels # original MultiIndex
192192
193-
df[['foo','qux']].columns # sliced
193+
df[['foo','qux']].columns.levels # sliced
194194
195195
This is done to avoid a recomputation of the levels in order to make slicing
196196
highly performant. If you want to see only the used levels, you can use the
@@ -210,7 +210,8 @@ To reconstruct the ``MultiIndex`` with only the used levels, the
210210

211211
.. ipython:: python
212212
213-
df[['foo', 'qux']].columns.remove_unused_levels()
213+
new_mi = df[['foo', 'qux']].columns.remove_unused_levels()
214+
new_mi.levels
214215
215216
Data alignment and using ``reindex``
216217
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

doc/source/whatsnew/v0.25.0.rst

+32
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,38 @@ a dict to a Series groupby aggregation (:ref:`whatsnew_0200.api_breaking.depreca
7474

7575
See :ref:`groupby.aggregate.named` for more.
7676

77+
78+
.. _whatsnew_0250.enhancements.multi_index_repr:
79+
80+
Better repr for MultiIndex
81+
^^^^^^^^^^^^^^^^^^^^^^^^^^
82+
83+
Printing of :class:`MultiIndex` instances now shows tuples of each row and ensures
84+
that the tuple items are vertically aligned, so it's now easier to understand
85+
the structure of the ``MultiIndex``. (:issue:`13480`):
86+
87+
The repr now looks like this:
88+
89+
.. ipython:: python
90+
91+
pd.MultiIndex.from_product([['a', 'abc'], range(500)])
92+
93+
Previously, outputting a :class:`MultiIndex` printed all the ``levels`` and
94+
``codes`` of the ``MultiIndex``, which was visually unappealing and made
95+
the output more difficult to navigate. For example (limiting the range to 5):
96+
97+
.. code-block:: ipython
98+
99+
In [1]: pd.MultiIndex.from_product([['a', 'abc'], range(5)])
100+
Out[1]: MultiIndex(levels=[['a', 'abc'], [0, 1, 2, 3]],
101+
...: codes=[[0, 0, 0, 0, 1, 1, 1, 1], [0, 1, 2, 3, 0, 1, 2, 3]])
102+
103+
In the new repr, all values will be shown, if the number of rows is smaller
104+
than :attr:`options.display.max_seq_items` (default: 100 items). Horizontally,
105+
the output will truncate, if it's wider than :attr:`options.display.width`
106+
(default: 80 characters).
107+
108+
77109
.. _whatsnew_0250.enhancements.other:
78110

79111
Other Enhancements

pandas/core/indexes/base.py

+27-14
Original file line numberDiff line numberDiff line change
@@ -1332,16 +1332,23 @@ def set_names(self, names, level=None, inplace=False):
13321332
>>> idx = pd.MultiIndex.from_product([['python', 'cobra'],
13331333
... [2018, 2019]])
13341334
>>> idx
1335-
MultiIndex(levels=[['cobra', 'python'], [2018, 2019]],
1336-
codes=[[1, 1, 0, 0], [0, 1, 0, 1]])
1335+
MultiIndex([('python', 2018),
1336+
('python', 2019),
1337+
( 'cobra', 2018),
1338+
( 'cobra', 2019)],
1339+
)
13371340
>>> idx.set_names(['kind', 'year'], inplace=True)
13381341
>>> idx
1339-
MultiIndex(levels=[['cobra', 'python'], [2018, 2019]],
1340-
codes=[[1, 1, 0, 0], [0, 1, 0, 1]],
1342+
MultiIndex([('python', 2018),
1343+
('python', 2019),
1344+
( 'cobra', 2018),
1345+
( 'cobra', 2019)],
13411346
names=['kind', 'year'])
13421347
>>> idx.set_names('species', level=0)
1343-
MultiIndex(levels=[['cobra', 'python'], [2018, 2019]],
1344-
codes=[[1, 1, 0, 0], [0, 1, 0, 1]],
1348+
MultiIndex([('python', 2018),
1349+
('python', 2019),
1350+
( 'cobra', 2018),
1351+
( 'cobra', 2019)],
13451352
names=['species', 'year'])
13461353
"""
13471354

@@ -1403,12 +1410,16 @@ def rename(self, name, inplace=False):
14031410
... [2018, 2019]],
14041411
... names=['kind', 'year'])
14051412
>>> idx
1406-
MultiIndex(levels=[['cobra', 'python'], [2018, 2019]],
1407-
codes=[[1, 1, 0, 0], [0, 1, 0, 1]],
1413+
MultiIndex([('python', 2018),
1414+
('python', 2019),
1415+
( 'cobra', 2018),
1416+
( 'cobra', 2019)],
14081417
names=['kind', 'year'])
14091418
>>> idx.rename(['species', 'year'])
1410-
MultiIndex(levels=[['cobra', 'python'], [2018, 2019]],
1411-
codes=[[1, 1, 0, 0], [0, 1, 0, 1]],
1419+
MultiIndex([('python', 2018),
1420+
('python', 2019),
1421+
( 'cobra', 2018),
1422+
( 'cobra', 2019)],
14121423
names=['species', 'year'])
14131424
>>> idx.rename('species')
14141425
Traceback (most recent call last):
@@ -5442,8 +5453,8 @@ def ensure_index_from_sequences(sequences, names=None):
54425453
54435454
>>> ensure_index_from_sequences([['a', 'a'], ['a', 'b']],
54445455
names=['L1', 'L2'])
5445-
MultiIndex(levels=[['a'], ['a', 'b']],
5446-
codes=[[0, 0], [0, 1]],
5456+
MultiIndex([('a', 'a'),
5457+
('a', 'b')],
54475458
names=['L1', 'L2'])
54485459
54495460
See Also
@@ -5483,8 +5494,10 @@ def ensure_index(index_like, copy=False):
54835494
Index([('a', 'a'), ('b', 'c')], dtype='object')
54845495
54855496
>>> ensure_index([['a', 'a'], ['b', 'c']])
5486-
MultiIndex(levels=[['a'], ['b', 'c']],
5487-
codes=[[0, 0], [0, 1]])
5497+
MultiIndex([('a', 'b'),
5498+
('a', 'c')],
5499+
dtype='object')
5500+
)
54885501
54895502
See Also
54905503
--------

0 commit comments

Comments
 (0)