Skip to content

Commit d733368

Browse files
authored
BUG: always strip .freq when putting DTI/TDI into Series/DataFrame (#41425)
1 parent bab8393 commit d733368

File tree

8 files changed

+22
-16
lines changed

8 files changed

+22
-16
lines changed

Diff for: doc/source/whatsnew/v1.3.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,7 @@ Other
972972
- Bug in :func:`pandas.util.show_versions` where console JSON output was not proper JSON (:issue:`39701`)
973973
- Bug in :meth:`DataFrame.convert_dtypes` incorrectly raised ValueError when called on an empty DataFrame (:issue:`40393`)
974974
- Bug in :meth:`DataFrame.clip` not interpreting missing values as no threshold (:issue:`40420`)
975+
- Bug in :class:`Series` backed by :class:`DatetimeArray` or :class:`TimedeltaArray` sometimes failing to set the array's ``freq`` to ``None`` (:issue:`41425`)
975976

976977
.. ---------------------------------------------------------------------------
977978

Diff for: pandas/core/base.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,8 @@ def to_numpy(
498498
499499
>>> ser = pd.Series(pd.date_range('2000', periods=2, tz="CET"))
500500
>>> ser.to_numpy(dtype=object)
501-
array([Timestamp('2000-01-01 00:00:00+0100', tz='CET', freq='D'),
502-
Timestamp('2000-01-02 00:00:00+0100', tz='CET', freq='D')],
501+
array([Timestamp('2000-01-01 00:00:00+0100', tz='CET'),
502+
Timestamp('2000-01-02 00:00:00+0100', tz='CET')],
503503
dtype=object)
504504
505505
Or ``dtype='datetime64[ns]'`` to return an ndarray of native

Diff for: pandas/core/internals/array_manager.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
from pandas.core.internals.blocks import (
8686
ensure_block_shape,
8787
external_values,
88+
maybe_coerce_values,
8889
new_block,
8990
to_native_types,
9091
)
@@ -701,7 +702,7 @@ def __init__(
701702

702703
if verify_integrity:
703704
self._axes = [ensure_index(ax) for ax in axes]
704-
self.arrays = [ensure_wrapped_if_datetimelike(arr) for arr in arrays]
705+
self.arrays = [maybe_coerce_values(arr) for arr in arrays]
705706
self._verify_integrity()
706707

707708
def _verify_integrity(self) -> None:
@@ -814,7 +815,7 @@ def iset(self, loc: int | slice | np.ndarray, value: ArrayLike):
814815

815816
# TODO we receive a datetime/timedelta64 ndarray from DataFrame._iset_item
816817
# but we should avoid that and pass directly the proper array
817-
value = ensure_wrapped_if_datetimelike(value)
818+
value = maybe_coerce_values(value)
818819

819820
assert isinstance(value, (np.ndarray, ExtensionArray))
820821
assert value.ndim == 1
@@ -873,7 +874,7 @@ def insert(self, loc: int, item: Hashable, value: ArrayLike) -> None:
873874
raise ValueError(
874875
f"Expected a 1D array, got an array with shape {value.shape}"
875876
)
876-
value = ensure_wrapped_if_datetimelike(value)
877+
value = maybe_coerce_values(value)
877878

878879
# TODO self.arrays can be empty
879880
# assert len(value) == len(self.arrays[0])
@@ -1188,7 +1189,7 @@ def __init__(
11881189
assert len(arrays) == 1
11891190
self._axes = [ensure_index(ax) for ax in self._axes]
11901191
arr = arrays[0]
1191-
arr = ensure_wrapped_if_datetimelike(arr)
1192+
arr = maybe_coerce_values(arr)
11921193
if isinstance(arr, ABCPandasArray):
11931194
arr = arr.to_numpy()
11941195
self.arrays = [arr]

Diff for: pandas/core/internals/blocks.py

+4
Original file line numberDiff line numberDiff line change
@@ -1860,6 +1860,10 @@ def maybe_coerce_values(values) -> ArrayLike:
18601860
if issubclass(values.dtype.type, str):
18611861
values = np.array(values, dtype=object)
18621862

1863+
if isinstance(values, (DatetimeArray, TimedeltaArray)) and values.freq is not None:
1864+
# freq is only stored in DatetimeIndex/TimedeltaIndex, not in Series/DataFrame
1865+
values = values._with_freq(None)
1866+
18631867
return values
18641868

18651869

Diff for: pandas/core/series.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -813,8 +813,8 @@ def __array__(self, dtype: NpDtype | None = None) -> np.ndarray:
813813
814814
>>> tzser = pd.Series(pd.date_range('2000', periods=2, tz="CET"))
815815
>>> np.asarray(tzser, dtype="object")
816-
array([Timestamp('2000-01-01 00:00:00+0100', tz='CET', freq='D'),
817-
Timestamp('2000-01-02 00:00:00+0100', tz='CET', freq='D')],
816+
array([Timestamp('2000-01-01 00:00:00+0100', tz='CET'),
817+
Timestamp('2000-01-02 00:00:00+0100', tz='CET')],
818818
dtype=object)
819819
820820
Or the values may be localized to UTC and the tzinfo discarded with

Diff for: pandas/tests/extension/test_datetime.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ class TestDatetimeDtype(BaseDatetimeTests, base.BaseDtypeTests):
9797

9898

9999
class TestConstructors(BaseDatetimeTests, base.BaseConstructorsTests):
100-
pass
100+
def test_series_constructor(self, data):
101+
# Series construction drops any .freq attr
102+
data = data._with_freq(None)
103+
super().test_series_constructor(data)
101104

102105

103106
class TestGetitem(BaseDatetimeTests, base.BaseGetitemTests):

Diff for: pandas/tests/frame/methods/test_set_index.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def test_set_index_cast_datetimeindex(self):
9696
idf = df.set_index("A")
9797
assert isinstance(idf.index, DatetimeIndex)
9898

99-
def test_set_index_dst(self, using_array_manager):
99+
def test_set_index_dst(self):
100100
di = date_range("2006-10-29 00:00:00", periods=3, freq="H", tz="US/Pacific")
101101

102102
df = DataFrame(data={"a": [0, 1, 2], "b": [3, 4, 5]}, index=di).reset_index()
@@ -106,8 +106,7 @@ def test_set_index_dst(self, using_array_manager):
106106
data={"a": [0, 1, 2], "b": [3, 4, 5]},
107107
index=Index(di, name="index"),
108108
)
109-
if not using_array_manager:
110-
exp.index = exp.index._with_freq(None)
109+
exp.index = exp.index._with_freq(None)
111110
tm.assert_frame_equal(res, exp)
112111

113112
# GH#12920

Diff for: pandas/tests/window/test_rolling.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ def test_rolling_datetime(axis_frame, tz_naive_fixture):
586586
),
587587
],
588588
)
589-
def test_rolling_window_as_string(center, expected_data, using_array_manager):
589+
def test_rolling_window_as_string(center, expected_data):
590590
# see gh-22590
591591
date_today = datetime.now()
592592
days = date_range(date_today, date_today + timedelta(365), freq="D")
@@ -602,9 +602,7 @@ def test_rolling_window_as_string(center, expected_data, using_array_manager):
602602
].agg("max")
603603

604604
index = days.rename("DateCol")
605-
if not using_array_manager:
606-
# INFO(ArrayManager) preserves the frequence of the index
607-
index = index._with_freq(None)
605+
index = index._with_freq(None)
608606
expected = Series(expected_data, index=index, name="metric")
609607
tm.assert_series_equal(result, expected)
610608

0 commit comments

Comments
 (0)