Skip to content

REF: move bits of offsets to liboffsets, de-privatize #33936

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions asv_bench/benchmarks/io/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

try:
from pandas._libs.tslibs.parsing import (
_concat_date_cols,
concat_date_cols,
_does_string_look_like_datetime,
)
except ImportError:
Expand Down Expand Up @@ -39,4 +39,4 @@ def setup(self, value, dim):
)

def time_check_concat(self, value, dim):
_concat_date_cols(self.object)
concat_date_cols(self.object)
6 changes: 3 additions & 3 deletions pandas/_libs/tslibs/frequencies.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,13 @@ cpdef get_freq_code(freqstr):
if is_integer_object(freqstr):
return freqstr, 1

base, stride = _base_and_stride(freqstr)
base, stride = base_and_stride(freqstr)
code = _period_str_to_code(base)

return code, stride


cpdef _base_and_stride(str freqstr):
cpdef base_and_stride(str freqstr):
"""
Return base freq and stride info from string representation

Expand Down Expand Up @@ -267,7 +267,7 @@ cpdef str get_base_alias(freqstr):
-------
base_alias : str
"""
return _base_and_stride(freqstr)[0]
return base_and_stride(freqstr)[0]


cpdef int get_to_timestamp_base(int base):
Expand Down
113 changes: 109 additions & 4 deletions pandas/_libs/tslibs/offsets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import cython

