Skip to content

Commit

Permalink
Tslibs resolution (#18034)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrockmendel authored and jreback committed Nov 13, 2017
1 parent 4292a27 commit c3cfe90
Show file tree
Hide file tree
Showing 11 changed files with 717 additions and 682 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.22.0.txt
Expand Up @@ -45,6 +45,7 @@ Other API Changes
- :class:`Timestamp` will no longer silently ignore unused or invalid ``tz`` or ``tzinfo`` keyword arguments (:issue:`17690`)
- :class:`Timestamp` will no longer silently ignore invalid ``freq`` arguments (:issue:`5168`)
- :class:`CacheableOffset` and :class:`WeekDay` are no longer available in the ``pandas.tseries.offsets`` module (:issue:`17830`)
- `tseries.frequencies.get_freq_group()` and `tseries.frequencies.DAYS` are removed from the public API (:issue:`18034`)

.. _whatsnew_0220.deprecations:

Expand Down
112 changes: 10 additions & 102 deletions pandas/_libs/period.pyx
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# cython: profile=False
from datetime import datetime, date, timedelta
import operator

Expand Down Expand Up @@ -27,28 +28,23 @@ from util cimport is_period_object, is_string_object, INT32_MIN

from lib cimport is_null_datetimelike
from pandas._libs import tslib
from pandas._libs.tslib import Timestamp, iNaT, NaT
from pandas._libs.tslib import Timestamp, iNaT
from tslibs.timezones cimport (
is_utc, is_tzlocal, get_utcoffset, get_dst_info, maybe_get_tz)
from tslibs.timedeltas cimport delta_to_nanoseconds

from tslibs.parsing import parse_time_string, NAT_SENTINEL
from tslibs.parsing import (parse_time_string, NAT_SENTINEL,
_get_rule_month, _MONTH_NUMBERS)
from tslibs.frequencies cimport get_freq_code
from tslibs.nattype import nat_strings
from tslibs.resolution import resolution, Resolution
from tslibs.nattype import nat_strings, NaT
from tslibs.nattype cimport _nat_scalar_rules

from pandas.tseries import offsets
from pandas.tseries import frequencies

cdef int64_t NPY_NAT = util.get_nat()

cdef int RESO_US = frequencies.RESO_US
cdef int RESO_MS = frequencies.RESO_MS
cdef int RESO_SEC = frequencies.RESO_SEC
cdef int RESO_MIN = frequencies.RESO_MIN
cdef int RESO_HR = frequencies.RESO_HR
cdef int RESO_DAY = frequencies.RESO_DAY

cdef extern from "period_helper.h":
ctypedef struct date_info:
int64_t absdate
Expand Down Expand Up @@ -487,98 +483,10 @@ def extract_freq(ndarray[object] values):
raise ValueError('freq not specified and cannot be inferred')


cpdef resolution(ndarray[int64_t] stamps, tz=None):
cdef:
Py_ssize_t i, n = len(stamps)
pandas_datetimestruct dts
int reso = RESO_DAY, curr_reso

if tz is not None:
tz = maybe_get_tz(tz)
return _reso_local(stamps, tz)
else:
for i in range(n):
if stamps[i] == NPY_NAT:
continue
dt64_to_dtstruct(stamps[i], &dts)
curr_reso = _reso_stamp(&dts)
if curr_reso < reso:
reso = curr_reso
return reso


cdef inline int _reso_stamp(pandas_datetimestruct *dts):
if dts.us != 0:
if dts.us % 1000 == 0:
return RESO_MS
return RESO_US
elif dts.sec != 0:
return RESO_SEC
elif dts.min != 0:
return RESO_MIN
elif dts.hour != 0:
return RESO_HR
return RESO_DAY

cdef _reso_local(ndarray[int64_t] stamps, object tz):
cdef:
Py_ssize_t n = len(stamps)
int reso = RESO_DAY, curr_reso
ndarray[int64_t] trans, deltas, pos
pandas_datetimestruct dts

if is_utc(tz):
for i in range(n):
if stamps[i] == NPY_NAT:
continue
dt64_to_dtstruct(stamps[i], &dts)
curr_reso = _reso_stamp(&dts)
if curr_reso < reso:
reso = curr_reso
elif is_tzlocal(tz):
for i in range(n):
if stamps[i] == NPY_NAT:
continue
dt64_to_dtstruct(stamps[i], &dts)
dt = datetime(dts.year, dts.month, dts.day, dts.hour,
dts.min, dts.sec, dts.us, tz)
delta = int(get_utcoffset(tz, dt).total_seconds()) * 1000000000
dt64_to_dtstruct(stamps[i] + delta, &dts)
curr_reso = _reso_stamp(&dts)
if curr_reso < reso:
reso = curr_reso
else:
# Adjust datetime64 timestamp, recompute datetimestruct
trans, deltas, typ = get_dst_info(tz)

_pos = trans.searchsorted(stamps, side='right') - 1
if _pos.dtype != np.int64:
_pos = _pos.astype(np.int64)
pos = _pos

# statictzinfo
if typ not in ['pytz', 'dateutil']:
for i in range(n):
if stamps[i] == NPY_NAT:
continue
dt64_to_dtstruct(stamps[i] + deltas[0], &dts)
curr_reso = _reso_stamp(&dts)
if curr_reso < reso:
reso = curr_reso
else:
for i in range(n):
if stamps[i] == NPY_NAT:
continue
dt64_to_dtstruct(stamps[i] + deltas[pos[i]], &dts)
curr_reso = _reso_stamp(&dts)
if curr_reso < reso:
reso = curr_reso

return reso


# -----------------------------------------------------------------------
# period helpers


cdef ndarray[int64_t] localize_dt64arr_to_period(ndarray[int64_t] stamps,
int freq, object tz):
cdef:
Expand Down Expand Up @@ -1191,7 +1099,7 @@ class Period(_Period):

if freq is None:
try:
freq = frequencies.Resolution.get_freq(reso)
freq = Resolution.get_freq(reso)
except KeyError:
raise ValueError(
"Invalid frequency or could not infer: %s" % reso)
Expand Down Expand Up @@ -1236,7 +1144,7 @@ def _quarter_to_myear(year, quarter, freq):
if quarter <= 0 or quarter > 4:
raise ValueError('Quarter must be 1 <= q <= 4')

mnum = tslib._MONTH_NUMBERS[tslib._get_rule_month(freq)] + 1
mnum = _MONTH_NUMBERS[_get_rule_month(freq)] + 1
month = (mnum + (quarter - 1) * 3) % 12 + 1
if month > mnum:
year -= 1
Expand Down
55 changes: 1 addition & 54 deletions pandas/_libs/tslib.pyx
Expand Up @@ -50,6 +50,7 @@ from datetime cimport (
# stdlib datetime imports
from datetime import time as datetime_time


from tslibs.np_datetime cimport (check_dts_bounds,
reverse_ops,
cmp_scalar,
Expand All @@ -61,12 +62,6 @@ from tslibs.np_datetime cimport (check_dts_bounds,
get_timedelta64_value)
from tslibs.np_datetime import OutOfBoundsDatetime

from khash cimport (
khiter_t,
kh_destroy_int64, kh_put_int64,
kh_init_int64, kh_int64_t,
kh_resize_int64, kh_get_int64)

from .tslibs.parsing import parse_datetime_string

cimport cython
Expand Down Expand Up @@ -877,33 +872,6 @@ Timestamp.min = Timestamp(_NS_LOWER_BOUND)
Timestamp.max = Timestamp(_NS_UPPER_BOUND)


# ----------------------------------------------------------------------
# Frequency inference

def unique_deltas(ndarray[int64_t] arr):
cdef:
Py_ssize_t i, n = len(arr)
int64_t val
khiter_t k
kh_int64_t *table
int ret = 0
list uniques = []

table = kh_init_int64()
kh_resize_int64(table, 10)
for i in range(n - 1):
val = arr[i + 1] - arr[i]
k = kh_get_int64(table, val)
if k == table.n_buckets:
kh_put_int64(table, val, &ret)
uniques.append(val)
kh_destroy_int64(table)

result = np.array(uniques, dtype=np.int64)
result.sort()
return result


cdef str _NDIM_STRING = "ndim"

# This is PITA. Because we inherit from datetime, which has very specific
Expand Down Expand Up @@ -1388,27 +1356,6 @@ _MONTH_NUMBERS = {k: i for i, k in enumerate(_MONTHS)}
_MONTH_ALIASES = {(k + 1): v for k, v in enumerate(_MONTHS)}


cpdef object _get_rule_month(object source, object default='DEC'):
"""
Return starting month of given freq, default is December.
Example
-------
>>> _get_rule_month('D')
'DEC'
>>> _get_rule_month('A-JAN')
'JAN'
"""
if hasattr(source, 'freqstr'):
source = source.freqstr
source = source.upper()
if '-' not in source:
return default
else:
return source.split('-')[1]


cpdef array_with_unit_to_datetime(ndarray values, unit, errors='coerce'):
"""
convert the ndarray according to the unit
Expand Down

0 comments on commit c3cfe90

Please sign in to comment.