diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 74710ca48308c..0c1c835e2b70c 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -696,6 +696,7 @@ Numeric - Bug in :meth:`DataFrame.transform` would raise ``SpecificationError`` when passed a dictionary and columns were missing; will now raise a ``KeyError`` instead (:issue:`40004`) - Bug in :meth:`DataFrameGroupBy.rank` giving incorrect results with ``pct=True`` and equal values between consecutive groups (:issue:`40518`) - Bug in :meth:`Series.count` would result in an ``int32`` result on 32-bit platforms when argument ``level=None`` (:issue:`40908`) +- Bug in :meth:`Series.clip` would fail if series contains NA values and has nullable int or float as a data type (:issue:`40851`) Conversion ^^^^^^^^^^ diff --git a/pandas/core/generic.py b/pandas/core/generic.py index eba4a36315ba4..db909ef0a6822 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -7404,10 +7404,10 @@ def _clip_with_scalar(self, lower, upper, inplace: bool_t = False): with np.errstate(all="ignore"): if upper is not None: - subset = self.to_numpy() <= upper + subset = (self <= upper).to_numpy() result = result.where(subset, upper, axis=None, inplace=False) if lower is not None: - subset = self.to_numpy() >= lower + subset = (self >= lower).to_numpy() result = result.where(subset, lower, axis=None, inplace=False) if np.any(mask): diff --git a/pandas/tests/series/methods/test_clip.py b/pandas/tests/series/methods/test_clip.py index 442718d677101..6185fe6c54fa4 100644 --- a/pandas/tests/series/methods/test_clip.py +++ b/pandas/tests/series/methods/test_clip.py @@ -40,6 +40,26 @@ def test_clip_types_and_nulls(self): assert list(isna(s)) == list(isna(lower)) assert list(isna(s)) == list(isna(upper)) + def test_series_clipping_with_na_values( + self, any_nullable_numeric_dtype, nulls_fixture + ): + # Ensure that clipping method can handle NA values with out failing + # GH#40581 + + s = Series([nulls_fixture, 1.0, 3.0], dtype=any_nullable_numeric_dtype) + s_clipped_upper = s.clip(upper=2.0) + s_clipped_lower = s.clip(lower=2.0) + + expected_upper = Series( + [nulls_fixture, 1.0, 2.0], dtype=any_nullable_numeric_dtype + ) + expected_lower = Series( + [nulls_fixture, 2.0, 3.0], dtype=any_nullable_numeric_dtype + ) + + tm.assert_series_equal(s_clipped_upper, expected_upper) + tm.assert_series_equal(s_clipped_lower, expected_lower) + def test_clip_with_na_args(self): """Should process np.nan argument as None """ # GH#17276