Skip to content
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

BUG: Let MultiIndex.set_levels accept any iterable (#23273) #23291

Merged
merged 1 commit into from
Oct 25, 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/whatsnew/v0.24.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,7 @@ Indexing
- Bug in :meth:`DataFrame.loc` when indexing with an :class:`IntervalIndex` (:issue:`19977`)
- :class:`Index` no longer mangles ``None``, ``NaN`` and ``NaT``, i.e. they are treated as three different keys. However, for numeric Index all three are still coerced to a ``NaN`` (:issue:`22332`)
- Bug in `scalar in Index` if scalar is a float while the ``Index`` is of integer dtype (:issue:`22085`)
- Bug in `MultiIndex.set_levels` when levels value is not subscriptable (:issue:`23273`)

Missing
^^^^^^^
Expand Down
3 changes: 3 additions & 0 deletions pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,9 @@ def set_levels(self, levels, level=None, inplace=False,
labels=[[0, 0, 1, 1], [0, 1, 0, 1]],
names=[u'foo', u'bar'])
"""
if is_list_like(levels) and not isinstance(levels, Index):
levels = list(levels)

if level is not None and not is_list_like(level):
if not is_list_like(levels):
Copy link
Contributor

Choose a reason for hiding this comment

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

you can just add

elif is_list_like(levels):
   levels = list(levels)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tried this approach, but unfortunately, it breaks functionality where one passes to set_levels CategoricalIndex (or in general, something else which is more complex than a list).

Specifically, this turns

index.set_levels(CategoricalIndex(list("bac")), 0)

into

index.set_levels(list("bac"), 0)

and breaks test_set_levels_categorical

I can add change this with

if is_list_like(levels) and not isinstance(levels, Index):

but I'm not sure what's better

Copy link
Contributor

Choose a reason for hiding this comment

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

the 2nd would be fine, we don't need/want any more api checkers)

raise TypeError("Levels must be list-like")
Expand Down
14 changes: 14 additions & 0 deletions pandas/tests/indexes/multi/test_get_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,3 +414,17 @@ def test_set_value_keeps_names():
df.at[('grethe', '4'), 'one'] = 99.34
assert df._is_copy is None
assert df.index.names == ('Name', 'Number')


def test_set_levels_with_iterable():
# GH23273
sizes = [1, 2, 3]
colors = ['black'] * 3
index = pd.MultiIndex.from_arrays([sizes, colors], names=['size', 'color'])

result = index.set_levels(map(int, ['3', '2', '1']), level='size')

expected_sizes = [3, 2, 1]
expected = pd.MultiIndex.from_arrays([expected_sizes, colors],
names=['size', 'color'])
tm.assert_index_equal(result, expected)