-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
[REF] Move comparison methods to EAMixins, share code #21872
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
1d6e970
ea608fc
0f39561
fbf1e6e
e7b7e90
5b0aa46
0b88a5c
af07649
e05c7e4
6da6da3
5d142c9
0af9876
1044d23
89e92b8
8ecc86d
1bcdb8f
935ac7b
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 |
---|---|---|
|
@@ -10,19 +10,53 @@ | |
DIFFERENT_FREQ_INDEX, IncompatibleFrequency) | ||
|
||
from pandas.errors import NullFrequencyError, PerformanceWarning | ||
from pandas import compat | ||
|
||
from pandas.tseries import frequencies | ||
from pandas.tseries.offsets import Tick | ||
|
||
from pandas.core.dtypes.common import ( | ||
needs_i8_conversion, | ||
is_list_like, | ||
is_bool_dtype, | ||
is_period_dtype, | ||
is_timedelta64_dtype, | ||
is_object_dtype) | ||
from pandas.core.dtypes.generic import ABCSeries, ABCDataFrame, ABCIndexClass | ||
|
||
import pandas.core.common as com | ||
from pandas.core.algorithms import checked_add_with_arr | ||
|
||
|
||
def _make_comparison_op(op, cls): | ||
# TODO: share code with indexes.base version? Main difference is that | ||
# the block for MultiIndex was removed here. | ||
def cmp_method(self, other): | ||
if isinstance(other, ABCDataFrame): | ||
return NotImplemented | ||
|
||
if isinstance(other, (np.ndarray, ABCIndexClass, ABCSeries)): | ||
if other.ndim > 0 and len(self) != len(other): | ||
raise ValueError('Lengths must match to compare') | ||
|
||
if needs_i8_conversion(self) and needs_i8_conversion(other): | ||
# we may need to directly compare underlying | ||
# representations | ||
return self._evaluate_compare(other, op) | ||
|
||
# numpy will show a DeprecationWarning on invalid elementwise | ||
# comparisons, this will raise in the future | ||
with warnings.catch_warnings(record=True): | ||
with np.errstate(all='ignore'): | ||
result = op(self.values, np.asarray(other)) | ||
|
||
return result | ||
|
||
name = '__{name}__'.format(name=op.__name__) | ||
# TODO: docstring? | ||
return compat.set_function_name(cmp_method, name, cls) | ||
|
||
|
||
class AttributesMixin(object): | ||
|
||
@property | ||
|
@@ -435,3 +469,85 @@ def _addsub_offset_array(self, other, op): | |
if not is_period_dtype(self): | ||
kwargs['freq'] = 'infer' | ||
return type(self)(res_values, **kwargs) | ||
|
||
# -------------------------------------------------------------- | ||
# Comparison Methods | ||
|
||
def _evaluate_compare(self, other, op): | ||
""" | ||
We have been called because a comparison between | ||
8 aware arrays. numpy >= 1.11 will | ||
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. "8 aware arrays" ? 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. This is the existing docstring in indexes.datetimelike. I'm open to suggestions. |
||
now warn about NaT comparisons | ||
""" | ||
# Called by comparison methods when comparing datetimelike | ||
# with datetimelike | ||
|
||
if not isinstance(other, type(self)): | ||
# coerce to a similar object | ||
if not is_list_like(other): | ||
# scalar | ||
other = [other] | ||
elif lib.is_scalar(lib.item_from_zerodim(other)): | ||
# ndarray scalar | ||
other = [other.item()] | ||
other = type(self)(other) | ||
|
||
# compare | ||
result = op(self.asi8, other.asi8) | ||
|
||
# technically we could support bool dtyped Index | ||
# for now just return the indexing array directly | ||
mask = (self._isnan) | (other._isnan) | ||
|
||
filler = iNaT | ||
if is_bool_dtype(result): | ||
filler = False | ||
|
||
result[mask] = filler | ||
return result | ||
|
||
# TODO: get this from ExtensionOpsMixin | ||
@classmethod | ||
def _add_comparison_methods(cls): | ||
""" add in comparison methods """ | ||
# DatetimeArray and TimedeltaArray comparison methods will | ||
# call these as their super(...) methods | ||
cls.__eq__ = _make_comparison_op(operator.eq, cls) | ||
cls.__ne__ = _make_comparison_op(operator.ne, cls) | ||
cls.__lt__ = _make_comparison_op(operator.lt, cls) | ||
cls.__gt__ = _make_comparison_op(operator.gt, cls) | ||
cls.__le__ = _make_comparison_op(operator.le, cls) | ||
cls.__ge__ = _make_comparison_op(operator.ge, cls) | ||
|
||
|
||
DatetimeLikeArrayMixin._add_comparison_methods() | ||
|
||
|
||
# ------------------------------------------------------------------- | ||
# Shared Constructor Helpers | ||
|
||
def validate_periods(periods): | ||
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. docstring |
||
""" | ||
If a `periods` argument is passed to the Datetime/Timedelta Array/Index | ||
constructor, cast it to an integer. | ||
|
||
Parameters | ||
---------- | ||
periods : None, float, int | ||
|
||
Returns | ||
------- | ||
periods : None or int | ||
|
||
Raises | ||
------ | ||
TypeError | ||
if periods is None, float, or int | ||
""" | ||
if periods is not None: | ||
if lib.is_float(periods): | ||
periods = int(periods) | ||
elif not lib.is_integer(periods): | ||
raise TypeError('periods must be a number, got {periods}' | ||
.format(periods=periods)) | ||
return periods |
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.
Yes, if you can.