Skip to content

Commit b9e96fa

Browse files
authored
BUG: MultiIndex.reindex with non-MultiIndex; Series constructor (#41707)
1 parent b8ee68b commit b9e96fa

File tree

7 files changed

+36
-54
lines changed

7 files changed

+36
-54
lines changed

doc/source/whatsnew/v1.3.0.rst

+3
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,7 @@ MultiIndex
961961
- Bug in :meth:`MultiIndex.equals` incorrectly returning ``True`` when :class:`MultiIndex` containing ``NaN`` even when they are differently ordered (:issue:`38439`)
962962
- Bug in :meth:`MultiIndex.intersection` always returning empty when intersecting with :class:`CategoricalIndex` (:issue:`38653`)
963963
- Bug in :meth:`MultiIndex.reindex` raising ``ValueError`` with empty MultiIndex and indexing only a specific level (:issue:`41170`)
964+
- Bug in :meth:`MultiIndex.reindex` raising ``TypeError`` when reindexing against a flat :class:`Index` (:issue:`41707`)
964965

965966
I/O
966967
^^^
@@ -1075,6 +1076,7 @@ Reshaping
10751076
- Bug in :meth:`DataFrame.sort_values` not reshaping index correctly after sorting on columns, when ``ignore_index=True`` (:issue:`39464`)
10761077
- Bug in :meth:`DataFrame.append` returning incorrect dtypes with combinations of ``ExtensionDtype`` dtypes (:issue:`39454`)
10771078
- Bug in :meth:`DataFrame.append` returning incorrect dtypes with combinations of ``datetime64`` and ``timedelta64`` dtypes (:issue:`39574`)
1079+
- Bug in :meth:`DataFrame.append` with a :class:`DataFrame` with a :class:`MultiIndex` and appending a :class:`Series` whose :class:`Index` is not a :class:`MultiIndex` (:issue:`41707`)
10781080
- Bug in :meth:`DataFrame.pivot_table` returning a ``MultiIndex`` for a single value when operating on and empty ``DataFrame`` (:issue:`13483`)
10791081
- Allow :class:`Index` to be passed to the :func:`numpy.all` function (:issue:`40180`)
10801082
- Bug in :meth:`DataFrame.stack` not preserving ``CategoricalDtype`` in a ``MultiIndex`` (:issue:`36991`)
@@ -1129,6 +1131,7 @@ Other
11291131
- Bug in :meth:`DataFrame.clip` not interpreting missing values as no threshold (:issue:`40420`)
11301132
- Bug in :class:`Series` backed by :class:`DatetimeArray` or :class:`TimedeltaArray` sometimes failing to set the array's ``freq`` to ``None`` (:issue:`41425`)
11311133
- Bug in creating a :class:`Series` from a ``range`` object that does not fit in the bounds of ``int64`` dtype (:issue:`30173`)
1134+
- Bug in creating a :class:`Series` from a ``dict`` with all-tuple keys and an :class:`Index` that requires reindexing (:issue:`41707`)
11321135

11331136
.. ---------------------------------------------------------------------------
11341137

pandas/core/frame.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -8921,10 +8921,7 @@ def append(
89218921

89228922
index = Index([other.name], name=self.index.name)
89238923
idx_diff = other.index.difference(self.columns)
8924-
try:
8925-
combined_columns = self.columns.append(idx_diff)
8926-
except TypeError:
8927-
combined_columns = self.columns.astype(object).append(idx_diff)
8924+
combined_columns = self.columns.append(idx_diff)
89288925
other = (
89298926
other.reindex(combined_columns, copy=False)
89308927
.to_frame()

pandas/core/indexes/interval.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -774,9 +774,11 @@ def _get_indexer_pointwise(self, target: Index) -> tuple[np.ndarray, np.ndarray]
774774
except KeyError:
775775
missing.append(i)
776776
locs = np.array([-1])
777-
except InvalidIndexError as err:
778-
# i.e. non-scalar key
779-
raise TypeError(key) from err
777+
except InvalidIndexError:
778+
# i.e. non-scalar key e.g. a tuple.
779+
# see test_append_different_columns_types_raises
780+
missing.append(i)
781+
locs = np.array([-1])
780782

781783
indexer.append(locs)
782784

pandas/core/indexes/multi.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -2541,9 +2541,11 @@ def reindex(
25412541
elif (indexer >= 0).all():
25422542
target = self.take(indexer)
25432543
else:
2544-
# hopefully?
2545-
target = MultiIndex.from_tuples(target)
2546-
2544+
try:
2545+
target = MultiIndex.from_tuples(target)
2546+
except TypeError:
2547+
# not all tuples, see test_constructor_dict_multiindex_reindex_flat
2548+
return target, indexer
25472549
if (
25482550
preserve_names
25492551
and target.nlevels == self.nlevels

pandas/tests/indexes/multi/test_reindex.py

+11
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,14 @@ def test_reindex_empty_with_level(values):
115115
expected_indexer = np.array([], dtype=result_indexer.dtype)
116116
tm.assert_index_equal(result, expected)
117117
tm.assert_numpy_array_equal(result_indexer, expected_indexer)
118+
119+
120+
def test_reindex_not_all_tuples():
121+
keys = [("i", "i"), ("i", "j"), ("j", "i"), "j"]
122+
mi = MultiIndex.from_tuples(keys[:-1])
123+
idx = Index(keys)
124+
res, indexer = mi.reindex(idx)
125+
126+
tm.assert_index_equal(res, idx)
127+
expected = np.array([0, 1, 2, -1], dtype=np.intp)
128+
tm.assert_numpy_array_equal(indexer, expected)

pandas/tests/reshape/concat/test_append.py

+3-44
Original file line numberDiff line numberDiff line change
@@ -184,18 +184,12 @@ def test_append_preserve_index_name(self):
184184
dt.datetime(2013, 1, 3, 7, 12),
185185
]
186186
),
187+
pd.MultiIndex.from_arrays(["A B C".split(), "D E F".split()]),
187188
]
188189

189-
indexes_cannot_append_with_other = [
190-
pd.MultiIndex.from_arrays(["A B C".split(), "D E F".split()])
191-
]
192-
193-
# error: Unsupported operand types for + ("List[Index]" and "List[MultiIndex]")
194-
all_indexes = (
195-
indexes_can_append + indexes_cannot_append_with_other # type: ignore[operator]
190+
@pytest.mark.parametrize(
191+
"index", indexes_can_append, ids=lambda x: type(x).__name__
196192
)
197-
198-
@pytest.mark.parametrize("index", all_indexes, ids=lambda x: type(x).__name__)
199193
def test_append_same_columns_type(self, index):
200194
# GH18359
201195

@@ -249,41 +243,6 @@ def test_append_different_columns_types(self, df_columns, series_index):
249243
)
250244
tm.assert_frame_equal(result, expected)
251245

252-
@pytest.mark.parametrize(
253-
"index_can_append", indexes_can_append, ids=lambda x: type(x).__name__
254-
)
255-
@pytest.mark.parametrize(
256-
"index_cannot_append_with_other",
257-
indexes_cannot_append_with_other,
258-
ids=lambda x: type(x).__name__,
259-
)
260-
def test_append_different_columns_types_raises(
261-
self, index_can_append, index_cannot_append_with_other
262-
):
263-
# GH18359
264-
# Dataframe.append will raise if MultiIndex appends
265-
# or is appended to a different index type
266-
#
267-
# See also test 'test_append_different_columns_types' above for
268-
# appending without raising.
269-
270-
df = DataFrame([[1, 2, 3], [4, 5, 6]], columns=index_can_append)
271-
ser = Series([7, 8, 9], index=index_cannot_append_with_other, name=2)
272-
msg = (
273-
r"Expected tuple, got (int|long|float|str|"
274-
r"pandas._libs.interval.Interval)|"
275-
r"object of type '(int|float|Timestamp|"
276-
r"pandas._libs.interval.Interval)' has no len\(\)|"
277-
)
278-
with pytest.raises(TypeError, match=msg):
279-
df.append(ser)
280-
281-
df = DataFrame([[1, 2, 3], [4, 5, 6]], columns=index_cannot_append_with_other)
282-
ser = Series([7, 8, 9], index=index_can_append, name=2)
283-
284-
with pytest.raises(TypeError, match=msg):
285-
df.append(ser)
286-
287246
def test_append_dtype_coerce(self, sort):
288247

289248
# GH 4993

pandas/tests/series/test_constructors.py

+8
Original file line numberDiff line numberDiff line change
@@ -1717,6 +1717,14 @@ def test_constructor_dict_multiindex(self):
17171717
result = result.reindex(index=expected.index)
17181718
tm.assert_series_equal(result, expected)
17191719

1720+
def test_constructor_dict_multiindex_reindex_flat(self):
1721+
# construction involves reindexing with a MultiIndex corner case
1722+
data = {("i", "i"): 0, ("i", "j"): 1, ("j", "i"): 2, "j": np.nan}
1723+
expected = Series(data)
1724+
1725+
result = Series(expected[:-1].to_dict(), index=expected.index)
1726+
tm.assert_series_equal(result, expected)
1727+
17201728
def test_constructor_dict_timedelta_index(self):
17211729
# GH #12169 : Resample category data with timedelta index
17221730
# construct Series from dict as data and TimedeltaIndex as index

0 commit comments

Comments
 (0)