diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 4c3e53ddcfa26..95825c814d57e 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -749,6 +749,7 @@ Numeric - Bug in :class:`DataFrame` arithmetic ops with a subclass whose :meth:`_constructor` attribute is a callable other than the subclass itself (:issue:`43201`) - Bug in arithmetic operations involving :class:`RangeIndex` where the result would have the incorrect ``name`` (:issue:`43962`) - Bug in arithmetic operations involving :class:`Series` where the result could have the incorrect ``name`` when the operands having matching NA or matching tuple names (:issue:`44459`) +- Bug in :meth:`Series.clip` raising if bounds are a :class:`Series` with ``NA`` values for datetimes or nullable integer dtypes (:issue:`44785`) - Bug in division with ``IntegerDtype`` or ``BooleanDtype`` array and NA scalar incorrectly raising (:issue:`44685`) - Bug in multiplying a :class:`Series` with ``FloatingDtype`` with a timedelta-like scalar incorrectly raising (:issue:`44772`) - diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 1e25b0f4eb176..ba69847c55814 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -7381,7 +7381,14 @@ def _clip_with_one_bound(self, threshold, method, axis, inplace): # GH 40420 # Treat missing thresholds as no bounds, not clipping the values if is_list_like(threshold): - fill_value = np.inf if method.__name__ == "le" else -np.inf + method_name_le = method.__name__ == "le" + if self.ndim == 1 and ( + is_extension_array_dtype(self.dtype) + or is_datetime64_any_dtype(self.dtype) + ): + fill_value = self.max() if method_name_le else self.min() + else: + fill_value = np.inf if method_name_le else -np.inf threshold_inf = threshold.fillna(fill_value) else: threshold_inf = threshold diff --git a/pandas/tests/series/methods/test_clip.py b/pandas/tests/series/methods/test_clip.py index bc6d5aeb0a581..8a1f1bdaea091 100644 --- a/pandas/tests/series/methods/test_clip.py +++ b/pandas/tests/series/methods/test_clip.py @@ -89,6 +89,15 @@ def test_clip_against_series(self): tm.assert_series_equal(s.clip(lower, upper), Series([1.0, 2.0, 3.5])) tm.assert_series_equal(s.clip(1.5, upper), Series([1.5, 1.5, 3.5])) + @pytest.mark.parametrize("bound_values", [[pd.NA, 1], [1, pd.NA]]) + def test_clip_against_series_ea_int_dtype(self, any_int_ea_dtype, bound_values): + # GH#44785 + ser = Series([1, 1], dtype=any_int_ea_dtype) + bounds = Series(bound_values, dtype=any_int_ea_dtype) + expected = ser.copy() + result = ser.clip(bounds) + tm.assert_series_equal(result, expected) + @pytest.mark.parametrize("inplace", [True, False]) @pytest.mark.parametrize("upper", [[1, 2, 3], np.asarray([1, 2, 3])]) def test_clip_against_list_like(self, inplace, upper): @@ -138,6 +147,19 @@ def test_clip_with_timestamps_and_oob_datetimes(self): tm.assert_series_equal(result, expected) + def test_clip_timestamp_and_na(self, tz_naive_fixture): + # GH#44785 + ser = Series([Timestamp("1970-01-01", tz=tz_naive_fixture)] * 2) + expected = ser.copy() + + bounds = Series([pd.NaT, Timestamp("1970-01-01", tz=tz_naive_fixture)]) + result = ser.clip(bounds) + tm.assert_series_equal(result, expected) + + bounds = Series([Timestamp("1970-01-01", tz=tz_naive_fixture), pd.NaT]) + result = ser.clip(bounds) + tm.assert_series_equal(result, expected) + def test_clip_pos_args_deprecation(self): # https://github.com/pandas-dev/pandas/issues/41485 ser = Series([1, 2, 3])