Skip to content

Commit 093c0ad

Browse files
jbrockmendelclaude
andauthored
REF: remove DatetimeArray._with_freq, further freq management cleanup (GH#24566) (#65276)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b0a82fc commit 093c0ad

File tree

15 files changed

+45
-72
lines changed

15 files changed

+45
-72
lines changed

pandas/core/arrays/datetimelike.py

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,7 @@ def _add_datetimelike_scalar(self, other) -> DatetimeArray:
11051105

11061106
dtype = tz_to_dtype(tz=other.tz, unit=self.unit)
11071107
res_values = result.view(f"M8[{self.unit}]")
1108-
return DatetimeArray._simple_new(res_values, dtype=dtype, freq=None)
1108+
return DatetimeArray._simple_new(res_values, dtype=dtype)
11091109

11101110
@final
11111111
def _add_datetime_arraylike(self, other: DatetimeArray) -> DatetimeArray:
@@ -1165,7 +1165,7 @@ def _sub_datetimelike(self, other: Timestamp | DatetimeArray) -> TimedeltaArray:
11651165
res_values = add_overflowsafe(self.asi8, np.asarray(-other_i8, dtype="i8"))
11661166
res_m8 = res_values.view(f"timedelta64[{self.unit}]")
11671167

1168-
return TimedeltaArray._simple_new(res_m8, dtype=res_m8.dtype, freq=None)
1168+
return TimedeltaArray._simple_new(res_m8, dtype=res_m8.dtype)
11691169

11701170
@final
11711171
def _add_period(self, other: Period) -> PeriodArray:
@@ -1227,12 +1227,7 @@ def _add_timedeltalike(self, other: Timedelta | TimedeltaArray) -> Self:
12271227
new_values = add_overflowsafe(self.asi8, np.asarray(other_i8, dtype="i8"))
12281228
res_values = new_values.view(self._ndarray.dtype)
12291229

1230-
# error: Unexpected keyword argument "freq" for "_simple_new" of "NDArrayBacked"
1231-
return type(self)._simple_new(
1232-
res_values,
1233-
dtype=self.dtype,
1234-
freq=None, # type: ignore[call-arg]
1235-
)
1230+
return type(self)._simple_new(res_values, dtype=self.dtype)
12361231

12371232
@final
12381233
def _add_nat(self) -> Self:
@@ -1249,12 +1244,7 @@ def _add_nat(self) -> Self:
12491244
result = np.empty(self.shape, dtype=np.int64)
12501245
result.fill(iNaT)
12511246
result = result.view(self._ndarray.dtype) # preserve reso
1252-
# error: Unexpected keyword argument "freq" for "_simple_new" of "NDArrayBacked"
1253-
return type(self)._simple_new(
1254-
result,
1255-
dtype=self.dtype,
1256-
freq=None, # type: ignore[call-arg]
1257-
)
1247+
return type(self)._simple_new(result, dtype=self.dtype)
12581248

12591249
@final
12601250
def _sub_nat(self) -> np.ndarray:
@@ -2386,35 +2376,6 @@ def all(self, *, axis: AxisInt | None = None, skipna: bool = True) -> bool:
23862376
def _maybe_clear_freq(self) -> None:
23872377
self._freq = None
23882378

2389-
def _with_freq(self, freq) -> Self:
2390-
"""
2391-
Helper to get a view on the same data, with a new freq.
2392-
2393-
Parameters
2394-
----------
2395-
freq : DateOffset, None, or "infer"
2396-
2397-
Returns
2398-
-------
2399-
Same type as self
2400-
"""
2401-
# GH#29843
2402-
if freq is None:
2403-
# Always valid
2404-
pass
2405-
elif isinstance(freq, BaseOffset):
2406-
# In the TimedeltaArray case, we require a Tick offset
2407-
if self.dtype.kind == "m" and not isinstance(freq, (Tick, Day)):
2408-
raise TypeError("TimedeltaArray/Index freq must be a Tick")
2409-
elif freq == "infer":
2410-
freq = to_offset(self.inferred_freq)
2411-
else:
2412-
raise ValueError(f"Invalid frequency: {freq!r}")
2413-
2414-
arr = self.view()
2415-
arr._freq = freq
2416-
return arr
2417-
24182379
# --------------------------------------------------------------
24192380
# ExtensionArray Interface
24202381

pandas/core/arrays/datetimes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ def tz_convert(self, tz) -> Self:
945945

946946
# No conversion since timestamps are all UTC to begin with
947947
dtype = tz_to_dtype(tz, unit=self.unit)
948-
return self._simple_new(self._ndarray, dtype=dtype, freq=None)
948+
return self._simple_new(self._ndarray, dtype=dtype)
949949

950950
@dtl.ravel_compat
951951
def tz_localize(
@@ -1122,7 +1122,7 @@ def tz_localize(
11221122
new_dates_dt64 = new_dates.view(f"M8[{self.unit}]")
11231123
dtype = tz_to_dtype(tz, unit=self.unit)
11241124

1125-
return self._simple_new(new_dates_dt64, dtype=dtype, freq=None)
1125+
return self._simple_new(new_dates_dt64, dtype=dtype)
11261126

11271127
# ----------------------------------------------------------------
11281128
# Conversion Methods - Vectorized analogues of Timestamp methods

pandas/core/arrays/period.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,7 @@ def to_timestamp(self, freq=None, how: str = "start") -> DatetimeArray:
992992
# TODO: other cases?
993993
return dta
994994
else:
995-
dta = dta._with_freq("infer")
995+
dta._freq = to_offset(dta.inferred_freq)
996996
if freq is not None:
997997
freq = to_offset(freq)
998998
if (

pandas/core/arrays/timedeltas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ def _accumulate(self, name: str, *, skipna: bool = True, **kwargs):
409409
op = getattr(datetimelike_accumulations, name)
410410
result = op(self._ndarray.copy(), skipna=skipna, **kwargs)
411411

412-
return type(self)._simple_new(result, freq=None, dtype=self.dtype)
412+
return type(self)._simple_new(result, dtype=self.dtype)
413413
elif name == "cumprod":
414414
raise TypeError("cumprod not supported for Timedelta.")
415415

pandas/core/indexes/datetimelike.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -917,16 +917,18 @@ def as_unit(self, unit: TimeUnit) -> Self:
917917

918918
def _with_freq(self, freq):
919919
# GH#29843
920-
if freq is None or (len(self) == 0 and isinstance(freq, BaseOffset)):
921-
# None is always valid. For offsets on empty index the array's
922-
# _with_freq below performs the m-dtype Tick validation.
920+
if freq is None:
923921
pass
924-
else:
925-
# As an internal method, we can ensure this assertion always holds
926-
assert freq == "infer"
922+
elif isinstance(freq, BaseOffset):
923+
if self.dtype.kind == "m" and not isinstance(freq, (Tick, Day)):
924+
raise TypeError("TimedeltaArray/Index freq must be a Tick")
925+
elif freq == "infer":
927926
freq = to_offset(self.inferred_freq)
927+
else:
928+
raise ValueError(f"Invalid frequency: {freq!r}")
928929

929-
arr = self._data._with_freq(freq)
930+
arr = self._data.view()
931+
arr._freq = freq
930932
return type(self)._simple_new(arr, name=self._name)
931933

932934
@property
@@ -1218,12 +1220,13 @@ def _union(self, other, sort):
12181220
return self._range_union(other, sort=sort)
12191221

12201222
if self._can_fast_union(other):
1221-
result = self._fast_union(other, sort=sort)
12221223
# in the case with sort=None, the _can_fast_union check ensures
12231224
# that result.freq == self.freq
1224-
return result
1225+
return self._fast_union(other, sort=sort)
12251226
else:
1226-
return super()._union(other, sort)._with_freq("infer") # type: ignore[union-attr]
1227+
# super()._union can return an ArrayLike; wrap into an Index first
1228+
result = self._wrap_setop_result(other, super()._union(other, sort))
1229+
return result._with_freq("infer") # type: ignore[attr-defined]
12271230

12281231
# --------------------------------------------------------------------
12291232
# Join Methods

pandas/core/indexes/datetimes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ def normalize(self) -> Self:
496496
dtype='datetime64[us, Asia/Calcutta]', freq=None)
497497
"""
498498
arr = self._data.normalize()
499-
arr = arr._with_freq("infer")
499+
arr._freq = to_offset(arr.inferred_freq)
500500
return type(self)._simple_new(arr, name=self.name)
501501

502502
def tz_convert(self, tz) -> Self:

pandas/core/internals/blocks.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2252,7 +2252,8 @@ def maybe_coerce_values(values: ArrayLike) -> ArrayLike:
22522252

22532253
if isinstance(values, (DatetimeArray, TimedeltaArray)) and values.freq is not None:
22542254
# freq is only stored in DatetimeIndex/TimedeltaIndex, not in Series/DataFrame
2255-
values = values._with_freq(None)
2255+
values = values.view()
2256+
values._freq = None
22562257

22572258
return values
22582259

pandas/tests/arithmetic/test_timedelta64.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,7 @@ def test_td64arr_add_sub_datetimelike_scalar(
10731073
if box_with_array is pd.array:
10741074
# GH#24566 Array-level __neg__ and __rsub__ don't carry freq;
10751075
# freq is managed by the Index layer.
1076-
expected2 = expected2._with_freq(None)
1076+
expected2._freq = None
10771077

10781078
tm.assert_equal(ts - tdarr, expected2)
10791079
tm.assert_equal(ts + (-tdarr), expected2)

pandas/tests/arrays/datetimes/test_constructors.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def test_mixing_naive_tzaware_raises(self, meth):
4545
def test_from_pandas_array(self):
4646
arr = pd.array(np.arange(5, dtype=np.int64)) * 3600 * 10**9
4747

48-
result = DatetimeArray._from_sequence(arr, dtype="M8[ns]")._with_freq("infer")
48+
result = DatetimeArray._from_sequence(arr, dtype="M8[ns]")
49+
result._freq = pd.tseries.frequencies.to_offset(result.inferred_freq)
4950

5051
expected = pd.date_range("1970-01-01", periods=5, freq="h", unit="ns")._data
5152
tm.assert_datetime_array_equal(result, expected)

pandas/tests/arrays/datetimes/test_cumulative.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import pandas._testing as tm
44
from pandas.core.arrays import DatetimeArray
55

6+
from pandas.tseries.frequencies import to_offset
7+
68

79
class TestAccumulator:
810
def test_accumulators_freq(self):
@@ -14,7 +16,8 @@ def test_accumulators_freq(self):
1416
"2000-01-03",
1517
],
1618
dtype="M8[ns]",
17-
)._with_freq("infer")
19+
)
20+
arr._freq = to_offset(arr.inferred_freq)
1821
result = arr._accumulate("cummin")
1922
expected = DatetimeArray._from_sequence(["2000-01-01"] * 3, dtype="M8[ns]")
2023
tm.assert_datetime_array_equal(result, expected)
@@ -39,6 +42,7 @@ def test_accumulators_disallowed(self, func):
3942
"2000-01-02",
4043
],
4144
dtype="M8[ns]",
42-
)._with_freq("infer")
45+
)
46+
arr._freq = to_offset(arr.inferred_freq)
4347
with pytest.raises(TypeError, match=f"Accumulation {func}"):
4448
arr._accumulate(func)

0 commit comments

Comments
 (0)