Skip to content

Commit 18e24a9

Browse files
committed
PERF: Cache MultiIndex.levels (pandas-dev#31651)
* PERF: Cache MultiIndex.levels Closes pandas-dev#31648 * fixup tests
1 parent 4476644 commit 18e24a9

File tree

2 files changed

+20
-11
lines changed

2 files changed

+20
-11
lines changed

pandas/core/indexes/multi.py

+19-10
Original file line numberDiff line numberDiff line change
@@ -620,16 +620,6 @@ def from_frame(cls, df, sortorder=None, names=None):
620620

621621
# --------------------------------------------------------------------
622622

623-
@property
624-
def levels(self):
625-
result = [
626-
x._shallow_copy(name=name) for x, name in zip(self._levels, self._names)
627-
]
628-
for level in result:
629-
# disallow midx.levels[0].name = "foo"
630-
level._no_setting_name = True
631-
return FrozenList(result)
632-
633623
@property
634624
def _values(self):
635625
# We override here, since our parent uses _data, which we don't use.
@@ -659,6 +649,22 @@ def array(self):
659649
"'MultiIndex.to_numpy()' to get a NumPy array of tuples."
660650
)
661651

652+
# --------------------------------------------------------------------
653+
# Levels Methods
654+
655+
@cache_readonly
656+
def levels(self):
657+
# Use cache_readonly to ensure that self.get_locs doesn't repeatedly
658+
# create new IndexEngine
659+
# https://github.com/pandas-dev/pandas/issues/31648
660+
result = [
661+
x._shallow_copy(name=name) for x, name in zip(self._levels, self._names)
662+
]
663+
for level in result:
664+
# disallow midx.levels[0].name = "foo"
665+
level._no_setting_name = True
666+
return FrozenList(result)
667+
662668
def _set_levels(
663669
self, levels, level=None, copy=False, validate=True, verify_integrity=False
664670
):
@@ -1227,6 +1233,9 @@ def _set_names(self, names, level=None, validate=True):
12271233
)
12281234
self._names[lev] = name
12291235

1236+
# If .levels has been accessed, the names in our cache will be stale.
1237+
self._reset_cache()
1238+
12301239
names = property(
12311240
fset=_set_names, fget=_get_names, doc="""\nNames of levels in MultiIndex.\n"""
12321241
)

pandas/tests/indexes/multi/test_get_set.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def test_set_levels_codes_directly(idx):
159159
minor_codes = [(x + 1) % 1 for x in minor_codes]
160160
new_codes = [major_codes, minor_codes]
161161

162-
msg = "can't set attribute"
162+
msg = "[Cc]an't set attribute"
163163
with pytest.raises(AttributeError, match=msg):
164164
idx.levels = new_levels
165165
with pytest.raises(AttributeError, match=msg):

0 commit comments

Comments
 (0)