import time
from typing import Any
import warnings
from cpython.datetime cimport (PyDateTime_IMPORT,
PyDateTime_Check,
PyDelta_Check,
Expand Down Expand Up @@ -103,7 +104,7 @@ def as_datetime(obj):
return obj


cpdef bint _is_normalized(dt):
cpdef bint is_normalized(dt):
if (dt.hour != 0 or dt.minute != 0 or dt.second != 0 or
dt.microsecond != 0 or getattr(dt, 'nanosecond', 0) != 0):
return False
Expand Down Expand Up @@ -230,7 +231,7 @@ def _get_calendar(weekmask, holidays, calendar):
holidays = holidays + calendar.holidays().tolist()
except AttributeError:
pass
holidays = [_to_dt64D(dt) for dt in holidays]
holidays = [to_dt64D(dt) for dt in holidays]
holidays = tuple(sorted(holidays))

kwargs = {'weekmask': weekmask}
Expand All @@ -241,7 +242,7 @@ def _get_calendar(weekmask, holidays, calendar):
return busdaycalendar, holidays


def _to_dt64D(dt):
def to_dt64D(dt):
# Currently
# > np.datetime64(dt.datetime(2013,5,1),dtype='datetime64[D]')
# numpy.datetime64('2013-05-01T02:00:00.000000+0200')
Expand All @@ -264,7 +265,7 @@ def _to_dt64D(dt):
# Validation


def _validate_business_time(t_input):
def validate_business_time(t_input):
if isinstance(t_input, str):
try:
t = time.strptime(t_input, '%H:%M')
Expand Down Expand Up @@ -440,6 +441,9 @@ class _BaseOffset:
# that allows us to use methods that can go in a `cdef class`
return self * 1

# ------------------------------------------------------------------
# Name and Rendering Methods

def __repr__(self) -> str:
className = getattr(self, '_outputName', type(self).__name__)

Expand All @@ -455,6 +459,44 @@ class _BaseOffset:
out = f'<{n_str}{className}{plural}{self._repr_attrs()}>'
return out

@property
def name(self) -> str:
return self.rule_code

@property
def _prefix(self) -> str:
raise NotImplementedError("Prefix not defined")

@property
def rule_code(self) -> str:
return self._prefix

@property
def freqstr(self) -> str:
try:
code = self.rule_code
except NotImplementedError:
return str(repr(self))

if self.n != 1:
fstr = f"{self.n}{code}"
else:
fstr = code

try:
if self._offset:
fstr += self._offset_str()
except AttributeError:
# TODO: standardize `_offset` vs `offset` naming convention
pass

return fstr

def _offset_str(self) -> str:
return ""

# ------------------------------------------------------------------

def _get_offset_day(self, datetime other):
# subclass must implement `_day_opt`; calling from the base class
# will raise NotImplementedError.
Expand Down Expand Up @@ -530,6 +572,26 @@ class _BaseOffset:

return state

@property
def nanos(self):
raise ValueError(f"{self} is a non-fixed frequency")

def onOffset(self, dt) -> bool:
warnings.warn(
"onOffset is a deprecated, use is_on_offset instead",
FutureWarning,
stacklevel=1,
)
return self.is_on_offset(dt)

def isAnchored(self) -> bool:
warnings.warn(
"isAnchored is a deprecated, use is_anchored instead",
FutureWarning,
stacklevel=1,
)
return self.is_anchored()


class BaseOffset(_BaseOffset):
# Here we add __rfoo__ methods that don't play well with cdef classes
Expand Down Expand Up @@ -564,6 +626,49 @@ class _Tick:
return _wrap_timedelta_result(result)


class BusinessMixin:
"""
Mixin to business types to provide related functions.
"""

@property
def offset(self):
"""
Alias for self._offset.
"""
# Alias for backward compat
return self._offset

def _repr_attrs(self) -> str:
if self.offset:
attrs = [f"offset={repr(self.offset)}"]
else:
attrs = []
out = ""
if attrs:
out += ": " + ", ".join(attrs)
return out


class CustomMixin:
"""
Mixin for classes that define and validate calendar, holidays,
and weekdays attributes.
"""

def __init__(self, weekmask, holidays, calendar):
calendar, holidays = _get_calendar(
weekmask=weekmask, holidays=holidays, calendar=calendar
)
# Custom offset instances are identified by the
# following two attributes. See DateOffset._params()
# holidays, weekmask

object.__setattr__(self, "weekmask", weekmask)
object.__setattr__(self, "holidays", holidays)
object.__setattr__(self, "calendar", calendar)


# ----------------------------------------------------------------------
# RelativeDelta Arithmetic

Expand Down
4 changes: 2 additions & 2 deletions pandas/_libs/tslibs/parsing.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@ cdef inline object convert_to_unicode(object item, bint keep_trivial_numbers):

@cython.wraparound(False)
@cython.boundscheck(False)
def _concat_date_cols(tuple date_cols, bint keep_trivial_numbers=True):
def concat_date_cols(tuple date_cols, bint keep_trivial_numbers=True):
"""
Concatenates elements from numpy arrays in `date_cols` into strings.

Expand All @@ -957,7 +957,7 @@ def _concat_date_cols(tuple date_cols, bint keep_trivial_numbers=True):
--------
>>> dates=np.array(['3/31/2019', '4/31/2019'], dtype=object)
>>> times=np.array(['11:20', '10:45'], dtype=object)
>>> result = _concat_date_cols((dates, times))
>>> result = concat_date_cols((dates, times))
>>> result
array(['3/31/2019 11:20', '4/31/2019 10:45'], dtype=object)
"""
Expand Down
6 changes: 3 additions & 3 deletions pandas/_libs/tslibs/period.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1685,7 +1685,7 @@ cdef class _Period:
resampled : Period
"""
freq = self._maybe_convert_freq(freq)
how = _validate_end_alias(how)
how = validate_end_alias(how)
base1, mult1 = get_freq_code(self.freq)
base2, mult2 = get_freq_code(freq)

Expand Down Expand Up @@ -1758,7 +1758,7 @@ cdef class _Period:
"""
if freq is not None:
freq = self._maybe_convert_freq(freq)
how = _validate_end_alias(how)
how = validate_end_alias(how)

end = how == 'E'
if end:
Expand Down Expand Up @@ -2509,7 +2509,7 @@ def quarter_to_myear(year: int, quarter: int, freq):
return year, month


def _validate_end_alias(how):
def validate_end_alias(how):
how_dict = {'S': 'S', 'E': 'E',
'START': 'S', 'FINISH': 'E',
'BEGIN': 'S', 'END': 'E'}
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/arrays/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,7 @@ def to_period(self, freq=None):

# https://github.com/pandas-dev/pandas/issues/33358
if res is None:
base, stride = libfrequencies._base_and_stride(freq)
base, stride = libfrequencies.base_and_stride(freq)
res = f"{stride}{base}"

freq = res
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/arrays/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ def to_timestamp(self, freq=None, how="start"):
"""
from pandas.core.arrays import DatetimeArray

how = libperiod._validate_end_alias(how)
how = libperiod.validate_end_alias(how)

end = how == "E"
if end:
Expand Down Expand Up @@ -524,7 +524,7 @@ def asfreq(self, freq=None, how: str = "E") -> "PeriodArray":
PeriodIndex(['2010-01', '2011-01', '2012-01', '2013-01', '2014-01',
'2015-01'], dtype='period[M]', freq='M')
"""
how = libperiod._validate_end_alias(how)
how = libperiod.validate_end_alias(how)

freq = Period._maybe_convert_freq(freq)

Expand Down
4 changes: 2 additions & 2 deletions pandas/io/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3264,7 +3264,7 @@ def _make_date_converter(
):
def converter(*date_cols):
if date_parser is None:
strs = parsing._concat_date_cols(date_cols)
strs = parsing.concat_date_cols(date_cols)

try:
return tools.to_datetime(
Expand Down Expand Up @@ -3292,7 +3292,7 @@ def converter(*date_cols):
try:
return tools.to_datetime(
parsing.try_parse_dates(
parsing._concat_date_cols(date_cols),
parsing.concat_date_cols(date_cols),
parser=date_parser,
dayfirst=dayfirst,
),
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/io/parser/test_parse_dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def date_parser(*date_cols):
-------
parsed : Series
"""
return parsing.try_parse_dates(parsing._concat_date_cols(date_cols))
return parsing.try_parse_dates(parsing.concat_date_cols(date_cols))

result = parser.read_csv(
StringIO(data),
Expand Down Expand Up @@ -208,7 +208,7 @@ def test_concat_date_col_fail(container, dim):
date_cols = tuple(container([value]) for _ in range(dim))

with pytest.raises(ValueError, match=msg):
parsing._concat_date_cols(date_cols)
parsing.concat_date_cols(date_cols)


@pytest.mark.parametrize("keep_date_col", [True, False])
Expand Down
2 changes: 1 addition & 1 deletion pandas/tseries/frequencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def to_offset(freq) -> Optional[DateOffset]:
stride = freq[1]
if isinstance(stride, str):
name, stride = stride, name
name, _ = libfreqs._base_and_stride(name)
name, _ = libfreqs.base_and_stride(name)
delta = _get_offset(name) * stride

elif isinstance(freq, timedelta):
Expand Down
Loading