From 8c7ede5109b0460127bc84c4091020f58e849dce Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 2 Nov 2022 16:06:49 -0700 Subject: [PATCH 1/2] DEPR: maybe_promote with pydate --- doc/source/whatsnew/v2.0.0.rst | 1 + pandas/core/dtypes/cast.py | 42 ++++------------------ pandas/tests/dtypes/cast/test_promote.py | 12 +++---- pandas/tests/frame/methods/test_reindex.py | 14 ++++---- 4 files changed, 20 insertions(+), 49 deletions(-) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index d71160cdbc369..90251343a5700 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -302,6 +302,7 @@ Removal of prior version deprecations/changes - Changed behavior of empty data passed into :class:`Series`; the default dtype will be ``object`` instead of ``float64`` (:issue:`29405`) - Changed the behavior of :meth:`DatetimeIndex.union`, :meth:`DatetimeIndex.intersection`, and :meth:`DatetimeIndex.symmetric_difference` with mismatched timezones to convert to UTC instead of casting to object dtype (:issue:`39328`) - Changed the behavior of :func:`to_datetime` with argument "now" with ``utc=False`` to match ``Timestamp("now")`` (:issue:`18705`) +- Changed the behavior of :meth:`.reindex` with a ``datetime64`` dtype and a ``datetime.date`` object for ``fill_value``; these are no longer considered equivalent to ``datetime.datetime`` objects so the reindex casts to object dtype (:issue:`39767`) - Changed behavior of :meth:`SparseArray.astype` when given a dtype that is not explicitly ``SparseDtype``, cast to the exact requested dtype rather than silently using a ``SparseDtype`` instead (:issue:`34457`) - Changed behavior of :class:`DataFrame` constructor given floating-point ``data`` and an integer ``dtype``, when the data cannot be cast losslessly, the floating point dtype is retained, matching :class:`Series` behavior (:issue:`41170`) - Changed behavior of :class:`Index` constructor when given a ``np.ndarray`` with object-dtype containing numeric entries; this now retains object dtype rather than inferring a numeric dtype, consistent with :class:`Series` behavior (:issue:`42870`) diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index a75448347233c..c53adb5735d84 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -5,7 +5,6 @@ from __future__ import annotations from datetime import ( - date, datetime, timedelta, ) @@ -615,41 +614,14 @@ def _maybe_promote(dtype: np.dtype, fill_value=np.nan): if inferred == dtype: return dtype, fv - # TODO(2.0): once this deprecation is enforced, this whole case - # becomes equivalent to: - # dta = DatetimeArray._from_sequence([], dtype="M8[ns]") - # try: - # fv = dta._validate_setitem_value(fill_value) - # return dta.dtype, fv - # except (ValueError, TypeError): - # return _dtype_obj, fill_value - if isinstance(fill_value, date) and not isinstance(fill_value, datetime): - # deprecate casting of date object to match infer_dtype_from_scalar - # and DatetimeArray._validate_setitem_value - try: - fv = Timestamp(fill_value).to_datetime64() - except OutOfBoundsDatetime: - pass - else: - warnings.warn( - "Using a `date` object for fill_value with `datetime64[ns]` " - "dtype is deprecated. In a future version, this will be cast " - "to object dtype. Pass `fill_value=Timestamp(date_obj)` instead.", - FutureWarning, - stacklevel=find_stack_level(), - ) - return dtype, fv - elif isinstance(fill_value, str): - try: - # explicitly wrap in str to convert np.str_ - fv = Timestamp(str(fill_value)) - except (ValueError, TypeError): - pass - else: - if isna(fv) or fv.tz is None: - return dtype, fv.asm8 + from pandas.core.arrays import DatetimeArray - return np.dtype("object"), fill_value + dta = DatetimeArray._from_sequence([], dtype="M8[ns]") + try: + fv = dta._validate_setitem_value(fill_value) + return dta.dtype, fv + except (ValueError, TypeError): + return _dtype_obj, fill_value elif issubclass(dtype.type, np.timedelta64): inferred, fv = infer_dtype_from_scalar(fill_value, pandas_dtype=True) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index bfecbbbfc0435..c54e99f2c2225 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -25,7 +25,6 @@ from pandas.core.dtypes.missing import isna import pandas as pd -import pandas._testing as tm @pytest.fixture( @@ -403,15 +402,12 @@ def test_maybe_promote_any_with_datetime64(any_numpy_dtype_reduced, fill_value): expected_dtype = np.dtype(object) exp_val_for_scalar = fill_value - warn = None - msg = "Using a `date` object for fill_value" if type(fill_value) is datetime.date and dtype.kind == "M": - # Casting date to dt64 is deprecated - warn = FutureWarning + # Casting date to dt64 is deprecated, in 2.0 enforced to cast to object + expected_dtype = np.dtype(object) + exp_val_for_scalar = fill_value - with tm.assert_produces_warning(warn, match=msg): - # stacklevel is chosen to make sense when called from higher-level functions - _check_promote(dtype, fill_value, expected_dtype, exp_val_for_scalar) + _check_promote(dtype, fill_value, expected_dtype, exp_val_for_scalar) @pytest.mark.parametrize( diff --git a/pandas/tests/frame/methods/test_reindex.py b/pandas/tests/frame/methods/test_reindex.py index a132519970721..b30bd69806a94 100644 --- a/pandas/tests/frame/methods/test_reindex.py +++ b/pandas/tests/frame/methods/test_reindex.py @@ -136,30 +136,32 @@ def test_reindex_copies(self): @td.skip_array_manager_not_yet_implemented def test_reindex_date_fill_value(self): - # passing date to dt64 is deprecated + # passing date to dt64 is deprecated; enforced in 2.0 to cast to object arr = date_range("2016-01-01", periods=6).values.reshape(3, 2) df = DataFrame(arr, columns=["A", "B"], index=range(3)) ts = df.iloc[0, 0] fv = ts.date() - with tm.assert_produces_warning(FutureWarning): - res = df.reindex(index=range(4), columns=["A", "B", "C"], fill_value=fv) + res = df.reindex(index=range(4), columns=["A", "B", "C"], fill_value=fv) expected = DataFrame( - {"A": df["A"].tolist() + [ts], "B": df["B"].tolist() + [ts], "C": [ts] * 4} + {"A": df["A"].tolist() + [fv], "B": df["B"].tolist() + [fv], "C": [fv] * 4}, + dtype=object, ) tm.assert_frame_equal(res, expected) # only reindexing rows - with tm.assert_produces_warning(FutureWarning): - res = df.reindex(index=range(4), fill_value=fv) + res = df.reindex(index=range(4), fill_value=fv) tm.assert_frame_equal(res, expected[["A", "B"]]) # same with a datetime-castable str res = df.reindex( index=range(4), columns=["A", "B", "C"], fill_value="2016-01-01" ) + expected = DataFrame( + {"A": df["A"].tolist() + [ts], "B": df["B"].tolist() + [ts], "C": [ts] * 4}, + ) tm.assert_frame_equal(res, expected) def test_reindex_with_multi_index(self): From d50cf334120023f59954c115ec819764d53b0fea Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 4 Nov 2022 13:09:19 -0700 Subject: [PATCH 2/2] fix whatsnew --- doc/source/whatsnew/v2.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index 2a071532e16bc..5d3f25a9a2570 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -403,7 +403,7 @@ Removal of prior version deprecations/changes - Changed behavior of empty data passed into :class:`Series`; the default dtype will be ``object`` instead of ``float64`` (:issue:`29405`) - Changed the behavior of :meth:`DatetimeIndex.union`, :meth:`DatetimeIndex.intersection`, and :meth:`DatetimeIndex.symmetric_difference` with mismatched timezones to convert to UTC instead of casting to object dtype (:issue:`39328`) - Changed the behavior of :func:`to_datetime` with argument "now" with ``utc=False`` to match ``Timestamp("now")`` (:issue:`18705`) -- Changed the behavior of :meth:`.reindex` with a ``datetime64`` dtype and a ``datetime.date`` object for ``fill_value``; these are no longer considered equivalent to ``datetime.datetime`` objects so the reindex casts to object dtype (:issue:`39767`) +- Changed the behavior of :meth:`Index.reindex`, :meth:`Series.reindex`, and :meth:`DataFrame.reindex` with a ``datetime64`` dtype and a ``datetime.date`` object for ``fill_value``; these are no longer considered equivalent to ``datetime.datetime`` objects so the reindex casts to object dtype (:issue:`39767`) - Changed behavior of :meth:`SparseArray.astype` when given a dtype that is not explicitly ``SparseDtype``, cast to the exact requested dtype rather than silently using a ``SparseDtype`` instead (:issue:`34457`) - Changed behavior of :meth:`Index.ravel` to return a view on the original :class:`Index` instead of a ``np.ndarray`` (:issue:`36900`) - Changed behavior of :meth:`Index.to_frame` with explicit ``name=None`` to use ``None`` for the column name instead of the index's name or default ``0`` (:issue:`45523`)