Skip to content

Commit c9f4e67

Browse files
committed
REF/PERF: move MultiIndex._tuples into _cache
1 parent df1d440 commit c9f4e67

File tree

2 files changed

+26
-23
lines changed

2 files changed

+26
-23
lines changed

pandas/core/indexes/multi.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,6 @@ class MultiIndex(Index):
243243
_comparables = ["names"]
244244
rename = Index.set_names
245245

246-
_tuples = None
247246
sortorder: Optional[int]
248247

249248
# --------------------------------------------------------------------
@@ -634,16 +633,9 @@ def from_frame(cls, df, sortorder=None, names=None):
634633

635634
# --------------------------------------------------------------------
636635

637-
@property
636+
@cache_readonly
638637
def _values(self):
639638
# We override here, since our parent uses _data, which we don't use.
640-
return self.values
641-
642-
@property
643-
def values(self):
644-
if self._tuples is not None:
645-
return self._tuples
646-
647639
values = []
648640

649641
for i in range(self.nlevels):
@@ -657,8 +649,12 @@ def values(self):
657649
vals = np.array(vals, copy=False)
658650
values.append(vals)
659651

660-
self._tuples = lib.fast_zip(values)
661-
return self._tuples
652+
arr = lib.fast_zip(values)
653+
return arr
654+
655+
@property
656+
def values(self):
657+
return self._values
662658

663659
@property
664660
def array(self):
@@ -737,7 +733,6 @@ def _set_levels(
737733
if any(names):
738734
self._set_names(names)
739735

740-
self._tuples = None
741736
self._reset_cache()
742737

743738
def set_levels(self, levels, level=None, inplace=None, verify_integrity=True):
@@ -906,7 +901,6 @@ def _set_codes(
906901

907902
self._codes = new_codes
908903

909-
self._tuples = None
910904
self._reset_cache()
911905

912906
def set_codes(self, codes, level=None, inplace=None, verify_integrity=True):

pandas/tests/indexes/multi/test_compat.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,45 +68,54 @@ def test_inplace_mutation_resets_values():
6868

6969
mi1 = MultiIndex(levels=levels, codes=codes)
7070
mi2 = MultiIndex(levels=levels2, codes=codes)
71+
72+
assert "_values" not in mi1._cache
73+
assert "_values" not in mi2._cache
74+
7175
vals = mi1.values.copy()
7276
vals2 = mi2.values.copy()
7377

74-
assert mi1._tuples is not None
78+
assert isinstance(mi1._cache["_values"], np.ndarray)
7579

7680
# Make sure level setting works
7781
new_vals = mi1.set_levels(levels2).values
7882
tm.assert_almost_equal(vals2, new_vals)
7983

80-
# Non-inplace doesn't kill _tuples [implementation detail]
81-
tm.assert_almost_equal(mi1._tuples, vals)
84+
# Non-inplace doesn't drop _values from _cache [implementation detail]
85+
tm.assert_almost_equal(mi1._cache["_values"], vals)
8286

8387
# ...and values is still same too
8488
tm.assert_almost_equal(mi1.values, vals)
8589

86-
# Inplace should kill _tuples
90+
# Inplace should drop _values from _cache
8791
with tm.assert_produces_warning(FutureWarning):
8892
mi1.set_levels(levels2, inplace=True)
93+
assert "_values" not in mi1._cache
8994
tm.assert_almost_equal(mi1.values, vals2)
9095

9196
# Make sure label setting works too
9297
codes2 = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]
9398
exp_values = np.empty((6,), dtype=object)
9499
exp_values[:] = [(1, "a")] * 6
95-
96100
# Must be 1d array of tuples
97101
assert exp_values.shape == (6,)
98-
new_values = mi2.set_codes(codes2).values
102+
103+
new_mi = mi2.set_codes(codes2)
104+
assert "_values" not in new_mi._cache
105+
new_values = new_mi.values
106+
assert "_values" in new_mi._cache
99107

100108
# Not inplace shouldn't change
101-
tm.assert_almost_equal(mi2._tuples, vals2)
109+
tm.assert_almost_equal(mi2._cache["_values"], vals2)
102110

103111
# Should have correct values
104112
tm.assert_almost_equal(exp_values, new_values)
105113

106-
# ...and again setting inplace should kill _tuples, etc
107-
with tm.assert_produces_warning(FutureWarning):
108-
mi2.set_codes(codes2, inplace=True)
114+
# ...and again setting inplace should drop _values from _cache, etc
115+
mi2.set_codes(codes2, inplace=True)
116+
assert "_values" not in mi2._cache
109117
tm.assert_almost_equal(mi2.values, new_values)
118+
assert "_values" in mi2._cache
110119

111120

112121
def test_ndarray_compat_properties(idx, compat_props):

0 commit comments

Comments
 (0)