Skip to content

Commit 7277459

Browse files
committed
ENH: add MultiIndex.to_dataframe
closes #12397 Author: Jeff Reback <[email protected]> Closes #15216 from jreback/to_dataframe and squashes the following commits: b744fb5 [Jeff Reback] ENH: add MultiIndex.to_dataframe
1 parent ba05744 commit 7277459

File tree

4 files changed

+63
-1
lines changed

4 files changed

+63
-1
lines changed

doc/source/api.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,7 @@ MultiIndex Components
14601460
MultiIndex.set_levels
14611461
MultiIndex.set_labels
14621462
MultiIndex.to_hierarchical
1463+
MultiIndex.to_frame
14631464
MultiIndex.is_lexsorted
14641465
MultiIndex.droplevel
14651466
MultiIndex.swaplevel

doc/source/whatsnew/v0.20.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ Other enhancements
131131
- New ``UnsortedIndexError`` (subclass of ``KeyError``) raised when indexing/slicing into an
132132
unsorted MultiIndex (:issue:`11897`). This allows differentiation between errors due to lack
133133
of sorting or an incorrect key. See :ref:`here <advanced.unsorted>`
134-
134+
- ``MultiIndex`` has gained a ``.to_frame()`` method to convert to a ``DataFrame`` (:issue:`12397`)
135135
- ``pd.cut`` and ``pd.qcut`` now support datetime64 and timedelta64 dtypes (:issue:`14714`, :issue:`14798`)
136136
- ``pd.qcut`` has gained the ``duplicates='raise'|'drop'`` option to control whether to raise on duplicated edges (:issue:`7751`)
137137
- ``Series`` provides a ``to_excel`` method to output Excel files (:issue:`8825`)

pandas/indexes/multi.py

+24
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,30 @@ def _to_safe_for_reshape(self):
827827
""" convert to object if we are a categorical """
828828
return self.set_levels([i._to_safe_for_reshape() for i in self.levels])
829829

830+
def to_frame(self, index=True):
831+
"""
832+
Create a DataFrame with the columns the levels of the MultiIndex
833+
834+
.. versionadded:: 0.20.0
835+
836+
Parameters
837+
----------
838+
index : boolean, default True
839+
return this MultiIndex as the index
840+
841+
Returns
842+
-------
843+
DataFrame
844+
"""
845+
846+
from pandas import DataFrame
847+
result = DataFrame({(name or level): self.get_level_values(level)
848+
for name, level in
849+
zip(self.names, range(len(self.levels)))})
850+
if index:
851+
result.index = self
852+
return result
853+
830854
def to_hierarchical(self, n_repeat, n_shuffle=1):
831855
"""
832856
Return a MultiIndex reshaped to conform to the

pandas/tests/indexes/test_multi.py

+37
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,43 @@ def test_format_sparse_config(self):
13481348

13491349
warnings.filters = warn_filters
13501350

1351+
def test_to_frame(self):
1352+
tuples = [(1, 'one'), (1, 'two'), (2, 'one'), (2, 'two')]
1353+
1354+
index = MultiIndex.from_tuples(tuples)
1355+
result = index.to_frame(index=False)
1356+
expected = DataFrame(tuples)
1357+
tm.assert_frame_equal(result, expected)
1358+
1359+
result = index.to_frame()
1360+
expected.index = index
1361+
tm.assert_frame_equal(result, expected)
1362+
1363+
tuples = [(1, 'one'), (1, 'two'), (2, 'one'), (2, 'two')]
1364+
index = MultiIndex.from_tuples(tuples, names=['first', 'second'])
1365+
result = index.to_frame(index=False)
1366+
expected = DataFrame(tuples)
1367+
expected.columns = ['first', 'second']
1368+
tm.assert_frame_equal(result, expected)
1369+
1370+
result = index.to_frame()
1371+
expected.index = index
1372+
tm.assert_frame_equal(result, expected)
1373+
1374+
index = MultiIndex.from_product([range(5),
1375+
pd.date_range('20130101', periods=3)])
1376+
result = index.to_frame(index=False)
1377+
expected = DataFrame(
1378+
{0: np.repeat(np.arange(5, dtype='int64'), 3),
1379+
1: np.tile(pd.date_range('20130101', periods=3), 5)})
1380+
tm.assert_frame_equal(result, expected)
1381+
1382+
index = MultiIndex.from_product([range(5),
1383+
pd.date_range('20130101', periods=3)])
1384+
result = index.to_frame()
1385+
expected.index = index
1386+
tm.assert_frame_equal(result, expected)
1387+
13511388
def test_to_hierarchical(self):
13521389
index = MultiIndex.from_tuples([(1, 'one'), (1, 'two'), (2, 'one'), (
13531390
2, 'two')])

0 commit comments

Comments
 (0)