diff --git a/asv_bench/benchmarks/tslibs/period.py b/asv_bench/benchmarks/tslibs/period.py index 9156c4aa90ea0..1a2c89b48c665 100644 --- a/asv_bench/benchmarks/tslibs/period.py +++ b/asv_bench/benchmarks/tslibs/period.py @@ -2,10 +2,15 @@ Period benchmarks that rely only on tslibs. See benchmarks.period for Period benchmarks that rely on other parts fo pandas. """ -from pandas import Period + +import numpy as np + +from pandas._libs.tslibs.period import Period, periodarr_to_dt64arr from pandas.tseries.frequencies import to_offset +from .tslib import _sizes + class PeriodProperties: @@ -68,3 +73,34 @@ def setup(self, freq, is_offset): def time_period_constructor(self, freq, is_offset): Period("2012-06-01", freq=freq) + + +class TimePeriodArrToDT64Arr: + params = [ + _sizes, + [ + 1000, + 1011, # Annual - November End + 2000, + 2011, # Quarterly - November End + 3000, + 4000, + 4006, # Weekly - Saturday End + 5000, + 6000, + 7000, + 8000, + 9000, + 10000, + 11000, + 12000, + ], + ] + param_names = ["size", "freq"] + + def setup(self, size, freq): + arr = np.arange(10, dtype="i8").repeat(size // 10) + self.i8values = arr + + def time_periodarray_to_dt64arr(self, size, freq): + periodarr_to_dt64arr(self.i8values, freq) diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index c0641297c4b8a..dbfb26784be63 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -56,6 +56,7 @@ from pandas._libs.tslibs.ccalendar cimport ( get_days_in_month, ) from pandas._libs.tslibs.ccalendar cimport c_MONTH_NUMBERS +from pandas._libs.tslibs.conversion import ensure_datetime64ns from pandas._libs.tslibs.dtypes cimport ( PeriodDtypeBase, @@ -946,14 +947,34 @@ def periodarr_to_dt64arr(const int64_t[:] periodarr, int freq): int64_t[:] out Py_ssize_t i, l - l = len(periodarr) + if freq < 6000: # i.e. FR_DAY, hard-code to avoid need to cast + l = len(periodarr) + out = np.empty(l, dtype="i8") - out = np.empty(l, dtype='i8') + # We get here with freqs that do not correspond to a datetime64 unit + for i in range(l): + out[i] = period_ordinal_to_dt64(periodarr[i], freq) - for i in range(l): - out[i] = period_ordinal_to_dt64(periodarr[i], freq) + return out.base # .base to access underlying np.ndarray - return out.base # .base to access underlying np.ndarray + else: + # Short-circuit for performance + if freq == FR_NS: + return periodarr.base + + if freq == FR_US: + dta = periodarr.base.view("M8[us]") + elif freq == FR_MS: + dta = periodarr.base.view("M8[ms]") + elif freq == FR_SEC: + dta = periodarr.base.view("M8[s]") + elif freq == FR_MIN: + dta = periodarr.base.view("M8[m]") + elif freq == FR_HR: + dta = periodarr.base.view("M8[h]") + elif freq == FR_DAY: + dta = periodarr.base.view("M8[D]") + return ensure_datetime64ns(dta) cpdef int64_t period_asfreq(int64_t ordinal, int freq1, int freq2, bint end):