def wrapper(self, other): if isinstance(other, str): try: other = self._scalar_from_string(other) except ValueError: # failed to parse as timedelta return invalid_comparison(self, other, op) if isinstance(other, self._recognized_scalars) or other is NaT: other = self._scalar_type(other) self._check_compatible_with(other) other_i8 = self._unbox_scalar(other) result = op(self.view("i8"), other_i8) if isna(other): result.fill(nat_result) elif not is_list_like(other): return invalid_comparison(self, other, op) elif len(other) != len(self): raise ValueError("Lengths must match") else: if isinstance(other, list): other = np.array(other) if not isinstance(other, (np.ndarray, cls)): return invalid_comparison(self, other, op) if is_object_dtype(other): with np.errstate(all="ignore"): result = ops.comp_method_OBJECT_ARRAY( op, self.astype(object), other ) o_mask = isna(other) elif not cls._is_recognized_dtype(other.dtype): # e.g. other is datetimearray return invalid_comparison(self, other, op) else: other = type(self)._from_sequence(other) self._check_compatible_with(other) result = op(self.view("i8"), other.view("i8")) o_mask = other._isnan if o_mask.any(): result[o_mask] = nat_result if self._hasnans: result[self._isnan] = nat_result return result
def comparison_op(left: Union[np.ndarray, ABCExtensionArray], right: Any, op) -> Union[np.ndarray, ABCExtensionArray]: """ Evaluate a comparison operation `=`, `!=`, `>=`, `>`, `<=`, or `<`. Parameters ---------- left : np.ndarray or ExtensionArray right : object Cannot be a DataFrame, Series, or Index. op : {operator.eq, operator.ne, operator.gt, operator.ge, operator.lt, operator.le} Returns ------- ndarrray or ExtensionArray """ from pandas.core.ops import should_extension_dispatch, dispatch_to_extension_op # NB: We assume extract_array has already been called on left and right lvalues = left rvalues = right rvalues = lib.item_from_zerodim(rvalues) if isinstance(rvalues, list): # TODO: same for tuples? rvalues = np.asarray(rvalues) if isinstance(rvalues, (np.ndarray, ABCExtensionArray, ABCIndexClass)): # TODO: make this treatment consistent across ops and classes. # We are not catching all listlikes here (e.g. frozenset, tuple) # The ambiguous case is object-dtype. See GH#27803 if len(lvalues) != len(rvalues): raise ValueError("Lengths must match to compare") if should_extension_dispatch(lvalues, rvalues): res_values = dispatch_to_extension_op(op, lvalues, rvalues) elif is_scalar(rvalues) and isna(rvalues): # numpy does not like comparisons vs None if op is operator.ne: res_values = np.ones(len(lvalues), dtype=bool) else: res_values = np.zeros(len(lvalues), dtype=bool) elif is_object_dtype(lvalues.dtype): res_values = comp_method_OBJECT_ARRAY(op, lvalues, rvalues) else: op_name = "__{op}__".format(op=op.__name__) method = getattr(lvalues, op_name) with np.errstate(all="ignore"): res_values = method(rvalues) if res_values is NotImplemented: res_values = invalid_comparison(lvalues, rvalues, op) if is_scalar(res_values): raise TypeError("Could not compare {typ} type with Series".format( typ=type(rvalues))) return res_values
def comparison_op(left: ArrayLike, right: Any, op) -> ArrayLike: """ Evaluate a comparison operation `=`, `!=`, `>=`, `>`, `<=`, or `<`. Note: the caller is responsible for ensuring that numpy warnings are suppressed (with np.errstate(all="ignore")) if needed. Parameters ---------- left : np.ndarray or ExtensionArray right : object Cannot be a DataFrame, Series, or Index. op : {operator.eq, operator.ne, operator.gt, operator.ge, operator.lt, operator.le} Returns ------- ndarray or ExtensionArray """ # NB: We assume extract_array has already been called on left and right lvalues = ensure_wrapped_if_datetimelike(left) rvalues = ensure_wrapped_if_datetimelike(right) rvalues = lib.item_from_zerodim(rvalues) if isinstance(rvalues, list): # TODO: same for tuples? rvalues = np.asarray(rvalues) if isinstance(rvalues, (np.ndarray, ABCExtensionArray)): # TODO: make this treatment consistent across ops and classes. # We are not catching all listlikes here (e.g. frozenset, tuple) # The ambiguous case is object-dtype. See GH#27803 if len(lvalues) != len(rvalues): raise ValueError("Lengths must match to compare", lvalues.shape, rvalues.shape) if should_extension_dispatch(lvalues, rvalues) or ( (isinstance(rvalues, (Timedelta, BaseOffset, Timestamp)) or right is NaT) and not is_object_dtype(lvalues.dtype)): # Call the method on lvalues res_values = op(lvalues, rvalues) elif is_scalar(rvalues) and isna(rvalues): # numpy does not like comparisons vs None if op is operator.ne: res_values = np.ones(lvalues.shape, dtype=bool) else: res_values = np.zeros(lvalues.shape, dtype=bool) elif is_numeric_v_string_like(lvalues, rvalues): # GH#36377 going through the numexpr path would incorrectly raise return invalid_comparison(lvalues, rvalues, op) elif is_object_dtype(lvalues.dtype) or isinstance(rvalues, str): res_values = comp_method_OBJECT_ARRAY(op, lvalues, rvalues) else: res_values = _na_arithmetic_op(lvalues, rvalues, op, is_cmp=True) return res_values
def na_op(x, y): # TODO: # should have guarantess on what x, y can be type-wise # Extension Dtypes are not called here # Checking that cases that were once handled here are no longer # reachable. assert not (is_categorical_dtype(y) and not is_scalar(y)) if is_object_dtype(x.dtype): result = _comp_method_OBJECT_ARRAY(op, x, y) elif is_datetimelike_v_numeric(x, y): return invalid_comparison(x, y, op) else: # we want to compare like types # we only want to convert to integer like if # we are not NotImplemented, otherwise # we would allow datetime64 (but viewed as i8) against # integer comparisons # we have a datetime/timedelta and may need to convert assert not needs_i8_conversion(x) mask = None if not is_scalar(y) and needs_i8_conversion(y): mask = isna(x) | isna(y) y = y.view("i8") x = x.view("i8") method = getattr(x, op_name, None) if method is not None: with np.errstate(all="ignore"): result = method(y) if result is NotImplemented: return invalid_comparison(x, y, op) else: result = op(x, y) if mask is not None and mask.any(): result[mask] = masker return result
def na_op(x, y): # TODO: # should have guarantees on what x, y can be type-wise # Extension Dtypes are not called here if is_object_dtype(x.dtype): result = comp_method_OBJECT_ARRAY(op, x, y) elif is_datetimelike_v_numeric(x, y): return invalid_comparison(x, y, op) else: method = getattr(x, op_name) with np.errstate(all="ignore"): result = method(y) if result is NotImplemented: return invalid_comparison(x, y, op) return result
def wrapper(self, other): ordinal_op = getattr(self.asi8, opname) if is_list_like(other) and len(other) != len(self): raise ValueError("Lengths must match") if isinstance(other, str): try: other = self._scalar_from_string(other) except ValueError: # string that can't be parsed as Period return invalid_comparison(self, other, op) elif isinstance(other, int): # TODO: sure we want to allow this? we dont for DTA/TDA # 2 tests rely on this other = Period(other, freq=self.freq) result = ordinal_op(other.ordinal) if isinstance(other, Period): self._check_compatible_with(other) result = ordinal_op(other.ordinal) elif isinstance(other, cls): self._check_compatible_with(other) result = ordinal_op(other.asi8) mask = self._isnan | other._isnan if mask.any(): result[mask] = nat_result return result elif other is NaT: result = np.empty(len(self.asi8), dtype=bool) result.fill(nat_result) else: return invalid_comparison(self, other, op) if self._hasnans: result[self._isnan] = nat_result return result
def wrapper(self, other): other = lib.item_from_zerodim(other) if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)): return NotImplemented if _is_convertible_to_td(other) or other is NaT: try: other = Timedelta(other) except ValueError: # failed to parse as timedelta return invalid_comparison(self, other, op) result = op(self.view("i8"), other.value) if isna(other): result.fill(nat_result) elif not is_list_like(other): return invalid_comparison(self, other, op) elif len(other) != len(self): raise ValueError("Lengths must match") else: try: other = type(self)._from_sequence(other)._data except (ValueError, TypeError): return invalid_comparison(self, other, op) result = op(self.view("i8"), other.view("i8")) result = com.values_from_object(result) o_mask = np.array(isna(other)) if o_mask.any(): result[o_mask] = nat_result if self._hasnans: result[self._isnan] = nat_result return result
def wrapper(self, other): if isinstance(other, str): try: other = self._scalar_from_string(other) except ValueError: # failed to parse as timedelta return invalid_comparison(self, other, op) if _is_convertible_to_td(other) or other is NaT: other = Timedelta(other) result = op(self.view("i8"), other.value) if isna(other): result.fill(nat_result) elif not is_list_like(other): return invalid_comparison(self, other, op) elif len(other) != len(self): raise ValueError("Lengths must match") else: try: other = type(self)._from_sequence(other)._data except (ValueError, TypeError): return invalid_comparison(self, other, op) result = op(self.view("i8"), other.view("i8")) result = com.values_from_object(result) o_mask = np.array(isna(other)) if o_mask.any(): result[o_mask] = nat_result if self._hasnans: result[self._isnan] = nat_result return result
def _na_arithmetic_op(left: np.ndarray, right, op, is_cmp: bool = False): """ Return the result of evaluating op on the passed in values. If native types are not compatible, try coercion to object dtype. Parameters ---------- left : np.ndarray right : np.ndarray or scalar Excludes DataFrame, Series, Index, ExtensionArray. is_cmp : bool, default False If this a comparison operation. Returns ------- array-like Raises ------ TypeError : invalid operation """ if isinstance(right, str): # can never use numexpr func = op else: func = partial(expressions.evaluate, op) try: result = func(left, right) except TypeError: if not is_cmp and (is_object_dtype(left.dtype) or is_object_dtype(right)): # For object dtype, fallback to a masked operation (only operating # on the non-missing values) # Don't do this for comparisons, as that will handle complex numbers # incorrectly, see GH#32047 result = _masked_arith_op(left, right, op) else: raise if is_cmp and (is_scalar(result) or result is NotImplemented): # numpy returned a scalar instead of operating element-wise # e.g. numeric array vs str # TODO: can remove this after dropping some future numpy version? return invalid_comparison(left, right, op) return missing.dispatch_fill_zeros(op, left, right, result)
def na_arithmetic_op(left, right, op, str_rep: Optional[str], is_cmp: bool = False): """ Return the result of evaluating op on the passed in values. If native types are not compatible, try coersion to object dtype. Parameters ---------- left : np.ndarray right : np.ndarray or scalar str_rep : str or None is_cmp : bool, default False If this a comparison operation. Returns ------- array-like Raises ------ TypeError : invalid operation """ import pandas.core.computation.expressions as expressions try: result = expressions.evaluate(op, str_rep, left, right) except TypeError: if is_cmp: # numexpr failed on comparison op, e.g. ndarray[float] > datetime # In this case we do not fall back to the masked op, as that # will handle complex numbers incorrectly, see GH#32047 raise result = masked_arith_op(left, right, op) if is_cmp and (is_scalar(result) or result is NotImplemented): # numpy returned a scalar instead of operating element-wise # e.g. numeric array vs str return invalid_comparison(left, right, op) return missing.dispatch_fill_zeros(op, left, right, result)
def _na_arithmetic_op(left, right, op, is_cmp: bool = False): """ Return the result of evaluating op on the passed in values. If native types are not compatible, try coercion to object dtype. Parameters ---------- left : np.ndarray right : np.ndarray or scalar is_cmp : bool, default False If this a comparison operation. Returns ------- array-like Raises ------ TypeError : invalid operation """ try: result = expressions.evaluate(op, left, right) except TypeError: if is_object_dtype(left) or is_object_dtype(right) and not is_cmp: # For object dtype, fallback to a masked operation (only operating # on the non-missing values) # Don't do this for comparisons, as that will handle complex numbers # incorrectly, see GH#32047 result = _masked_arith_op(left, right, op) else: raise if is_cmp and (is_scalar(result) or result is NotImplemented): # numpy returned a scalar instead of operating element-wise # e.g. numeric array vs str return invalid_comparison(left, right, op) return missing.dispatch_fill_zeros(op, left, right, result)
def wrapper(self, other): res_name = get_op_result_name(self, other) # TODO: shouldn't we be applying finalize whenever # not isinstance(other, ABCSeries)? finalizer = (lambda x: x.__finalize__(self) if isinstance(other, (np.ndarray, ABCIndexClass)) else x) if isinstance(other, ABCDataFrame): # pragma: no cover # Defer to DataFrame implementation; fail early return NotImplemented if isinstance(other, ABCSeries) and not self._indexed_same(other): raise ValueError( "Can only compare identically-labeled Series objects") other = lib.item_from_zerodim(other) if isinstance(other, list): # TODO: same for tuples? other = np.asarray(other) if isinstance(other, (np.ndarray, ABCExtensionArray, ABCIndexClass)): # TODO: make this treatment consistent across ops and classes. # We are not catching all listlikes here (e.g. frozenset, tuple) # The ambiguous case is object-dtype. See GH#27803 if len(self) != len(other): raise ValueError("Lengths must match to compare") lvalues = extract_array(self, extract_numpy=True) rvalues = extract_array(other, extract_numpy=True) if should_extension_dispatch(lvalues, rvalues): res_values = dispatch_to_extension_op(op, lvalues, rvalues) elif is_scalar(rvalues) and isna(rvalues): # numpy does not like comparisons vs None if op is operator.ne: res_values = np.ones(len(lvalues), dtype=bool) else: res_values = np.zeros(len(lvalues), dtype=bool) elif is_object_dtype(lvalues.dtype): res_values = comp_method_OBJECT_ARRAY(op, lvalues, rvalues) else: op_name = "__{op}__".format(op=op.__name__) method = getattr(lvalues, op_name) with np.errstate(all="ignore"): res_values = method(rvalues) if res_values is NotImplemented: res_values = invalid_comparison(lvalues, rvalues, op) if is_scalar(res_values): raise TypeError( "Could not compare {typ} type with Series".format( typ=type(rvalues))) result = self._constructor(res_values, index=self.index) result = finalizer(result) # Set the result's name after finalizer is called because finalizer # would set it back to self.name result.name = res_name return result
def wrapper(self, other): ordinal_op = getattr(self.asi8, opname) if is_list_like(other) and len(other) != len(self): raise ValueError("Lengths must match") if isinstance(other, str): try: other = self._scalar_from_string(other) except ValueError: # string that can't be parsed as Period return invalid_comparison(self, other, op) elif isinstance(other, int): # TODO: sure we want to allow this? we dont for DTA/TDA # 2 tests rely on this other = Period(other, freq=self.freq) result = ordinal_op(other.ordinal) if isinstance(other, Period): self._check_compatible_with(other) result = ordinal_op(other.ordinal) elif other is NaT: result = np.empty(len(self.asi8), dtype=bool) result.fill(nat_result) elif not is_list_like(other): return invalid_comparison(self, other, op) else: if isinstance(other, list): # TODO: could use pd.Index to do inference? other = np.array(other) if not isinstance(other, (np.ndarray, cls)): return invalid_comparison(self, other, op) if is_object_dtype(other): with np.errstate(all="ignore"): result = ops.comp_method_OBJECT_ARRAY( op, self.astype(object), other ) o_mask = isna(other) elif not is_period_dtype(other): # e.g. is_timedelta64_dtype(other) return invalid_comparison(self, other, op) else: assert isinstance(other, cls), type(other) self._check_compatible_with(other) result = ordinal_op(other.asi8) o_mask = other._isnan if o_mask.any(): result[o_mask] = nat_result if self._hasnans: result[self._isnan] = nat_result return result