Skip to content

Commit 78fee04

Browse files
jschendeljorisvandenbossche
authored andcommitted
DEPR: Deprecate DatetimeIndex.offset in favor of DatetimeIndex.freq (#20730)
1 parent ede11af commit 78fee04

File tree

7 files changed

+114
-85
lines changed

7 files changed

+114
-85
lines changed

doc/source/whatsnew/v0.23.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,7 @@ Deprecations
877877
- The ``convert_datetime64`` parameter in :func:`DataFrame.to_records` has been deprecated and will be removed in a future version. The NumPy bug motivating this parameter has been resolved. The default value for this parameter has also changed from ``True`` to ``None`` (:issue:`18160`).
878878
- :func:`Series.rolling().apply() <pandas.core.window.Rolling.apply>`, :func:`DataFrame.rolling().apply() <pandas.core.window.Rolling.apply>`,
879879
:func:`Series.expanding().apply() <pandas.core.window.Expanding.apply>`, and :func:`DataFrame.expanding().apply() <pandas.core.window.Expanding.apply>` have deprecated passing an ``np.array`` by default. One will need to pass the new ``raw`` parameter to be explicit about what is passed (:issue:`20584`)
880+
- ``DatetimeIndex.offset`` is deprecated. Use ``DatetimeIndex.freq`` instead (:issue:`20716`)
880881

881882
.. _whatsnew_0230.prior_deprecations:
882883

pandas/core/indexes/datetimes.py

+69-53
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ def _add_comparison_methods(cls):
302302
_engine_type = libindex.DatetimeEngine
303303

304304
tz = None
305-
offset = None
305+
_freq = None
306306
_comparables = ['name', 'freqstr', 'tz']
307307
_attributes = ['name', 'freq', 'tz']
308308

@@ -415,7 +415,7 @@ def __new__(cls, data=None,
415415
subarr = data.values
416416

417417
if freq is None:
418-
freq = data.offset
418+
freq = data.freq
419419
verify_integrity = False
420420
else:
421421
if data.dtype != _NS_DTYPE:
@@ -467,12 +467,12 @@ def __new__(cls, data=None,
467467
if freq_infer:
468468
inferred = subarr.inferred_freq
469469
if inferred:
470-
subarr.offset = to_offset(inferred)
470+
subarr.freq = to_offset(inferred)
471471

472472
return subarr._deepcopy_if_needed(ref_to_data, copy)
473473

474474
@classmethod
475-
def _generate(cls, start, end, periods, name, offset,
475+
def _generate(cls, start, end, periods, name, freq,
476476
tz=None, normalize=False, ambiguous='raise', closed=None):
477477
if com._count_not_none(start, end, periods) != 2:
478478
raise ValueError('Of the three parameters: start, end, and '
@@ -535,7 +535,7 @@ def _generate(cls, start, end, periods, name, offset,
535535
else:
536536
_normalized = _normalized and end.time() == _midnight
537537

538-
if hasattr(offset, 'delta') and offset != offsets.Day():
538+
if hasattr(freq, 'delta') and freq != offsets.Day():
539539
if inferred_tz is None and tz is not None:
540540
# naive dates
541541
if start is not None and start.tz is None:
@@ -551,11 +551,11 @@ def _generate(cls, start, end, periods, name, offset,
551551
if end.tz is None and start.tz is not None:
552552
end = end.tz_localize(start.tz, ambiguous=False)
553553

554-
if _use_cached_range(offset, _normalized, start, end):
554+
if _use_cached_range(freq, _normalized, start, end):
555555
index = cls._cached_range(start, end, periods=periods,
556-
offset=offset, name=name)
556+
freq=freq, name=name)
557557
else:
558-
index = _generate_regular_range(start, end, periods, offset)
558+
index = _generate_regular_range(start, end, periods, freq)
559559

560560
else:
561561

@@ -574,11 +574,11 @@ def _generate(cls, start, end, periods, name, offset,
574574
if end.tz is None and start.tz is not None:
575575
start = start.replace(tzinfo=None)
576576

577-
if _use_cached_range(offset, _normalized, start, end):
577+
if _use_cached_range(freq, _normalized, start, end):
578578
index = cls._cached_range(start, end, periods=periods,
579-
offset=offset, name=name)
579+
freq=freq, name=name)
580580
else:
581-
index = _generate_regular_range(start, end, periods, offset)
581+
index = _generate_regular_range(start, end, periods, freq)
582582

583583
if tz is not None and getattr(index, 'tz', None) is None:
584584
index = conversion.tz_localize_to_utc(_ensure_int64(index), tz,
@@ -596,12 +596,12 @@ def _generate(cls, start, end, periods, name, offset,
596596
index = index[1:]
597597
if not right_closed and len(index) and index[-1] == end:
598598
index = index[:-1]
599-
index = cls._simple_new(index, name=name, freq=offset, tz=tz)
599+
index = cls._simple_new(index, name=name, freq=freq, tz=tz)
600600
return index
601601

602602
@property
603603
def _box_func(self):
604-
return lambda x: Timestamp(x, freq=self.offset, tz=self.tz)
604+
return lambda x: Timestamp(x, freq=self.freq, tz=self.tz)
605605

606606
def _convert_for_op(self, value):
607607
""" Convert value to be insertable to ndarray """
@@ -647,7 +647,7 @@ def _simple_new(cls, values, name=None, freq=None, tz=None,
647647
result = object.__new__(cls)
648648
result._data = values
649649
result.name = name
650-
result.offset = freq
650+
result._freq = freq
651651
result._tz = timezones.maybe_get_tz(tz)
652652
result._tz = timezones.tz_standardize(result._tz)
653653
result._reset_identity()
@@ -734,7 +734,7 @@ def _has_same_tz(self, other):
734734
return zzone == vzone
735735

736736
@classmethod
737-
def _cached_range(cls, start=None, end=None, periods=None, offset=None,
737+
def _cached_range(cls, start=None, end=None, periods=None, freq=None,
738738
name=None):
739739
if start is None and end is None:
740740
# I somewhat believe this should never be raised externally
@@ -747,54 +747,54 @@ def _cached_range(cls, start=None, end=None, periods=None, offset=None,
747747
raise TypeError(
748748
'Must either specify period or provide both start and end.')
749749

750-
if offset is None:
750+
if freq is None:
751751
# This can't happen with external-facing code
752-
raise TypeError('Must provide offset.')
752+
raise TypeError('Must provide freq.')
753753

754754
drc = _daterange_cache
755-
if offset not in _daterange_cache:
756-
xdr = generate_range(offset=offset, start=_CACHE_START,
755+
if freq not in _daterange_cache:
756+
xdr = generate_range(offset=freq, start=_CACHE_START,
757757
end=_CACHE_END)
758758

759759
arr = tools.to_datetime(list(xdr), box=False)
760760

761761
cachedRange = DatetimeIndex._simple_new(arr)
762-
cachedRange.offset = offset
762+
cachedRange.freq = freq
763763
cachedRange = cachedRange.tz_localize(None)
764764
cachedRange.name = None
765-
drc[offset] = cachedRange
765+
drc[freq] = cachedRange
766766
else:
767-
cachedRange = drc[offset]
767+
cachedRange = drc[freq]
768768

769769
if start is None:
770770
if not isinstance(end, Timestamp):
771771
raise AssertionError('end must be an instance of Timestamp')
772772

773-
end = offset.rollback(end)
773+
end = freq.rollback(end)
774774

775775
endLoc = cachedRange.get_loc(end) + 1
776776
startLoc = endLoc - periods
777777
elif end is None:
778778
if not isinstance(start, Timestamp):
779779
raise AssertionError('start must be an instance of Timestamp')
780780

781-
start = offset.rollforward(start)
781+
start = freq.rollforward(start)
782782

783783
startLoc = cachedRange.get_loc(start)
784784
endLoc = startLoc + periods
785785
else:
786-
if not offset.onOffset(start):
787-
start = offset.rollforward(start)
786+
if not freq.onOffset(start):
787+
start = freq.rollforward(start)
788788

789-
if not offset.onOffset(end):
790-
end = offset.rollback(end)
789+
if not freq.onOffset(end):
790+
end = freq.rollback(end)
791791

792792
startLoc = cachedRange.get_loc(start)
793793
endLoc = cachedRange.get_loc(end) + 1
794794

795795
indexSlice = cachedRange[startLoc:endLoc]
796796
indexSlice.name = name
797-
indexSlice.offset = offset
797+
indexSlice.freq = freq
798798

799799
return indexSlice
800800

@@ -836,7 +836,7 @@ def __setstate__(self, state):
836836
np.ndarray.__setstate__(data, nd_state)
837837

838838
self.name = own_state[0]
839-
self.offset = own_state[1]
839+
self.freq = own_state[1]
840840
self._tz = timezones.tz_standardize(own_state[2])
841841

842842
# provide numpy < 1.7 compat
@@ -1184,7 +1184,7 @@ def union(self, other):
11841184
result._tz = timezones.tz_standardize(this.tz)
11851185
if (result.freq is None and
11861186
(this.freq is not None or other.freq is not None)):
1187-
result.offset = to_offset(result.inferred_freq)
1187+
result.freq = to_offset(result.inferred_freq)
11881188
return result
11891189

11901190
def to_perioddelta(self, freq):
@@ -1232,7 +1232,7 @@ def union_many(self, others):
12321232
this._tz = timezones.tz_standardize(tz)
12331233

12341234
if this.freq is None:
1235-
this.offset = to_offset(this.inferred_freq)
1235+
this.freq = to_offset(this.inferred_freq)
12361236
return this
12371237

12381238
def join(self, other, how='left', level=None, return_indexers=False,
@@ -1271,7 +1271,7 @@ def _maybe_utc_convert(self, other):
12711271
def _wrap_joined_index(self, joined, other):
12721272
name = self.name if self.name == other.name else None
12731273
if (isinstance(other, DatetimeIndex) and
1274-
self.offset == other.offset and
1274+
self.freq == other.freq and
12751275
self._can_fast_union(other)):
12761276
joined = self._shallow_copy(joined)
12771277
joined.name = name
@@ -1284,9 +1284,9 @@ def _can_fast_union(self, other):
12841284
if not isinstance(other, DatetimeIndex):
12851285
return False
12861286

1287-
offset = self.offset
1287+
freq = self.freq
12881288

1289-
if offset is None or offset != other.offset:
1289+
if freq is None or freq != other.freq:
12901290
return False
12911291

12921292
if not self.is_monotonic or not other.is_monotonic:
@@ -1306,10 +1306,10 @@ def _can_fast_union(self, other):
13061306

13071307
# Only need to "adjoin", not overlap
13081308
try:
1309-
return (right_start == left_end + offset) or right_start in left
1309+
return (right_start == left_end + freq) or right_start in left
13101310
except (ValueError):
13111311

1312-
# if we are comparing an offset that does not propagate timezones
1312+
# if we are comparing a freq that does not propagate timezones
13131313
# this will raise
13141314
return False
13151315

@@ -1329,7 +1329,7 @@ def _fast_union(self, other):
13291329
left_start, left_end = left[0], left[-1]
13301330
right_end = right[-1]
13311331

1332-
if not self.offset._should_cache():
1332+
if not self.freq._should_cache():
13331333
# concatenate dates
13341334
if left_end < right_end:
13351335
loc = right.searchsorted(left_end, side='right')
@@ -1341,7 +1341,7 @@ def _fast_union(self, other):
13411341
else:
13421342
return type(self)(start=left_start,
13431343
end=max(left_end, right_end),
1344-
freq=left.offset)
1344+
freq=left.freq)
13451345

13461346
def __iter__(self):
13471347
"""
@@ -1393,18 +1393,18 @@ def intersection(self, other):
13931393
result = Index.intersection(self, other)
13941394
if isinstance(result, DatetimeIndex):
13951395
if result.freq is None:
1396-
result.offset = to_offset(result.inferred_freq)
1396+
result.freq = to_offset(result.inferred_freq)
13971397
return result
13981398

1399-
elif (other.offset is None or self.offset is None or
1400-
other.offset != self.offset or
1401-
not other.offset.isAnchored() or
1399+
elif (other.freq is None or self.freq is None or
1400+
other.freq != self.freq or
1401+
not other.freq.isAnchored() or
14021402
(not self.is_monotonic or not other.is_monotonic)):
14031403
result = Index.intersection(self, other)
14041404
result = self._shallow_copy(result._values, name=result.name,
14051405
tz=result.tz, freq=None)
14061406
if result.freq is None:
1407-
result.offset = to_offset(result.inferred_freq)
1407+
result.freq = to_offset(result.inferred_freq)
14081408
return result
14091409

14101410
if len(self) == 0:
@@ -1729,12 +1729,28 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None):
17291729
@property
17301730
def freq(self):
17311731
"""get/set the frequency of the Index"""
1732-
return self.offset
1732+
return self._freq
17331733

17341734
@freq.setter
17351735
def freq(self, value):
17361736
"""get/set the frequency of the Index"""
1737-
self.offset = value
1737+
self._freq = value
1738+
1739+
@property
1740+
def offset(self):
1741+
"""get/set the frequency of the Index"""
1742+
msg = ('DatetimeIndex.offset has been deprecated and will be removed '
1743+
'in a future version; use DatetimeIndex.freq instead.')
1744+
warnings.warn(msg, FutureWarning, stacklevel=2)
1745+
return self.freq
1746+
1747+
@offset.setter
1748+
def offset(self, value):
1749+
"""get/set the frequency of the Index"""
1750+
msg = ('DatetimeIndex.offset has been deprecated and will be removed '
1751+
'in a future version; use DatetimeIndex.freq instead.')
1752+
warnings.warn(msg, FutureWarning, stacklevel=2)
1753+
self.freq = value
17381754

17391755
year = _field_accessor('year', 'Y', "The year of the datetime")
17401756
month = _field_accessor('month', 'M',
@@ -2525,9 +2541,9 @@ def day_name(self, locale=None):
25252541
DatetimeIndex._add_datetimelike_methods()
25262542

25272543

2528-
def _generate_regular_range(start, end, periods, offset):
2529-
if isinstance(offset, Tick):
2530-
stride = offset.nanos
2544+
def _generate_regular_range(start, end, periods, freq):
2545+
if isinstance(freq, Tick):
2546+
stride = freq.nanos
25312547
if periods is None:
25322548
b = Timestamp(start).value
25332549
# cannot just use e = Timestamp(end) + 1 because arange breaks when
@@ -2558,7 +2574,7 @@ def _generate_regular_range(start, end, periods, offset):
25582574
end = end.to_pydatetime()
25592575

25602576
xdr = generate_range(start=start, end=end,
2561-
periods=periods, offset=offset)
2577+
periods=periods, offset=freq)
25622578

25632579
dates = list(xdr)
25642580
# utc = len(dates) > 0 and dates[0].tzinfo is not None
@@ -2855,9 +2871,9 @@ def _in_range(start, end, rng_start, rng_end):
28552871
return start > rng_start and end < rng_end
28562872

28572873

2858-
def _use_cached_range(offset, _normalized, start, end):
2859-
return (offset._should_cache() and
2860-
not (offset._normalize_cache and not _normalized) and
2874+
def _use_cached_range(freq, _normalized, start, end):
2875+
return (freq._should_cache() and
2876+
not (freq._normalize_cache and not _normalized) and
28612877
_naive_in_cache_range(start, end))
28622878

28632879

pandas/tests/indexes/datetimes/test_construction.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -598,16 +598,16 @@ def test_datetimeindex_constructor_misc(self):
598598
idx2 = DatetimeIndex(start=sdate, end=edate,
599599
freq=offsets.Week(weekday=6))
600600
assert len(idx1) == len(idx2)
601-
assert idx1.offset == idx2.offset
601+
assert idx1.freq == idx2.freq
602602

603603
idx1 = DatetimeIndex(start=sdate, end=edate, freq='QS')
604604
idx2 = DatetimeIndex(start=sdate, end=edate,
605605
freq=offsets.QuarterBegin(startingMonth=1))
606606
assert len(idx1) == len(idx2)
607-
assert idx1.offset == idx2.offset
607+
assert idx1.freq == idx2.freq
608608

609609
idx1 = DatetimeIndex(start=sdate, end=edate, freq='BQ')
610610
idx2 = DatetimeIndex(start=sdate, end=edate,
611611
freq=offsets.BQuarterEnd(startingMonth=12))
612612
assert len(idx1) == len(idx2)
613-
assert idx1.offset == idx2.offset
613+
assert idx1.freq == idx2.freq

0 commit comments

Comments
 (0)