Skip to content

Commit

Permalink
API: Restore getting name from MultiIndex level (pandas-dev#29061)
Browse files Browse the repository at this point in the history
* API: Restore getting name from MultiIndex level

xref https://issues.apache.org/jira/browse/ARROW-6922 /
pandas-dev#27242 (comment)
/ pandas-dev#29032

No docs yet, since it isn't clear how this will eventually sort out. But
we at least want to preserve this behavior for 1.0

* fixups
  • Loading branch information
TomAugspurger authored and proost committed Dec 19, 2019
1 parent 738b368 commit 3dd2150
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 31 deletions.
7 changes: 5 additions & 2 deletions pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,10 @@ def from_frame(cls, df, sortorder=None, names=None):

@property
def levels(self):
return self._levels
result = [
x._shallow_copy(name=name) for x, name in zip(self._levels, self._names)
]
return FrozenList(result)

@property
def _values(self):
Expand Down Expand Up @@ -830,7 +833,7 @@ def _set_codes(
if level is None:
new_codes = FrozenList(
_ensure_frozen(level_codes, lev, copy=copy)._shallow_copy()
for lev, level_codes in zip(self.levels, codes)
for lev, level_codes in zip(self._levels, codes)
)
else:
level = [self._get_level_number(l) for l in level]
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/frame/test_alter_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -978,7 +978,7 @@ def test_reset_index(self, float_frame):
):
values = lev.take(level_codes)
name = names[i]
tm.assert_index_equal(values, Index(deleveled[name].rename(name=None)))
tm.assert_index_equal(values, Index(deleveled[name]))

stacked.index.names = [None, None]
deleveled2 = stacked.reset_index()
Expand Down
6 changes: 3 additions & 3 deletions pandas/tests/indexes/multi/test_constructor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def test_constructor_single_level():
levels=[["foo", "bar", "baz", "qux"]], codes=[[0, 1, 2, 3]], names=["first"]
)
assert isinstance(result, MultiIndex)
expected = Index(["foo", "bar", "baz", "qux"])
expected = Index(["foo", "bar", "baz", "qux"], name="first")
tm.assert_index_equal(result.levels[0], expected)
assert result.names == ["first"]

Expand Down Expand Up @@ -292,7 +292,7 @@ def test_from_arrays_empty():
# 1 level
result = MultiIndex.from_arrays(arrays=[[]], names=["A"])
assert isinstance(result, MultiIndex)
expected = Index([])
expected = Index([], name="A")
tm.assert_index_equal(result.levels[0], expected)
assert result.names == ["A"]

Expand Down Expand Up @@ -440,7 +440,7 @@ def test_from_product_empty_zero_levels():

def test_from_product_empty_one_level():
result = MultiIndex.from_product([[]], names=["A"])
expected = pd.Index([])
expected = pd.Index([], name="A")
tm.assert_index_equal(result.levels[0], expected)
assert result.names == ["A"]

Expand Down
23 changes: 15 additions & 8 deletions pandas/tests/indexes/multi/test_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_index_name_retained():


def test_changing_names(idx):
assert [level.name for level in idx.levels] == [None, None]
assert [level.name for level in idx.levels] == ["first", "second"]

view = idx.view()
copy = idx.copy()
Expand All @@ -36,16 +36,16 @@ def test_changing_names(idx):
# changing names should not change level names on object
new_names = [name + "a" for name in idx.names]
idx.names = new_names
check_level_names(idx, [None, None])
check_level_names(idx, ["firsta", "seconda"])

# and not on copies
check_level_names(view, [None, None])
check_level_names(copy, [None, None])
check_level_names(shallow_copy, [None, None])
check_level_names(view, ["first", "second"])
check_level_names(copy, ["first", "second"])
check_level_names(shallow_copy, ["first", "second"])

# and copies shouldn't change original
shallow_copy.names = [name + "c" for name in shallow_copy.names]
check_level_names(idx, [None, None])
check_level_names(idx, ["firsta", "seconda"])


def test_take_preserve_name(idx):
Expand Down Expand Up @@ -81,7 +81,7 @@ def test_names(idx, index_names):
# names are assigned in setup
assert index_names == ["first", "second"]
level_names = [level.name for level in idx.levels]
assert level_names == [None, None]
assert level_names == index_names

# setting bad names on existing
index = idx
Expand Down Expand Up @@ -109,11 +109,18 @@ def test_names(idx, index_names):
# names are assigned on index, but not transferred to the levels
index.names = ["a", "b"]
level_names = [level.name for level in index.levels]
assert level_names == [None, None]
assert level_names == ["a", "b"]


def test_duplicate_level_names_access_raises(idx):
# GH19029
idx.names = ["foo", "foo"]
with pytest.raises(ValueError, match="name foo occurs multiple times"):
idx._get_level_number("foo")


def test_get_names_from_levels():
idx = pd.MultiIndex.from_product([["a"], [1, 2]], names=["a", "b"])

