Skip to content

Commit 21d75fb

Browse files
authored
BUG: TimedeltaIndex[:] losing freq (pandas-dev#33834)
1 parent 085860a commit 21d75fb

File tree

3 files changed

+12
-85
lines changed

3 files changed

+12
-85
lines changed

pandas/core/indexes/datetimelike.py

+4-84
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
Base and utility classes for tseries type pandas objects.
33
"""
4-
from datetime import datetime, timedelta
4+
from datetime import datetime
55
from typing import Any, List, Optional, Union, cast
66

77
import numpy as np
@@ -16,18 +16,14 @@
1616
from pandas.core.dtypes.common import (
1717
ensure_int64,
1818
is_bool_dtype,
19-
is_datetime64_any_dtype,
2019
is_dtype_equal,
2120
is_integer,
2221
is_list_like,
23-
is_object_dtype,
2422
is_period_dtype,
2523
is_scalar,
26-
is_timedelta64_dtype,
2724
)
2825
from pandas.core.dtypes.concat import concat_compat
2926
from pandas.core.dtypes.generic import ABCIndex, ABCIndexClass, ABCSeries
30-
from pandas.core.dtypes.missing import isna
3127

3228
from pandas.core import algorithms
3329
from pandas.core.arrays import DatetimeArray, PeriodArray, TimedeltaArray
@@ -46,7 +42,6 @@
4642
from pandas.core.tools.timedeltas import to_timedelta
4743

4844
from pandas.tseries.frequencies import DateOffset
49-
from pandas.tseries.offsets import Tick
5045

5146
_index_doc_kwargs = dict(ibase._index_doc_kwargs)
5247

@@ -77,33 +72,13 @@ def wrapper(left, right):
7772
return wrapper
7873

7974

80-
def _make_wrapped_arith_op_with_freq(opname: str):
81-
"""
82-
Dispatch the operation to the underlying ExtensionArray, and infer
83-
the appropriate frequency for the result.
84-
"""
85-
meth = make_wrapped_arith_op(opname)
86-
87-
def wrapped(self, other):
88-
result = meth(self, other)
89-
if result is NotImplemented:
90-
return NotImplemented
91-
92-
new_freq = self._get_addsub_freq(other, result)
93-
result._freq = new_freq
94-
return result
95-
96-
wrapped.__name__ = opname
97-
return wrapped
98-
99-
10075
@inherit_names(
10176
["inferred_freq", "_isnan", "_resolution", "resolution"],
10277
DatetimeLikeArrayMixin,
10378
cache=True,
10479
)
10580
@inherit_names(
106-
["mean", "asi8", "_box_func"], DatetimeLikeArrayMixin,
81+
["mean", "asi8", "freq", "freqstr", "_box_func"], DatetimeLikeArrayMixin,
10782
)
10883
class DatetimeIndexOpsMixin(ExtensionIndex):
10984
"""
@@ -437,44 +412,8 @@ def _partial_date_slice(
437412
# --------------------------------------------------------------------
438413
# Arithmetic Methods
439414

440-
def _get_addsub_freq(self, other, result) -> Optional[DateOffset]:
441-
"""
442-
Find the freq we expect the result of an addition/subtraction operation
443-
to have.
444-
"""
445-
if is_period_dtype(self.dtype):
446-
if is_period_dtype(result.dtype):
447-
# Only used for ops that stay PeriodDtype
448-
return self.freq
449-
return None
450-
elif self.freq is None:
451-
return None
452-
elif lib.is_scalar(other) and isna(other):
453-
return None
454-
455-
elif isinstance(other, (Tick, timedelta, np.timedelta64)):
456-
new_freq = None
457-
if isinstance(self.freq, Tick):
458-
new_freq = self.freq
459-
return new_freq
460-
461-
elif isinstance(other, DateOffset):
462-
# otherwise just DatetimeArray
463-
return None # TODO: Should we infer if it matches self.freq * n?
464-
elif isinstance(other, (datetime, np.datetime64)):
465-
return self.freq
466-
467-
elif is_timedelta64_dtype(other):
468-
return None # TODO: shouldnt we be able to do self.freq + other.freq?
469-
elif is_object_dtype(other):
470-
return None # TODO: is this quite right? sometimes we unpack singletons
471-
elif is_datetime64_any_dtype(other):
472-
return None # TODO: shouldnt we be able to do self.freq + other.freq?
473-
else:
474-
raise NotImplementedError
475-
476-
__add__ = _make_wrapped_arith_op_with_freq("__add__")
477-
__sub__ = _make_wrapped_arith_op_with_freq("__sub__")
415+
__add__ = make_wrapped_arith_op("__add__")
416+
__sub__ = make_wrapped_arith_op("__sub__")
478417
__radd__ = make_wrapped_arith_op("__radd__")
479418
__rsub__ = make_wrapped_arith_op("__rsub__")
480419
__pow__ = make_wrapped_arith_op("__pow__")
@@ -643,25 +582,6 @@ class DatetimeTimedeltaMixin(DatetimeIndexOpsMixin, Int64Index):
643582
_is_monotonic_increasing = Index.is_monotonic_increasing
644583
_is_monotonic_decreasing = Index.is_monotonic_decreasing
645584
_is_unique = Index.is_unique
646-
_freq = lib.no_default
647-
648-
@property
649-
def freq(self):
650-
"""
651-
In limited circumstances, our freq may differ from that of our _data.
652-
"""
653-
if self._freq is not lib.no_default:
654-
return self._freq
655-
return self._data.freq
656-
657-
@property
658-
def freqstr(self):
659-
"""
660-
Return the frequency object as a string if its set, otherwise None.
661-
"""
662-
if self.freq is None:
663-
return None
664-
return self.freq.freqstr
665585

666586
def _with_freq(self, freq):
667587
arr = self._data._with_freq(freq)

pandas/core/indexes/period.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def _new_PeriodIndex(cls, **d):
7070
PeriodArray,
7171
wrap=True,
7272
)
73-
@inherit_names(["is_leap_year", "freq", "freqstr", "_format_native_types"], PeriodArray)
73+
@inherit_names(["is_leap_year", "_format_native_types"], PeriodArray)
7474
class PeriodIndex(DatetimeIndexOpsMixin, Int64Index):
7575
"""
7676
Immutable ndarray holding ordinal values indicating regular periods in time.

pandas/tests/indexes/datetimelike.py

+7
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,10 @@ def test_map_dictlike(self, mapper):
9696
expected = pd.Index([np.nan] * len(index))
9797
result = index.map(mapper([], []))
9898
tm.assert_index_equal(result, expected)
99+
100+
def test_getitem_preserves_freq(self):
101+
index = self.create_index()
102+
assert index.freq is not None
103+
104+
result = index[:]
105+
assert result.freq == index.freq

0 commit comments

Comments
 (0)