diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index ba5334b2f4fa8..2f112990cab55 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -279,6 +279,7 @@ Deprecations - Deprecated logical operation between two non boolean :class:`Series` with different indexes always coercing the result to bool dtype. In a future version, this will maintain the return type of the inputs. (:issue:`52500`, :issue:`52538`) - Deprecated :func:`value_counts`, use ``pd.Series(obj).value_counts()`` instead (:issue:`47862`) - Deprecated :meth:`Series.first` and :meth:`DataFrame.first` (please create a mask and filter using ``.loc`` instead) (:issue:`45908`) +- Deprecated :meth:`Series.interpolate` and :meth:`DataFrame.interpolate` for object-dtype (:issue:`53631`) - Deprecated allowing ``downcast`` keyword other than ``None``, ``False``, "infer", or a dict with these as values in :meth:`Series.fillna`, :meth:`DataFrame.fillna` (:issue:`40988`) - Deprecated allowing arbitrary ``fill_value`` in :class:`SparseDtype`, in a future version the ``fill_value`` will need to be compatible with the ``dtype.subtype``, either a scalar that can be held by that subtype or ``NaN`` for integer or bool subtypes (:issue:`23124`) - Deprecated behavior of :func:`assert_series_equal` and :func:`assert_frame_equal` considering NA-like values (e.g. ``NaN`` vs ``None`` as equivalent) (:issue:`52081`) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 6e71f32336546..711e552f262ac 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -7882,6 +7882,18 @@ def interpolate( FutureWarning, stacklevel=find_stack_level(), ) + elif np.any(obj.dtypes == object): + # GH#53631 + if not (obj.ndim == 2 and np.all(obj.dtypes == object)): + # don't warn in cases that already raise + warnings.warn( + f"{type(self).__name__}.interpolate with object dtype is " + "deprecated and will raise in a future version. Call " + "obj.infer_objects(copy=False) before interpolating instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) + if method not in fillna_methods: axis = self._info_axis_number diff --git a/pandas/tests/copy_view/test_interp_fillna.py b/pandas/tests/copy_view/test_interp_fillna.py index 3939fd45e1a0f..71023a538a2c3 100644 --- a/pandas/tests/copy_view/test_interp_fillna.py +++ b/pandas/tests/copy_view/test_interp_fillna.py @@ -113,7 +113,9 @@ def test_interpolate_cleaned_fill_method(using_copy_on_write): df = DataFrame({"a": ["a", np.nan, "c"], "b": 1}) df_orig = df.copy() - result = df.interpolate(method="linear") + msg = "DataFrame.interpolate with object dtype" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = df.interpolate(method="linear") if using_copy_on_write: assert np.shares_memory(get_array(result, "a"), get_array(df, "a")) diff --git a/pandas/tests/frame/methods/test_interpolate.py b/pandas/tests/frame/methods/test_interpolate.py index 5a3d53eb96a52..c834357d38036 100644 --- a/pandas/tests/frame/methods/test_interpolate.py +++ b/pandas/tests/frame/methods/test_interpolate.py @@ -69,7 +69,9 @@ def test_interp_basic(self, using_copy_on_write): "D": list("abcd"), } ) - result = df.interpolate() + msg = "DataFrame.interpolate with object dtype" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = df.interpolate() tm.assert_frame_equal(result, expected) # check we didn't operate inplace GH#45791 @@ -82,7 +84,8 @@ def test_interp_basic(self, using_copy_on_write): assert not np.shares_memory(cvalues, result["C"]._values) assert not np.shares_memory(dvalues, result["D"]._values) - res = df.interpolate(inplace=True) + with tm.assert_produces_warning(FutureWarning, match=msg): + res = df.interpolate(inplace=True) assert res is None tm.assert_frame_equal(df, expected) @@ -100,7 +103,9 @@ def test_interp_basic_with_non_range_index(self): } ) - result = df.set_index("C").interpolate() + msg = "DataFrame.interpolate with object dtype" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = df.set_index("C").interpolate() expected = df.set_index("C") expected.loc[3, "A"] = 3 expected.loc[5, "B"] = 9 @@ -120,7 +125,6 @@ def test_interp_bad_method(self): "A": [1, 2, np.nan, 4], "B": [1, 4, 9, np.nan], "C": [1, 2, 3, 5], - "D": list("abcd"), } ) msg = ( diff --git a/pandas/tests/series/methods/test_interpolate.py b/pandas/tests/series/methods/test_interpolate.py index 29f2f9f97aafe..8c4c5524ac3be 100644 --- a/pandas/tests/series/methods/test_interpolate.py +++ b/pandas/tests/series/methods/test_interpolate.py @@ -846,8 +846,10 @@ def test_interpolate_unsorted_index(self, ascending, expected_values): expected = Series(data=expected_values, index=expected_values, dtype=float) tm.assert_series_equal(result, expected) - def test_interpolate_afreq_raises(self): + def test_interpolate_asfreq_raises(self): ser = Series(["a", None, "b"], dtype=object) + msg2 = "Series.interpolate with object dtype" msg = "Invalid fill method" with pytest.raises(ValueError, match=msg): - ser.interpolate(method="asfreq") + with tm.assert_produces_warning(FutureWarning, match=msg2): + ser.interpolate(method="asfreq")