Skip to content

Commit 040150d

Browse files
mroeschkepmhatre1
authored andcommitted
CLN: shift with freq and fill_value, to_pydatetime returning Series (pandas-dev#57425)
* Enforce shift with freq and fill_value * Enforce to_pydatetime change * Change doc example * Use to_numpy * Use _values * Fix doctest
1 parent dc7ac67 commit 040150d

File tree

12 files changed

+44
-106
lines changed

12 files changed

+44
-106
lines changed

doc/source/whatsnew/v3.0.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ Deprecations
112112
Removal of prior version deprecations/changes
113113
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
114114
- :func:`read_excel`, :func:`read_json`, :func:`read_html`, and :func:`read_xml` no longer accept raw string or byte representation of the data. That type of data must be wrapped in a :py:class:`StringIO` or :py:class:`BytesIO` (:issue:`53767`)
115+
- :meth:`Series.dt.to_pydatetime` now returns a :class:`Series` of :py:class:`datetime.datetime` objects (:issue:`52459`)
115116
- All arguments except ``name`` in :meth:`Index.rename` are now keyword only (:issue:`56493`)
116117
- All arguments except the first ``path``-like argument in IO writers are now keyword only (:issue:`54229`)
117118
- All arguments in :meth:`Index.sort_values` are now keyword only (:issue:`56493`)
@@ -120,6 +121,7 @@ Removal of prior version deprecations/changes
120121
- Enforced deprecation disallowing parsing datetimes with mixed time zones unless user passes ``utc=True`` to :func:`to_datetime` (:issue:`57275`)
121122
- Enforced silent-downcasting deprecation for :ref:`all relevant methods <whatsnew_220.silent_downcasting>` (:issue:`54710`)
122123
- In :meth:`DataFrame.stack`, the default value of ``future_stack`` is now ``True``; specifying ``False`` will raise a ``FutureWarning`` (:issue:`55448`)
124+
- Passing both ``freq`` and ``fill_value`` in :meth:`DataFrame.shift` and :meth:`Series.shift` and :meth:`.DataFrameGroupBy.shift` now raises a ``ValueError`` (:issue:`54818`)
123125
- Removed :meth:`DateOffset.is_anchored` and :meth:`offsets.Tick.is_anchored` (:issue:`56594`)
124126
- Removed ``DataFrame.applymap``, ``Styler.applymap`` and ``Styler.applymap_index`` (:issue:`52364`)
125127
- Removed ``DataFrame.bool`` and ``Series.bool`` (:issue:`51756`)

pandas/conftest.py

-4
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,6 @@ def pytest_collection_modifyitems(items, config) -> None:
158158
("SeriesGroupBy.idxmax", "The behavior of Series.idxmax"),
159159
# Docstring divides by zero to show behavior difference
160160
("missing.mask_zero_div_zero", "divide by zero encountered"),
161-
(
162-
"to_pydatetime",
163-
"The behavior of DatetimeProperties.to_pydatetime is deprecated",
164-
),
165161
(
166162
"pandas.core.generic.NDFrame.first",
167163
"first is deprecated and will be removed in a future version. "

pandas/core/arrays/arrow/array.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -2918,7 +2918,9 @@ def _dt_month_name(self, locale: str | None = None) -> Self:
29182918
locale = "C"
29192919
return type(self)(pc.strftime(self._pa_array, format="%B", locale=locale))
29202920

2921-
def _dt_to_pydatetime(self) -> np.ndarray:
2921+
def _dt_to_pydatetime(self) -> Series:
2922+
from pandas import Series
2923+
29222924
if pa.types.is_date(self.dtype.pyarrow_dtype):
29232925
raise ValueError(
29242926
f"to_pydatetime cannot be called with {self.dtype.pyarrow_dtype} type. "
@@ -2927,7 +2929,7 @@ def _dt_to_pydatetime(self) -> np.ndarray:
29272929
data = self._pa_array.to_pylist()
29282930
if self._dtype.pyarrow_dtype.unit == "ns":
29292931
data = [None if ts is None else ts.to_pydatetime(warn=False) for ts in data]
2930-
return np.array(data, dtype=object)
2932+
return Series(data, dtype=object)
29312933

29322934
def _dt_tz_localize(
29332935
self,

pandas/core/frame.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -5588,14 +5588,9 @@ def shift(
55885588
) -> DataFrame:
55895589
if freq is not None and fill_value is not lib.no_default:
55905590
# GH#53832
5591-
warnings.warn(
5592-
"Passing a 'freq' together with a 'fill_value' silently ignores "
5593-
"the fill_value and is deprecated. This will raise in a future "
5594-
"version.",
5595-
FutureWarning,
5596-
stacklevel=find_stack_level(),
5591+
raise ValueError(
5592+
"Passing a 'freq' together with a 'fill_value' is not allowed."
55975593
)
5598-
fill_value = lib.no_default
55995594

56005595
if self.empty:
56015596
return self.copy()

pandas/core/generic.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -10269,14 +10269,9 @@ def shift(
1026910269

1027010270
if freq is not None and fill_value is not lib.no_default:
1027110271
# GH#53832
10272-
warnings.warn(
10273-
"Passing a 'freq' together with a 'fill_value' silently ignores "
10274-
"the fill_value and is deprecated. This will raise in a future "
10275-
"version.",
10276-
FutureWarning,
10277-
stacklevel=find_stack_level(),
10272+
raise ValueError(
10273+
"Passing a 'freq' together with a 'fill_value' is not allowed."
1027810274
)
10279-
fill_value = lib.no_default
1028010275

1028110276
if periods == 0:
1028210277
return self.copy(deep=False)

pandas/core/indexes/accessors.py

+12-32
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@
88
NoReturn,
99
cast,
1010
)
11-
import warnings
1211

1312
import numpy as np
1413

1514
from pandas._libs import lib
16-
from pandas.util._exceptions import find_stack_level
1715

1816
from pandas.core.dtypes.common import (
1917
is_integer_dtype,
@@ -213,16 +211,8 @@ def _delegate_method(self, name: str, *args, **kwargs):
213211
def to_pytimedelta(self):
214212
return cast(ArrowExtensionArray, self._parent.array)._dt_to_pytimedelta()
215213

216-
def to_pydatetime(self):
214+
def to_pydatetime(self) -> Series:
217215
# GH#20306
218-
warnings.warn(
219-
f"The behavior of {type(self).__name__}.to_pydatetime is deprecated, "
220-
"in a future version this will return a Series containing python "
221-
"datetime objects instead of an ndarray. To retain the old behavior, "
222-
"call `np.array` on the result",
223-
FutureWarning,
224-
stacklevel=find_stack_level(),
225-
)
226216
return cast(ArrowExtensionArray, self._parent.array)._dt_to_pydatetime()
227217

228218
def isocalendar(self) -> DataFrame:
@@ -318,15 +308,9 @@ class DatetimeProperties(Properties):
318308
Raises TypeError if the Series does not contain datetimelike values.
319309
"""
320310

321-
def to_pydatetime(self) -> np.ndarray:
311+
def to_pydatetime(self) -> Series:
322312
"""
323-
Return the data as an array of :class:`datetime.datetime` objects.
324-
325-
.. deprecated:: 2.1.0
326-
327-
The current behavior of dt.to_pydatetime is deprecated.
328-
In a future version this will return a Series containing python
329-
datetime objects instead of a ndarray.
313+
Return the data as a Series of :class:`datetime.datetime` objects.
330314
331315
Timezone information is retained if present.
332316
@@ -353,8 +337,9 @@ def to_pydatetime(self) -> np.ndarray:
353337
dtype: datetime64[ns]
354338
355339
>>> s.dt.to_pydatetime()
356-
array([datetime.datetime(2018, 3, 10, 0, 0),
357-
datetime.datetime(2018, 3, 11, 0, 0)], dtype=object)
340+
0 2018-03-10 00:00:00
341+
1 2018-03-11 00:00:00
342+
dtype: object
358343
359344
pandas' nanosecond precision is truncated to microseconds.
360345
@@ -365,19 +350,14 @@ def to_pydatetime(self) -> np.ndarray:
365350
dtype: datetime64[ns]
366351
367352
>>> s.dt.to_pydatetime()
368-
array([datetime.datetime(2018, 3, 10, 0, 0),
369-
datetime.datetime(2018, 3, 10, 0, 0)], dtype=object)
353+
0 2018-03-10 00:00:00
354+
1 2018-03-10 00:00:00
355+
dtype: object
370356
"""
371357
# GH#20306
372-
warnings.warn(
373-
f"The behavior of {type(self).__name__}.to_pydatetime is deprecated, "
374-
"in a future version this will return a Series containing python "
375-
"datetime objects instead of an ndarray. To retain the old behavior, "
376-
"call `np.array` on the result",
377-
FutureWarning,
378-
stacklevel=find_stack_level(),
379-
)
380-
return self._get_values().to_pydatetime()
358+
from pandas import Series
359+
360+
return Series(self._get_values().to_pydatetime(), dtype=object)
381361

382362
@property
383363
def freq(self):

pandas/io/sql.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -1037,10 +1037,7 @@ def insert_data(self) -> tuple[list[str], list[np.ndarray]]:
10371037
# GH#53854 to_pydatetime not supported for pyarrow date dtypes
10381038
d = ser._values.to_numpy(dtype=object)
10391039
else:
1040-
with warnings.catch_warnings():
1041-
warnings.filterwarnings("ignore", category=FutureWarning)
1042-
# GH#52459 to_pydatetime will return Index[object]
1043-
d = np.asarray(ser.dt.to_pydatetime(), dtype=object)
1040+
d = ser.dt.to_pydatetime()._values
10441041
else:
10451042
d = ser._values.to_pydatetime()
10461043
elif ser.dtype.kind == "m":

pandas/tests/extension/test_arrow.py

+8-15
Original file line numberDiff line numberDiff line change
@@ -2676,18 +2676,13 @@ def test_dt_to_pydatetime():
26762676
# GH 51859
26772677
data = [datetime(2022, 1, 1), datetime(2023, 1, 1)]
26782678
ser = pd.Series(data, dtype=ArrowDtype(pa.timestamp("ns")))
2679+
result = ser.dt.to_pydatetime()
2680+
expected = pd.Series(data, dtype=object)
2681+
tm.assert_series_equal(result, expected)
2682+
assert all(type(expected.iloc[i]) is datetime for i in range(len(expected)))
26792683

2680-
msg = "The behavior of ArrowTemporalProperties.to_pydatetime is deprecated"
2681-
with tm.assert_produces_warning(FutureWarning, match=msg):
2682-
result = ser.dt.to_pydatetime()
2683-
expected = np.array(data, dtype=object)
2684-
tm.assert_numpy_array_equal(result, expected)
2685-
assert all(type(res) is datetime for res in result)
2686-
2687-
msg = "The behavior of DatetimeProperties.to_pydatetime is deprecated"
2688-
with tm.assert_produces_warning(FutureWarning, match=msg):
2689-
expected = ser.astype("datetime64[ns]").dt.to_pydatetime()
2690-
tm.assert_numpy_array_equal(result, expected)
2684+
expected = ser.astype("datetime64[ns]").dt.to_pydatetime()
2685+
tm.assert_series_equal(result, expected)
26912686

26922687

26932688
@pytest.mark.parametrize("date_type", [32, 64])
@@ -2697,10 +2692,8 @@ def test_dt_to_pydatetime_date_error(date_type):
26972692
[date(2022, 12, 31)],
26982693
dtype=ArrowDtype(getattr(pa, f"date{date_type}")()),
26992694
)
2700-
msg = "The behavior of ArrowTemporalProperties.to_pydatetime is deprecated"
2701-
with tm.assert_produces_warning(FutureWarning, match=msg):
2702-
with pytest.raises(ValueError, match="to_pydatetime cannot be called with"):
2703-
ser.dt.to_pydatetime()
2695+
with pytest.raises(ValueError, match="to_pydatetime cannot be called with"):
2696+
ser.dt.to_pydatetime()
27042697

27052698

27062699
def test_dt_tz_localize_unsupported_tz_options():

pandas/tests/frame/methods/test_shift.py

+4-14
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,20 @@ def test_shift_axis1_with_valid_fill_value_one_array(self):
3030
expected2 = DataFrame([12345] * 5, dtype="Float64")
3131
tm.assert_frame_equal(res2, expected2)
3232

33-
def test_shift_deprecate_freq_and_fill_value(self, frame_or_series):
33+
def test_shift_disallow_freq_and_fill_value(self, frame_or_series):
3434
# Can't pass both!
3535
obj = frame_or_series(
3636
np.random.default_rng(2).standard_normal(5),
3737
index=date_range("1/1/2000", periods=5, freq="h"),
3838
)
3939

40-
msg = (
41-
"Passing a 'freq' together with a 'fill_value' silently ignores the "
42-
"fill_value"
43-
)
44-
with tm.assert_produces_warning(FutureWarning, match=msg):
40+
msg = "Passing a 'freq' together with a 'fill_value'"
41+
with pytest.raises(ValueError, match=msg):
4542
obj.shift(1, fill_value=1, freq="h")
4643

4744
if frame_or_series is DataFrame:
4845
obj.columns = date_range("1/1/2000", periods=1, freq="h")
49-
with tm.assert_produces_warning(FutureWarning, match=msg):
46+
with pytest.raises(ValueError, match=msg):
5047
obj.shift(1, axis=1, fill_value=1, freq="h")
5148

5249
@pytest.mark.parametrize(
@@ -717,13 +714,6 @@ def test_shift_with_iterable_freq_and_fill_value(self):
717714
df.shift(1, freq="h"),
718715
)
719716

720-
msg = (
721-
"Passing a 'freq' together with a 'fill_value' silently ignores the "
722-
"fill_value"
723-
)
724-
with tm.assert_produces_warning(FutureWarning, match=msg):
725-
df.shift([1, 2], fill_value=1, freq="h")
726-
727717
def test_shift_with_iterable_check_other_arguments(self):
728718
# GH#44424
729719
data = {"a": [1, 2], "b": [4, 5]}

pandas/tests/groupby/methods/test_groupby_shift_diff.py

+5-10
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,12 @@ def test_shift_periods_freq():
167167
tm.assert_frame_equal(result, expected)
168168

169169

170-
def test_shift_deprecate_freq_and_fill_value():
170+
def test_shift_disallow_freq_and_fill_value():
171171
# GH 53832
172172
data = {"a": [1, 2, 3, 4, 5, 6], "b": [0, 0, 0, 1, 1, 1]}
173173
df = DataFrame(data, index=date_range(start="20100101", periods=6))
174-
msg = (
175-
"Passing a 'freq' together with a 'fill_value' silently ignores the fill_value"
176-
)
177-
with tm.assert_produces_warning(FutureWarning, match=msg):
174+
msg = "Passing a 'freq' together with a 'fill_value'"
175+
with pytest.raises(ValueError, match=msg):
178176
df.groupby(df.index).shift(periods=-2, freq="D", fill_value="1")
179177

180178

@@ -247,9 +245,6 @@ def test_group_shift_with_multiple_periods_and_both_fill_and_freq_deprecated():
247245
{"a": [1, 2, 3, 4, 5], "b": [True, True, False, False, True]},
248246
index=date_range("1/1/2000", periods=5, freq="h"),
249247
)
250-
msg = (
251-
"Passing a 'freq' together with a 'fill_value' silently ignores the "
252-
"fill_value"
253-
)
254-
with tm.assert_produces_warning(FutureWarning, match=msg):
248+
msg = "Passing a 'freq' together with a 'fill_value'"
249+
with pytest.raises(ValueError, match=msg):
255250
df.groupby("b")[["a"]].shift([1, 2], fill_value=1, freq="h")

pandas/tests/series/accessors/test_cat_accessor.py

-3
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,6 @@ def test_dt_accessor_api_for_categorical(self, idx):
200200
if func == "to_period" and getattr(idx, "tz", None) is not None:
201201
# dropping TZ
202202
warn_cls.append(UserWarning)
203-
if func == "to_pydatetime":
204-
# deprecated to return Index[object]
205-
warn_cls.append(FutureWarning)
206203
if warn_cls:
207204
warn_cls = tuple(warn_cls)
208205
else:

pandas/tests/series/accessors/test_dt_accessor.py

+4-8
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,8 @@ def test_dt_namespace_accessor_datetime64(self, freq):
114114
for prop in ok_for_dt_methods:
115115
getattr(ser.dt, prop)
116116

117-
msg = "The behavior of DatetimeProperties.to_pydatetime is deprecated"
118-
with tm.assert_produces_warning(FutureWarning, match=msg):
119-
result = ser.dt.to_pydatetime()
120-
assert isinstance(result, np.ndarray)
117+
result = ser.dt.to_pydatetime()
118+
assert isinstance(result, Series)
121119
assert result.dtype == object
122120

123121
result = ser.dt.tz_localize("US/Eastern")
@@ -153,10 +151,8 @@ def test_dt_namespace_accessor_datetime64tz(self):
153151
for prop in ok_for_dt_methods:
154152
getattr(ser.dt, prop)
155153

156-
msg = "The behavior of DatetimeProperties.to_pydatetime is deprecated"
157-
with tm.assert_produces_warning(FutureWarning, match=msg):
158-
result = ser.dt.to_pydatetime()
159-
assert isinstance(result, np.ndarray)
154+
result = ser.dt.to_pydatetime()
155+
assert isinstance(result, Series)
160156
assert result.dtype == object
161157

162158
result = ser.dt.tz_convert("CET")

0 commit comments

Comments
 (0)