Skip to content

Add to_flat_index method to MultiIndex #22866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Nov 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1724,6 +1724,7 @@ MultiIndex Components
MultiIndex.set_levels
MultiIndex.set_labels
MultiIndex.to_hierarchical
MultiIndex.to_flat_index
MultiIndex.to_frame
MultiIndex.is_lexsorted
MultiIndex.sortlevel
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ Other Enhancements
- :func:`~DataFrame.to_parquet` now supports writing a ``DataFrame`` as a directory of parquet files partitioned by a subset of the columns when ``engine = 'pyarrow'`` (:issue:`23283`)
- :meth:`Timestamp.tz_localize`, :meth:`DatetimeIndex.tz_localize`, and :meth:`Series.tz_localize` have gained the ``nonexistent`` argument for alternative handling of nonexistent times. See :ref:`timeseries.timezone_nonexistent` (:issue:`8917`)
- :meth:`read_excel()` now accepts ``usecols`` as a list of column names or callable (:issue:`18273`)
- :meth:`MultiIndex.to_flat_index` has been added to flatten multiple levels into a single-level :class:`Index` object.

.. _whatsnew_0240.api_breaking:

Expand Down
20 changes: 20 additions & 0 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,26 @@ def _format_attrs(self):
"""
return format_object_attrs(self)

def to_flat_index(self):
"""
Identity method.

.. versionadded:: 0.24.0

This is implemented for compatability with subclass implementations
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compatability -> compatibility

By the way: reusing the same docstring would be a + for me. I would rather describe the actual operation (so on MuiltiIndex) and then say that it is idempotent on flat Indexes.

when chaining.

Returns
-------
pd.Index
Caller.

See Also
--------
MultiIndex.to_flat_index : Subclass implementation.
"""
return self

def to_series(self, index=None, name=None):
"""
Create a Series with both index and values equal to the index keys
Expand Down
29 changes: 29 additions & 0 deletions pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class MultiIndex(Index):
set_levels
set_labels
to_frame
to_flat_index
is_lexsorted
sortlevel
droplevel
Expand Down Expand Up @@ -1246,6 +1247,34 @@ def to_hierarchical(self, n_repeat, n_shuffle=1):
FutureWarning, stacklevel=2)
return MultiIndex(levels=levels, labels=labels, names=names)

def to_flat_index(self):
"""
Convert a MultiIndex to an Index of Tuples containing the level values.

.. versionadded:: 0.24.0

Returns
-------
pd.Index
Index with the MultiIndex data represented in Tuples.

Notes
-----
This method will simply return the caller if called by anything other
than a MultiIndex.

Examples
--------
>>> index = pd.MultiIndex.from_product(
... [['foo', 'bar'], ['baz', 'qux']],
... names=['a', 'b'])
>>> index.to_flat_index()
Index([('foo', 'baz'), ('foo', 'qux'),
('bar', 'baz'), ('bar', 'qux')],
dtype='object')
"""
return Index(self.values, tupleize_cols=False)

@property
def is_all_dates(self):
return False
Expand Down
8 changes: 8 additions & 0 deletions pandas/tests/indexes/multi/test_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,11 @@ def test_to_series_with_arguments(idx):
assert s.values is not idx.values
assert s.index is not idx
assert s.name != idx.name


def test_to_flat_index(idx):
expected = pd.Index((('foo', 'one'), ('foo', 'two'), ('bar', 'one'),
('baz', 'two'), ('qux', 'one'), ('qux', 'two')),
tupleize_cols=False)
result = idx.to_flat_index()
tm.assert_index_equal(result, expected)
8 changes: 8 additions & 0 deletions pandas/tests/indexes/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2266,6 +2266,14 @@ def test_tab_complete_warning(self, ip):
with provisionalcompleter('ignore'):
list(ip.Completer.completions('idx.', 4))

def test_to_flat_index(self, indices):
# 22866
if isinstance(indices, MultiIndex):
pytest.skip("Separate expectation for MultiIndex")

result = indices.to_flat_index()
tm.assert_index_equal(result, indices)


class TestMixedIntIndex(Base):
# Mostly the tests from common.py for which the results differ
Expand Down