Skip to content

Commit 2bcc465

Browse files
committed
BUG: Fix DataFrame binary arithmatic operation handling of unaligned MultiIndex columns
1 parent ca91dd4 commit 2bcc465

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

doc/source/whatsnew/v3.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,7 @@ MultiIndex
682682
- :meth:`DataFrame.melt` would not accept multiple names in ``var_name`` when the columns were a :class:`MultiIndex` (:issue:`58033`)
683683
- :meth:`MultiIndex.insert` would not insert NA value correctly at unified location of index -1 (:issue:`59003`)
684684
- :func:`MultiIndex.get_level_values` accessing a :class:`DatetimeIndex` does not carry the frequency attribute along (:issue:`58327`, :issue:`57949`)
685+
- Bug in :class:`DataFrame` arithmetic operations in case of unaligned MultiIndex columns (:issue:`60498`)
685686
-
686687

687688
I/O

pandas/core/frame.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7960,6 +7960,11 @@ def _arith_method_with_reindex(self, right: DataFrame, op) -> DataFrame:
79607960

79617961
new_left = left if lcol_indexer is None else left.iloc[:, lcol_indexer]
79627962
new_right = right if rcol_indexer is None else right.iloc[:, rcol_indexer]
7963+
7964+
# GH#60498 For MultiIndex column alignment
7965+
new_left.columns = cols
7966+
new_right.columns = cols
7967+
79637968
result = op(new_left, new_right)
79647969

79657970
# Do the join on the columns instead of using left._align_for_op
@@ -7990,6 +7995,13 @@ def _should_reindex_frame_op(self, right, op, axis: int, fill_value, level) -> b
79907995
if not isinstance(right, DataFrame):
79917996
return False
79927997

7998+
if (
7999+
isinstance(self.columns, MultiIndex)
8000+
or isinstance(right.columns, MultiIndex)
8001+
) and not self.columns.equals(right.columns):
8002+
# GH#60498 Reindex if MultiIndexe columns are not matching
8003+
return True
8004+
79938005
if fill_value is None and level is None and axis == 1:
79948006
# TODO: any other cases we should handle here?
79958007

pandas/tests/frame/test_arithmetic.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,6 +2033,31 @@ def test_arithmetic_multiindex_align():
20332033
tm.assert_frame_equal(result, expected)
20342034

20352035

2036+
def test_arithmetic_multiindex_column_align():
2037+
# GH#60498
2038+
df1 = DataFrame(
2039+
data=100,
2040+
columns=MultiIndex.from_product(
2041+
[["1A", "1B"], ["2A", "2B"]], names=["Lev1", "Lev2"]
2042+
),
2043+
index=["C1", "C2"],
2044+
)
2045+
df2 = DataFrame(
2046+
data=np.array([[0.1, 0.25], [0.2, 0.45]]),
2047+
columns=MultiIndex.from_product([["1A", "1B"]], names=["Lev1"]),
2048+
index=["C1", "C2"],
2049+
)
2050+
expected = DataFrame(
2051+
data=np.array([[10.0, 10.0, 25.0, 25.0], [20.0, 20.0, 45.0, 45.0]]),
2052+
columns=MultiIndex.from_product(
2053+
[["1A", "1B"], ["2A", "2B"]], names=["Lev1", "Lev2"]
2054+
),
2055+
index=["C1", "C2"],
2056+
)
2057+
result = df1 * df2
2058+
tm.assert_frame_equal(result, expected)
2059+
2060+
20362061
def test_bool_frame_mult_float():
20372062
# GH 18549
20382063
df = DataFrame(True, list("ab"), list("cd"))

0 commit comments

Comments
 (0)