Skip to content

Commit 9b95eeb

Browse files
authored
PERF: periodarr_to_dt64arr (#35171)
1 parent 406049e commit 9b95eeb

File tree

2 files changed

+63
-6
lines changed

2 files changed

+63
-6
lines changed

asv_bench/benchmarks/tslibs/period.py

+37-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22
Period benchmarks that rely only on tslibs. See benchmarks.period for
33
Period benchmarks that rely on other parts fo pandas.
44
"""
5-
from pandas import Period
5+
6+
import numpy as np
7+
8+
from pandas._libs.tslibs.period import Period, periodarr_to_dt64arr
69

710
from pandas.tseries.frequencies import to_offset
811

12+
from .tslib import _sizes
13+
914

1015
class PeriodProperties:
1116

@@ -68,3 +73,34 @@ def setup(self, freq, is_offset):
6873

6974
def time_period_constructor(self, freq, is_offset):
7075
Period("2012-06-01", freq=freq)
76+
77+
78+
class TimePeriodArrToDT64Arr:
79+
params = [
80+
_sizes,
81+
[
82+
1000,
83+
1011, # Annual - November End
84+
2000,
85+
2011, # Quarterly - November End
86+
3000,
87+
4000,
88+
4006, # Weekly - Saturday End
89+
5000,
90+
6000,
91+
7000,
92+
8000,
93+
9000,
94+
10000,
95+
11000,
96+
12000,
97+
],
98+
]
99+
param_names = ["size", "freq"]
100+
101+
def setup(self, size, freq):
102+
arr = np.arange(10, dtype="i8").repeat(size // 10)
103+
self.i8values = arr
104+
105+
def time_periodarray_to_dt64arr(self, size, freq):
106+
periodarr_to_dt64arr(self.i8values, freq)

pandas/_libs/tslibs/period.pyx

+26-5
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ from pandas._libs.tslibs.ccalendar cimport (
5454
get_days_in_month,
5555
)
5656
from pandas._libs.tslibs.ccalendar cimport c_MONTH_NUMBERS
57+
from pandas._libs.tslibs.conversion import ensure_datetime64ns
5758

5859
from pandas._libs.tslibs.dtypes cimport (
5960
PeriodDtypeBase,
@@ -943,14 +944,34 @@ def periodarr_to_dt64arr(const int64_t[:] periodarr, int freq):
943944
int64_t[:] out
944945
Py_ssize_t i, l
945946

946-
l = len(periodarr)
947+
if freq < 6000: # i.e. FR_DAY, hard-code to avoid need to cast
948+
l = len(periodarr)
949+
out = np.empty(l, dtype="i8")
947950

948-
out = np.empty(l, dtype='i8')
951+
# We get here with freqs that do not correspond to a datetime64 unit
952+
for i in range(l):
953+
out[i] = period_ordinal_to_dt64(periodarr[i], freq)
949954

950-
for i in range(l):
951-
out[i] = period_ordinal_to_dt64(periodarr[i], freq)
955+
return out.base # .base to access underlying np.ndarray
952956

953-
return out.base # .base to access underlying np.ndarray
957+
else:
958+
# Short-circuit for performance
959+
if freq == FR_NS:
960+
return periodarr.base
961+
962+
if freq == FR_US:
963+
dta = periodarr.base.view("M8[us]")
964+
elif freq == FR_MS:
965+
dta = periodarr.base.view("M8[ms]")
966+
elif freq == FR_SEC:
967+
dta = periodarr.base.view("M8[s]")
968+
elif freq == FR_MIN:
969+
dta = periodarr.base.view("M8[m]")
970+
elif freq == FR_HR:
971+
dta = periodarr.base.view("M8[h]")
972+
elif freq == FR_DAY:
973+
dta = periodarr.base.view("M8[D]")
974+
return ensure_datetime64ns(dta)
954975

955976

956977
cpdef int64_t period_asfreq(int64_t ordinal, int freq1, int freq2, bint end):

0 commit comments

Comments
 (0)