Skip to content

Commit 1e5ff23

Browse files
authored
ENH: allow passing freq=None to DatetimeIndex/TimedeltaIndex (#33635)
1 parent 0907d9e commit 1e5ff23

File tree

7 files changed

+49
-10
lines changed

7 files changed

+49
-10
lines changed

pandas/core/arrays/datetimelike.py

+1
Original file line numberDiff line numberDiff line change
@@ -1796,6 +1796,7 @@ def maybe_infer_freq(freq):
17961796
-------
17971797
freq : {DateOffset, None}
17981798
freq_infer : bool
1799+
Whether we should inherit the freq of passed data.
17991800
"""
18001801
freq_infer = False
18011802
if not isinstance(freq, DateOffset):

pandas/core/arrays/datetimes.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,13 @@ def _from_sequence(
302302
dtype=None,
303303
copy=False,
304304
tz=None,
305-
freq=None,
305+
freq=lib.no_default,
306306
dayfirst=False,
307307
yearfirst=False,
308308
ambiguous="raise",
309309
):
310+
explicit_none = freq is None
311+
freq = freq if freq is not lib.no_default else None
310312

311313
freq, freq_infer = dtl.maybe_infer_freq(freq)
312314

@@ -321,6 +323,8 @@ def _from_sequence(
321323
)
322324

323325
freq, freq_infer = dtl.validate_inferred_freq(freq, inferred_freq, freq_infer)
326+
if explicit_none:
327+
freq = None
324328

325329
dtype = tz_to_dtype(tz)
326330
result = cls._simple_new(subarr, freq=freq, dtype=dtype)

pandas/core/arrays/timedeltas.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,18 @@ def dtype(self):
141141
# ----------------------------------------------------------------
142142
# Constructors
143143

144-
def __init__(self, values, dtype=TD64NS_DTYPE, freq=None, copy=False):
144+
def __init__(self, values, dtype=TD64NS_DTYPE, freq=lib.no_default, copy=False):
145145
values = extract_array(values)
146146

147147
inferred_freq = getattr(values, "_freq", None)
148+
explicit_none = freq is None
149+
freq = freq if freq is not lib.no_default else None
148150

149151
if isinstance(values, type(self)):
150-
if freq is None:
152+
if explicit_none:
153+
# dont inherit from values
154+
pass
155+
elif freq is None:
151156
freq = values.freq
152157
elif freq and values.freq:
153158
freq = to_offset(freq)
@@ -206,13 +211,21 @@ def _simple_new(cls, values, freq=None, dtype=TD64NS_DTYPE):
206211
return result
207212

208213
@classmethod
209-
def _from_sequence(cls, data, dtype=TD64NS_DTYPE, copy=False, freq=None, unit=None):
214+
def _from_sequence(
215+
cls, data, dtype=TD64NS_DTYPE, copy=False, freq=lib.no_default, unit=None
216+
):
210217
if dtype:
211218
_validate_td64_dtype(dtype)
219+
220+
explicit_none = freq is None
221+
freq = freq if freq is not lib.no_default else None
222+
212223
freq, freq_infer = dtl.maybe_infer_freq(freq)
213224

214225
data, inferred_freq = sequence_to_td64ns(data, copy=copy, unit=unit)
215226
freq, freq_infer = dtl.validate_inferred_freq(freq, inferred_freq, freq_infer)
227+
if explicit_none:
228+
freq = None
216229

217230
result = cls._simple_new(data, freq=freq)
218231

pandas/core/indexes/datetimes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ class DatetimeIndex(DatetimeTimedeltaMixin):
224224
def __new__(
225225
cls,
226226
data=None,
227-
freq=None,
227+
freq=lib.no_default,
228228
tz=None,
229229
normalize=False,
230230
closed=None,

pandas/core/indexes/timedeltas.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
""" implement the TimedeltaIndex """
22

3-
from pandas._libs import NaT, Timedelta, index as libindex
3+
from pandas._libs import NaT, Timedelta, index as libindex, lib
44
from pandas._typing import DtypeObj, Label
55
from pandas.util._decorators import doc
66

@@ -121,7 +121,7 @@ def __new__(
121121
cls,
122122
data=None,
123123
unit=None,
124-
freq=None,
124+
freq=lib.no_default,
125125
closed=None,
126126
dtype=TD64NS_DTYPE,
127127
copy=False,
@@ -141,12 +141,12 @@ def __new__(
141141
"represent unambiguous timedelta values durations."
142142
)
143143

144-
if isinstance(data, TimedeltaArray) and freq is None:
144+
if isinstance(data, TimedeltaArray) and freq is lib.no_default:
145145
if copy:
146146
data = data.copy()
147147
return cls._simple_new(data, name=name)
148148

149-
if isinstance(data, TimedeltaIndex) and freq is None and name is None:
149+
if isinstance(data, TimedeltaIndex) and freq is lib.no_default and name is None:
150150
if copy:
151151
return data.copy()
152152
else:
@@ -340,6 +340,6 @@ def timedelta_range(
340340
if freq is None and com.any_none(periods, start, end):
341341
freq = "D"
342342

343-
freq, freq_infer = dtl.maybe_infer_freq(freq)
343+
freq, _ = dtl.maybe_infer_freq(freq)
344344
tdarr = TimedeltaArray._generate_range(start, end, periods, freq, closed=closed)
345345
return TimedeltaIndex._simple_new(tdarr, name=name)

pandas/tests/indexes/datetimes/test_constructors.py

+10
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,16 @@ def test_dti_constructor_preserve_dti_freq(self):
816816
rng2 = DatetimeIndex(rng)
817817
assert rng.freq == rng2.freq
818818

819+
def test_explicit_none_freq(self):
820+
# Explicitly passing freq=None is respected
821+
rng = date_range("1/1/2000", "1/2/2000", freq="5min")
822+
823+
result = DatetimeIndex(rng, freq=None)
824+
assert result.freq is None
825+
826+
result = DatetimeIndex(rng._data, freq=None)
827+
assert result.freq is None
828+
819829
def test_dti_constructor_years_only(self, tz_naive_fixture):
820830
tz = tz_naive_fixture
821831
# GH 6961

pandas/tests/indexes/timedeltas/test_constructors.py

+11
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,14 @@ def test_constructor_wrong_precision_raises(self):
227227
msg = r"dtype timedelta64\[us\] cannot be converted to timedelta64\[ns\]"
228228
with pytest.raises(ValueError, match=msg):
229229
pd.TimedeltaIndex(["2000"], dtype="timedelta64[us]")
230+
231+
def test_explicit_none_freq(self):
232+
# Explicitly passing freq=None is respected
233+
tdi = timedelta_range(1, periods=5)
234+
assert tdi.freq is not None
235+
236+
result = TimedeltaIndex(tdi, freq=None)
237+
assert result.freq is None
238+
239+
result = TimedeltaIndex(tdi._data, freq=None)
240+
assert result.freq is None

0 commit comments

Comments
 (0)