-
-
Notifications
You must be signed in to change notification settings - Fork 18.5k
REF: Move most remaining arith helpers to datetimelike mixins #21815
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
# -*- coding: utf-8 -*- | ||
import operator | ||
import warnings | ||
|
||
import numpy as np | ||
|
||
|
@@ -8,12 +9,16 @@ | |
from pandas._libs.tslibs.period import ( | ||
DIFFERENT_FREQ_INDEX, IncompatibleFrequency) | ||
|
||
from pandas.errors import NullFrequencyError | ||
from pandas.errors import NullFrequencyError, PerformanceWarning | ||
|
||
from pandas.tseries import frequencies | ||
from pandas.tseries.offsets import Tick | ||
|
||
from pandas.core.dtypes.common import is_period_dtype, is_timedelta64_dtype | ||
from pandas.core.dtypes.common import ( | ||
is_period_dtype, | ||
is_timedelta64_dtype, | ||
is_object_dtype) | ||
|
||
import pandas.core.common as com | ||
from pandas.core.algorithms import checked_add_with_arr | ||
|
||
|
@@ -108,38 +113,43 @@ def __getitem__(self, key): | |
if is_int: | ||
val = getitem(key) | ||
return self._box_func(val) | ||
|
||
if com.is_bool_indexer(key): | ||
key = np.asarray(key) | ||
if key.all(): | ||
key = slice(0, None, None) | ||
else: | ||
key = lib.maybe_booleans_to_slice(key.view(np.uint8)) | ||
|
||
attribs = self._get_attributes_dict() | ||
|
||
is_period = is_period_dtype(self) | ||
if is_period: | ||
freq = self.freq | ||
else: | ||
if com.is_bool_indexer(key): | ||
key = np.asarray(key) | ||
if key.all(): | ||
key = slice(0, None, None) | ||
freq = None | ||
if isinstance(key, slice): | ||
if self.freq is not None and key.step is not None: | ||
freq = key.step * self.freq | ||
else: | ||
key = lib.maybe_booleans_to_slice(key.view(np.uint8)) | ||
freq = self.freq | ||
|
||
attribs = self._get_attributes_dict() | ||
attribs['freq'] = freq | ||
|
||
is_period = is_period_dtype(self) | ||
result = getitem(key) | ||
if result.ndim > 1: | ||
# To support MPL which performs slicing with 2 dim | ||
# even though it only has 1 dim by definition | ||
if is_period: | ||
freq = self.freq | ||
else: | ||
freq = None | ||
if isinstance(key, slice): | ||
if self.freq is not None and key.step is not None: | ||
freq = key.step * self.freq | ||
else: | ||
freq = self.freq | ||
|
||
attribs['freq'] = freq | ||
return self._simple_new(result, **attribs) | ||
return result | ||
|
||
result = getitem(key) | ||
if result.ndim > 1: | ||
# To support MPL which performs slicing with 2 dim | ||
# even though it only has 1 dim by definition | ||
if is_period: | ||
return self._simple_new(result, **attribs) | ||
return result | ||
return self._simple_new(result, **attribs) | ||
|
||
return self._simple_new(result, **attribs) | ||
def astype(self, dtype, copy=True): | ||
if is_object_dtype(dtype): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. at some point pls add a doc-string |
||
return self._box_values(self.asi8) | ||
return super(DatetimeLikeArrayMixin, self).astype(dtype, copy) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
# ------------------------------------------------------------------ | ||
# Null Handling | ||
|
@@ -397,3 +407,31 @@ def _addsub_int_array(self, other, op): | |
# to _addsub_offset_array | ||
assert not is_timedelta64_dtype(self) | ||
return op(self, np.array(other) * self.freq) | ||
|
||
def _addsub_offset_array(self, other, op): | ||
""" | ||
Add or subtract array-like of DateOffset objects | ||
|
||
Parameters | ||
---------- | ||
other : Index, np.ndarray | ||
object-dtype containing pd.DateOffset objects | ||
op : {operator.add, operator.sub} | ||
|
||
Returns | ||
------- | ||
result : same class as self | ||
""" | ||
assert op in [operator.add, operator.sub] | ||
if len(other) == 1: | ||
return op(self, other[0]) | ||
|
||
warnings.warn("Adding/subtracting array of DateOffsets to " | ||
"{cls} not vectorized" | ||
.format(cls=type(self).__name__), PerformanceWarning) | ||
|
||
res_values = op(self.astype('O').values, np.array(other)) | ||
kwargs = {} | ||
if not is_period_dtype(self): | ||
kwargs['freq'] = 'infer' | ||
return type(self)(res_values, **kwargs) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,14 +8,16 @@ | |
from pandas._libs.tslib import NaT, iNaT | ||
from pandas._libs.tslibs.period import ( | ||
Period, IncompatibleFrequency, DIFFERENT_FREQ_INDEX, | ||
get_period_field_arr) | ||
get_period_field_arr, period_asfreq_arr) | ||
from pandas._libs.tslibs import period as libperiod | ||
from pandas._libs.tslibs.timedeltas import delta_to_nanoseconds | ||
from pandas._libs.tslibs.fields import isleapyear_arr | ||
|
||
from pandas import compat | ||
from pandas.util._decorators import cache_readonly | ||
|
||
from pandas.core.dtypes.common import is_integer_dtype, is_float_dtype | ||
from pandas.core.dtypes.common import ( | ||
is_integer_dtype, is_float_dtype, is_period_dtype) | ||
from pandas.core.dtypes.dtypes import PeriodDtype | ||
|
||
from pandas.tseries import frequencies | ||
|
@@ -113,12 +115,23 @@ def freq(self, value): | |
|
||
_attributes = ["freq"] | ||
|
||
def __new__(cls, values, freq=None, **kwargs): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe add a todo comment to convert this to init once the Mixin is converted to proper array |
||
if is_period_dtype(values): | ||
# PeriodArray, PeriodIndex | ||
if freq is not None and values.freq != freq: | ||
raise IncompatibleFrequency(freq, values.freq) | ||
freq = values.freq | ||
values = values.asi8 | ||
|
||
return cls._simple_new(values, freq, **kwargs) | ||
|
||
@classmethod | ||
def _simple_new(cls, values, freq=None, **kwargs): | ||
""" | ||
Values can be any type that can be coerced to Periods. | ||
Ordinals in an ndarray are fastpath-ed to `_from_ordinals` | ||
""" | ||
|
||
if not is_integer_dtype(values): | ||
values = np.array(values, copy=False) | ||
if len(values) > 0 and is_float_dtype(values): | ||
|
@@ -128,8 +141,6 @@ def _simple_new(cls, values, freq=None, **kwargs): | |
|
||
return cls._from_ordinals(values, freq) | ||
|
||
__new__ = _simple_new # For now... | ||
|
||
@classmethod | ||
def _from_ordinals(cls, values, freq=None): | ||
""" | ||
|
@@ -173,6 +184,65 @@ def is_leap_year(self): | |
""" Logical indicating if the date belongs to a leap year """ | ||
return isleapyear_arr(np.asarray(self.year)) | ||
|
||
def asfreq(self, freq=None, how='E'): | ||
""" | ||
Convert the Period Array/Index to the specified frequency `freq`. | ||
|
||
Parameters | ||
---------- | ||
freq : str | ||
a frequency | ||
how : str {'E', 'S'} | ||
'E', 'END', or 'FINISH' for end, | ||
'S', 'START', or 'BEGIN' for start. | ||
Whether the elements should be aligned to the end | ||
or start within pa period. January 31st ('END') vs. | ||
January 1st ('START') for example. | ||
|
||
Returns | ||
------- | ||
new : Period Array/Index with the new frequency | ||
|
||
Examples | ||
-------- | ||
>>> pidx = pd.period_range('2010-01-01', '2015-01-01', freq='A') | ||
>>> pidx | ||
<class 'pandas.core.indexes.period.PeriodIndex'> | ||
[2010, ..., 2015] | ||
Length: 6, Freq: A-DEC | ||
|
||
>>> pidx.asfreq('M') | ||
<class 'pandas.core.indexes.period.PeriodIndex'> | ||
[2010-12, ..., 2015-12] | ||
Length: 6, Freq: M | ||
|
||
>>> pidx.asfreq('M', how='S') | ||
<class 'pandas.core.indexes.period.PeriodIndex'> | ||
[2010-01, ..., 2015-01] | ||
Length: 6, Freq: M | ||
""" | ||
how = libperiod._validate_end_alias(how) | ||
|
||
freq = Period._maybe_convert_freq(freq) | ||
|
||
base1, mult1 = frequencies.get_freq_code(self.freq) | ||
base2, mult2 = frequencies.get_freq_code(freq) | ||
|
||
asi8 = self.asi8 | ||
# mult1 can't be negative or 0 | ||
end = how == 'E' | ||
if end: | ||
ordinal = asi8 + mult1 - 1 | ||
else: | ||
ordinal = asi8 | ||
|
||
new_data = period_asfreq_arr(ordinal, base1, base2, end) | ||
|
||
if self.hasnans: | ||
new_data[self._isnan] = iNaT | ||
|
||
return self._simple_new(new_data, self.name, freq=freq) | ||
|
||
# ------------------------------------------------------------------ | ||
# Arithmetic Methods | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just de-indenting this block, requested on the previous PR.