Skip to content

Commit 606cf1e

Browse files
BUG: handle fill_value with ExtensionArray in flex ops
1 parent 9234ed5 commit 606cf1e

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

doc/source/whatsnew/v3.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,7 @@ ExtensionArray
11871187
- 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`)
11881188
- 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`)
11891189
- Bug in various :class:`DataFrame` reductions for pyarrow temporal dtypes returning incorrect dtype when result was null (:issue:`59234`)
1190+
- Fixed flex arithmetic with :class:`ExtensionArray` operands raising when ``fill_value`` was passed. (:issue:`62467`)
11901191

11911192
Styler
11921193
^^^^^^

pandas/core/series.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6062,7 +6062,7 @@ def _flex_method(self, other, op, *, level=None, fill_value=None, axis: Axis = 0
60626062

60636063
if isinstance(other, Series):
60646064
return self._binop(other, op, level=level, fill_value=fill_value)
6065-
elif isinstance(other, (np.ndarray, list, tuple)):
6065+
elif isinstance(other, (np.ndarray, list, tuple, ExtensionArray)):
60666066
if len(other) != len(self):
60676067
raise ValueError("Lengths must be equal")
60686068
other = self._constructor(other, self.index, copy=False)

pandas/tests/series/test_arithmetic.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,28 @@ def _check_fill(meth, op, a, b, fill_value=0):
160160
# should accept axis=0 or axis='rows'
161161
op(a, b, axis=0)
162162

163+
@pytest.mark.parametrize("kind", ["datetime", "timedelta"])
164+
def test_rhs_extension_array_sub_with_fill_value(self, kind):
165+
# GH:62467
166+
if kind == "datetime":
167+
left = Series(
168+
[pd.Timestamp("2025-08-20"), pd.Timestamp("2025-08-21")],
169+
dtype=np.dtype("datetime64[ns]"),
170+
)
171+
else:
172+
left = Series(
173+
[Timedelta(days=1), Timedelta(days=2)],
174+
dtype=np.dtype("timedelta64[ns]"),
175+
)
176+
177+
right = (
178+
left._values
179+
) # DatetimeArray or TimedeltaArray which is an ExtensionArray
180+
181+
result = left.sub(right, fill_value=left.iloc[0])
182+
expected = Series(np.zeros(len(left), dtype=np.dtype("timedelta64[ns]")))
183+
tm.assert_series_equal(result, expected)
184+
163185
def test_flex_disallows_dataframe(self):
164186
# GH#46179
165187
df = pd.DataFrame(
@@ -430,6 +452,22 @@ def test_comparison_flex_alignment(self, values, op):
430452
expected = Series(values, index=list("abcd"))
431453
tm.assert_series_equal(result, expected)
432454

455+
@pytest.mark.parametrize(
456+
"left",
457+
[
458+
Series(Categorical(["a", "b", "a"])),
459+
Series(pd.period_range("2020Q1", periods=3, freq="Q")),
460+
],
461+
ids=["categorical", "period"],
462+
)
463+
def test_rhs_extension_array_eq_with_fill_value(self, left):
464+
# GH:#62467
465+
right = left._values # this is an ExtensionArray
466+
467+
result = left.eq(right, fill_value=left.iloc[0])
468+
expected = Series([True, True, True])
469+
tm.assert_series_equal(result, expected)
470+
433471
@pytest.mark.parametrize(
434472
"values, op, fill_value",
435473
[

0 commit comments

Comments
 (0)