From 606cf1e753f503a8f41d73dc2d00603ff6944be3 Mon Sep 17 00:00:00 2001 From: Praateek Mahajan Date: Tue, 21 Oct 2025 21:00:34 +0000 Subject: [PATCH] BUG: handle fill_value with ExtensionArray in flex ops --- doc/source/whatsnew/v3.0.0.rst | 1 + pandas/core/series.py | 2 +- pandas/tests/series/test_arithmetic.py | 38 ++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 259470a4f1513..3a03e0abf4ea8 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -1187,6 +1187,7 @@ ExtensionArray - Bug in comparison between object with :class:`ArrowDtype` and incompatible-dtyped (e.g. string vs bool) incorrectly raising instead of returning all-``False`` (for ``==``) or all-``True`` (for ``!=``) (:issue:`59505`) - Bug in constructing pandas data structures when passing into ``dtype`` a string of the type followed by ``[pyarrow]`` while PyArrow is not installed would raise ``NameError`` rather than ``ImportError`` (:issue:`57928`) - Bug in various :class:`DataFrame` reductions for pyarrow temporal dtypes returning incorrect dtype when result was null (:issue:`59234`) +- Fixed flex arithmetic with :class:`ExtensionArray` operands raising when ``fill_value`` was passed. (:issue:`62467`) Styler ^^^^^^ diff --git a/pandas/core/series.py b/pandas/core/series.py index f3aaee26fe470..c02a33dd35c08 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -6062,7 +6062,7 @@ def _flex_method(self, other, op, *, level=None, fill_value=None, axis: Axis = 0 if isinstance(other, Series): return self._binop(other, op, level=level, fill_value=fill_value) - elif isinstance(other, (np.ndarray, list, tuple)): + elif isinstance(other, (np.ndarray, list, tuple, ExtensionArray)): if len(other) != len(self): raise ValueError("Lengths must be equal") other = self._constructor(other, self.index, copy=False) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 7768a9a026f37..a9ed61e2c40cb 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -160,6 +160,28 @@ def _check_fill(meth, op, a, b, fill_value=0): # should accept axis=0 or axis='rows' op(a, b, axis=0) + @pytest.mark.parametrize("kind", ["datetime", "timedelta"]) + def test_rhs_extension_array_sub_with_fill_value(self, kind): + # GH:62467 + if kind == "datetime": + left = Series( + [pd.Timestamp("2025-08-20"), pd.Timestamp("2025-08-21")], + dtype=np.dtype("datetime64[ns]"), + ) + else: + left = Series( + [Timedelta(days=1), Timedelta(days=2)], + dtype=np.dtype("timedelta64[ns]"), + ) + + right = ( + left._values + ) # DatetimeArray or TimedeltaArray which is an ExtensionArray + + result = left.sub(right, fill_value=left.iloc[0]) + expected = Series(np.zeros(len(left), dtype=np.dtype("timedelta64[ns]"))) + tm.assert_series_equal(result, expected) + def test_flex_disallows_dataframe(self): # GH#46179 df = pd.DataFrame( @@ -430,6 +452,22 @@ def test_comparison_flex_alignment(self, values, op): expected = Series(values, index=list("abcd")) tm.assert_series_equal(result, expected) + @pytest.mark.parametrize( + "left", + [ + Series(Categorical(["a", "b", "a"])), + Series(pd.period_range("2020Q1", periods=3, freq="Q")), + ], + ids=["categorical", "period"], + ) + def test_rhs_extension_array_eq_with_fill_value(self, left): + # GH:#62467 + right = left._values # this is an ExtensionArray + + result = left.eq(right, fill_value=left.iloc[0]) + expected = Series([True, True, True]) + tm.assert_series_equal(result, expected) + @pytest.mark.parametrize( "values, op, fill_value", [