assert idx.levels[0].name == "a"
assert idx.levels[1].name == "b"
4 changes: 2 additions & 2 deletions pandas/tests/indexes/multi/test_reindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ def test_reindex(idx):
result, indexer = idx.reindex(list(idx[:4]))
assert isinstance(result, MultiIndex)
assert result.names == ["first", "second"]
assert [level.name for level in result.levels] == [None, None]
assert [level.name for level in result.levels] == ["first", "second"]

result, indexer = idx.reindex(list(idx))
assert isinstance(result, MultiIndex)
assert indexer is None
assert result.names == ["first", "second"]
assert [level.name for level in result.levels] == [None, None]
assert [level.name for level in result.levels] == ["first", "second"]


def test_reindex_level(idx):
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/indexes/multi/test_reshape.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ def test_insert(idx):
# key not contained in all levels
new_index = idx.insert(0, ("abc", "three"))

exp0 = Index(list(idx.levels[0]) + ["abc"])
exp0 = Index(list(idx.levels[0]) + ["abc"], name="first")
tm.assert_index_equal(new_index.levels[0], exp0)
assert new_index.names == ["first", "second"]

exp1 = Index(list(idx.levels[1]) + ["three"])
exp1 = Index(list(idx.levels[1]) + ["three"], name="second")
tm.assert_index_equal(new_index.levels[1], exp1)
assert new_index[0] == ("abc", "three")

Expand Down
6 changes: 4 additions & 2 deletions pandas/tests/reshape/test_concat.py
Original file line number Diff line number Diff line change
Expand Up @@ -1219,7 +1219,7 @@ def test_concat_keys_specific_levels(self):
names=["group_key"],
)

tm.assert_index_equal(result.columns.levels[0], Index(level))
tm.assert_index_equal(result.columns.levels[0], Index(level, name="group_key"))
tm.assert_index_equal(result.columns.levels[1], Index([0, 1, 2, 3]))

assert result.columns.names == ["group_key", None]
Expand Down Expand Up @@ -1412,7 +1412,9 @@ def test_concat_keys_and_levels(self):
names=["first", "second"],
)
assert result.index.names == ("first", "second", None)
tm.assert_index_equal(result.index.levels[0], Index(["baz", "foo"]))
tm.assert_index_equal(
result.index.levels[0], Index(["baz", "foo"], name="first")
)

def test_concat_keys_levels_no_overlap(self):
# GH #1406
Expand Down
21 changes: 10 additions & 11 deletions pandas/tests/test_multilevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -975,11 +975,11 @@ def test_count(self):
series.index.names = ["a", "b"]

result = series.count(level="b")
expect = self.series.count(level=1)
expect = self.series.count(level=1).rename_axis("b")
tm.assert_series_equal(result, expect)

result = series.count(level="a")
expect = self.series.count(level=0)
expect = self.series.count(level=0).rename_axis("a")
tm.assert_series_equal(result, expect)

msg = "Level x not found"
Expand Down Expand Up @@ -1641,16 +1641,14 @@ def test_constructor_with_tz(self):
result = MultiIndex.from_arrays([index, columns])

assert result.names == ["dt1", "dt2"]
# levels don't have names set, so set name of index/columns to None in checks
tm.assert_index_equal(result.levels[0], index.rename(name=None))
tm.assert_index_equal(result.levels[1], columns.rename(name=None))
tm.assert_index_equal(result.levels[0], index)
tm.assert_index_equal(result.levels[1], columns)

result = MultiIndex.from_arrays([Series(index), Series(columns)])

assert result.names == ["dt1", "dt2"]
# levels don't have names set, so set name of index/columns to None in checks
tm.assert_index_equal(result.levels[0], index.rename(name=None))
tm.assert_index_equal(result.levels[1], columns.rename(name=None))
tm.assert_index_equal(result.levels[0], index)
tm.assert_index_equal(result.levels[1], columns)

def test_set_index_datetime(self):
# GH 3950
Expand All @@ -1672,17 +1670,18 @@ def test_set_index_datetime(self):
df.index = df.index.tz_convert("US/Pacific")

expected = pd.DatetimeIndex(
["2011-07-19 07:00:00", "2011-07-19 08:00:00", "2011-07-19 09:00:00"]
["2011-07-19 07:00:00", "2011-07-19 08:00:00", "2011-07-19 09:00:00"],
name="datetime",
)
expected = expected.tz_localize("UTC").tz_convert("US/Pacific")

df = df.set_index("label", append=True)
tm.assert_index_equal(df.index.levels[0], expected)
tm.assert_index_equal(df.index.levels[1], Index(["a", "b"]))
tm.assert_index_equal(df.index.levels[1], Index(["a", "b"], name="label"))
assert df.index.names == ["datetime", "label"]

df = df.swaplevel(0, 1)
tm.assert_index_equal(df.index.levels[0], Index(["a", "b"]))
tm.assert_index_equal(df.index.levels[0], Index(["a", "b"], name="label"))
tm.assert_index_equal(df.index.levels[1], expected)
assert df.index.names == ["label", "datetime"]

Expand Down

0 comments on commit 3dd2150

Please sign in to comment.