Skip to content

Commit eeba91e

Browse files
authored
REF: partially decouple Period from Tick (#53440)
1 parent ad86efc commit eeba91e

File tree

8 files changed

+32
-18
lines changed

8 files changed

+32
-18
lines changed

pandas/_libs/tslibs/dtypes.pxd

+1
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,4 @@ cdef class PeriodDtypeBase:
105105
int64_t _n
106106

107107
cpdef int _get_to_timestamp_base(self)
108+
cpdef bint _is_tick_like(self)

pandas/_libs/tslibs/dtypes.pyi

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ class PeriodDtypeBase:
2626
@property
2727
def _freqstr(self) -> str: ...
2828
def __hash__(self) -> int: ...
29+
def _is_tick_like(self) -> bool: ...
30+
@property
31+
def _creso(self) -> int: ...
32+
@property
33+
def _td64_unit(self) -> str: ...
2934

3035
class FreqGroup(Enum):
3136
FR_ANN: int

pandas/_libs/tslibs/dtypes.pyx

+19
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,25 @@ cdef class PeriodDtypeBase:
7777
return FR_SEC
7878
return base
7979

80+
cpdef bint _is_tick_like(self):
81+
return self._dtype_code >= PeriodDtypeCode.D
82+
83+
@property
84+
def _creso(self) -> int:
85+
return {
86+
PeriodDtypeCode.D: NPY_DATETIMEUNIT.NPY_FR_D,
87+
PeriodDtypeCode.H: NPY_DATETIMEUNIT.NPY_FR_h,
88+
PeriodDtypeCode.T: NPY_DATETIMEUNIT.NPY_FR_m,
89+
PeriodDtypeCode.S: NPY_DATETIMEUNIT.NPY_FR_s,
90+
PeriodDtypeCode.L: NPY_DATETIMEUNIT.NPY_FR_ms,
91+
PeriodDtypeCode.U: NPY_DATETIMEUNIT.NPY_FR_us,
92+
PeriodDtypeCode.N: NPY_DATETIMEUNIT.NPY_FR_ns,
93+
}[self._dtype_code]
94+
95+
@property
96+
def _td64_unit(self) -> str:
97+
return npy_unit_to_abbrev(self._creso)
98+
8099

81100
_period_code_map = {
82101
# Annual freqs with various fiscal year ends.

pandas/_libs/tslibs/offsets.pyi

-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ def to_offset(freq: timedelta | str) -> BaseOffset: ...
110110
class Tick(SingleConstructorOffset):
111111
_creso: int
112112
_prefix: str
113-
_td64_unit: str
114113
def __init__(self, n: int = ..., normalize: bool = ...) -> None: ...
115114
@property
116115
def delta(self) -> Timedelta: ...

pandas/_libs/tslibs/offsets.pyx

-8
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,6 @@ cdef class SingleConstructorOffset(BaseOffset):
912912
cdef class Tick(SingleConstructorOffset):
913913
_adjust_dst = False
914914
_prefix = "undefined"
915-
_td64_unit = "undefined"
916915
_attributes = tuple(["n", "normalize"])
917916

918917
def __init__(self, n=1, normalize=False):
@@ -1092,55 +1091,48 @@ cdef class Tick(SingleConstructorOffset):
10921091
cdef class Day(Tick):
10931092
_nanos_inc = 24 * 3600 * 1_000_000_000
10941093
_prefix = "D"
1095-
_td64_unit = "D"
10961094
_period_dtype_code = PeriodDtypeCode.D
10971095
_creso = NPY_DATETIMEUNIT.NPY_FR_D
10981096

10991097

11001098
cdef class Hour(Tick):
11011099
_nanos_inc = 3600 * 1_000_000_000
11021100
_prefix = "H"
1103-
_td64_unit = "h"
11041101
_period_dtype_code = PeriodDtypeCode.H
11051102
_creso = NPY_DATETIMEUNIT.NPY_FR_h
11061103

11071104

11081105
cdef class Minute(Tick):
11091106
_nanos_inc = 60 * 1_000_000_000
11101107
_prefix = "T"
1111-
_td64_unit = "m"
11121108
_period_dtype_code = PeriodDtypeCode.T
11131109
_creso = NPY_DATETIMEUNIT.NPY_FR_m
11141110

11151111

11161112
cdef class Second(Tick):
11171113
_nanos_inc = 1_000_000_000
11181114
_prefix = "S"
1119-
_td64_unit = "s"
11201115
_period_dtype_code = PeriodDtypeCode.S
11211116
_creso = NPY_DATETIMEUNIT.NPY_FR_s
11221117

11231118

11241119
cdef class Milli(Tick):
11251120
_nanos_inc = 1_000_000
11261121
_prefix = "L"
1127-
_td64_unit = "ms"
11281122
_period_dtype_code = PeriodDtypeCode.L
11291123
_creso = NPY_DATETIMEUNIT.NPY_FR_ms
11301124

11311125

11321126
cdef class Micro(Tick):
11331127
_nanos_inc = 1000
11341128
_prefix = "U"
1135-
_td64_unit = "us"
11361129
_period_dtype_code = PeriodDtypeCode.U
11371130
_creso = NPY_DATETIMEUNIT.NPY_FR_us
11381131

11391132

11401133
cdef class Nano(Tick):
11411134
_nanos_inc = 1
11421135
_prefix = "N"
1143-
_td64_unit = "ns"
11441136
_period_dtype_code = PeriodDtypeCode.N
11451137
_creso = NPY_DATETIMEUNIT.NPY_FR_ns
11461138

pandas/_libs/tslibs/period.pyx

+2-3
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ from pandas._libs.tslibs.nattype cimport (
104104
from pandas._libs.tslibs.offsets cimport (
105105
BaseOffset,
106106
is_offset_object,
107-
is_tick_object,
108107
to_offset,
109108
)
110109

@@ -1793,7 +1792,7 @@ cdef class _Period(PeriodMixin):
17931792
cdef:
17941793
int64_t inc
17951794

1796-
if not is_tick_object(self.freq):
1795+
if not self._dtype._is_tick_like():
17971796
raise IncompatibleFrequency("Input cannot be converted to "
17981797
f"Period(freq={self.freqstr})")
17991798

@@ -1805,7 +1804,7 @@ cdef class _Period(PeriodMixin):
18051804
return NaT
18061805

18071806
try:
1808-
inc = delta_to_nanoseconds(other, reso=self.freq._creso, round_ok=False)
1807+
inc = delta_to_nanoseconds(other, reso=self._dtype._creso, round_ok=False)
18091808
except ValueError as err:
18101809
raise IncompatibleFrequency("Input cannot be converted to "
18111810
f"Period(freq={self.freqstr})") from err

pandas/_testing/asserters.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ def _get_ilevel_values(index, level):
331331
if check_names:
332332
assert_attr_equal("names", left, right, obj=obj)
333333
if isinstance(left, PeriodIndex) or isinstance(right, PeriodIndex):
334-
assert_attr_equal("freq", left, right, obj=obj)
334+
assert_attr_equal("dtype", left, right, obj=obj)
335335
if isinstance(left, IntervalIndex) or isinstance(right, IntervalIndex):
336336
assert_interval_array_equal(left._values, right._values)
337337

pandas/core/arrays/period.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -773,14 +773,13 @@ def _add_timedelta_arraylike(
773773
-------
774774
PeriodArray
775775
"""
776-
freq = self.freq
777-
if not isinstance(freq, Tick):
776+
if not self.dtype._is_tick_like():
778777
# We cannot add timedelta-like to non-tick PeriodArray
779778
raise TypeError(
780779
f"Cannot add or subtract timedelta64[ns] dtype from {self.dtype}"
781780
)
782781

783-
dtype = np.dtype(f"m8[{freq._td64_unit}]")
782+
dtype = np.dtype(f"m8[{self.dtype._td64_unit}]")
784783

785784
# Similar to _check_timedeltalike_freq_compat, but we raise with a
786785
# more specific exception message if necessary.
@@ -824,9 +823,9 @@ def _check_timedeltalike_freq_compat(self, other):
824823
------
825824
IncompatibleFrequency
826825
"""
827-
assert isinstance(self.freq, Tick) # checked by calling function
826+
assert self.dtype._is_tick_like() # checked by calling function
828827

829-
dtype = np.dtype(f"m8[{self.freq._td64_unit}]")
828+
dtype = np.dtype(f"m8[{self.dtype._td64_unit}]")
830829

831830
if isinstance(other, (timedelta, np.timedelta64, Tick)):
832831
td = np.asarray(Timedelta(other).asm8)

0 commit comments

Comments
 (0)