diff --git a/python/cudf/cudf/core/frame.py b/python/cudf/cudf/core/frame.py index 4a434be42ce..2e6a3c54885 100644 --- a/python/cudf/cudf/core/frame.py +++ b/python/cudf/cudf/core/frame.py @@ -18,7 +18,12 @@ import cudf from cudf import _lib as libcudf from cudf._typing import ColumnLike, DataFrameOrSeries -from cudf.core.column import as_column, build_categorical_column, column_empty +from cudf.core.column import ( + ColumnBase, + as_column, + build_categorical_column, + column_empty, +) from cudf.core.join import merge from cudf.utils.dtypes import ( is_categorical_dtype, @@ -3301,6 +3306,19 @@ def _reindex( return self._mimic_inplace(result, inplace=inplace) +_truediv_int_dtype_corrections = { + np.int8: np.float32, + np.int16: np.float32, + np.int32: np.float32, + np.int64: np.float64, + np.uint8: np.float32, + np.uint16: np.float32, + np.uint32: np.float64, + np.uint64: np.float64, + np.bool_: np.float32, +} + + class SingleColumnFrame(Frame): """A one-dimensional frame. @@ -3505,6 +3523,221 @@ def to_arrow(self): """ return self._column.to_arrow() + @property + def _copy_construct_defaults(self): + """A default dictionary of kwargs to be used for copy construction.""" + raise NotImplementedError + + def _copy_construct(self, **kwargs): + """Shallow copy this object by replacing certain ctor args. + """ + return self.__class__(**{**self._copy_construct_defaults, **kwargs}) + + def _binaryop( + self, + other, + fn, + fill_value=None, + reflect=False, + lhs=None, + *args, + **kwargs, + ): + """Perform a binary operation between two single column frames. + + Parameters + ---------- + other : SingleColumnFrame + The second operand. + fn : str + The operation + fill_value : Any, default None + The value to replace null values with. If ``None``, nulls are not + filled before the operation. + reflect : bool, default False + If ``True`` the operation is reflected (i.e whether to swap the + left and right operands). + lhs : SingleColumnFrame, default None + The left hand operand. If ``None``, self is used. This parameter + allows child classes to preprocess the inputs if necessary. + + Returns + ------- + SingleColumnFrame + A new instance containing the result of the operation. + """ + if lhs is None: + lhs = self + + rhs = self._normalize_binop_value(other) + + if fn == "truediv": + truediv_type = _truediv_int_dtype_corrections.get(lhs.dtype.type) + if truediv_type is not None: + lhs = lhs.astype(truediv_type) + + output_mask = None + if fill_value is not None: + if is_scalar(rhs): + if lhs.nullable: + lhs = lhs.fillna(fill_value) + else: + # If both columns are nullable, pandas semantics dictate that + # nulls that are present in both lhs and rhs are not filled. + if lhs.nullable and rhs.nullable: + # Note: lhs is a Frame, while rhs is already a column. + lmask = as_column(lhs._column.nullmask) + rmask = as_column(rhs.nullmask) + output_mask = (lmask | rmask).data + lhs = lhs.fillna(fill_value) + rhs = rhs.fillna(fill_value) + elif lhs.nullable: + lhs = lhs.fillna(fill_value) + elif rhs.nullable: + rhs = rhs.fillna(fill_value) + + outcol = lhs._column.binary_operator(fn, rhs, reflect=reflect) + + # Get the appropriate name for output operations involving two objects + # that are Series-like objects. The output shares the lhs's name unless + # the rhs is a _differently_ named Series-like object. + if ( + isinstance(other, (SingleColumnFrame, pd.Series, pd.Index)) + and self.name != other.name + ): + result_name = None + else: + result_name = self.name + + output = lhs._copy_construct(data=outcol, name=result_name) + + if output_mask is not None: + output._column = output._column.set_mask(output_mask) + return output + + def _normalize_binop_value(self, other): + """Returns a *column* (not a Series) or scalar for performing + binary operations with self._column. + """ + if isinstance(other, ColumnBase): + return other + if isinstance(other, SingleColumnFrame): + return other._column + if other is cudf.NA: + return cudf.Scalar(other, dtype=self.dtype) + else: + return self._column.normalize_binop_value(other) + + def _bitwise_binop(self, other, op): + """Type-coercing wrapper around _binaryop for bitwise operations.""" + # This will catch attempts at bitwise ops on extension dtypes. + try: + self_is_bool = np.issubdtype(self.dtype, np.bool_) + other_is_bool = np.issubdtype(other.dtype, np.bool_) + except TypeError: + raise TypeError( + f"Operation 'bitwise {op}' not supported between " + f"{self.dtype.type.__name__} and {other.dtype.type.__name__}" + ) + + if (self_is_bool or np.issubdtype(self.dtype, np.integer)) and ( + other_is_bool or np.issubdtype(other.dtype, np.integer) + ): + # TODO: This doesn't work on Series (op) DataFrame + # because dataframe doesn't have dtype + ser = self._binaryop(other, op) + if self_is_bool or other_is_bool: + ser = ser.astype(np.bool_) + return ser + else: + raise TypeError( + f"Operation 'bitwise {op}' not supported between " + f"{self.dtype.type.__name__} and {other.dtype.type.__name__}" + ) + + # Binary arithmetic operations. + def __add__(self, other): + return self._binaryop(other, "add") + + def __radd__(self, other): + return self._binaryop(other, "add", reflect=True) + + def __sub__(self, other): + return self._binaryop(other, "sub") + + def __rsub__(self, other): + return self._binaryop(other, "sub", reflect=True) + + def __mul__(self, other): + return self._binaryop(other, "mul") + + def __rmul__(self, other): + return self._binaryop(other, "mul", reflect=True) + + def __mod__(self, other): + return self._binaryop(other, "mod") + + def __rmod__(self, other): + return self._binaryop(other, "mod", reflect=True) + + def __pow__(self, other): + return self._binaryop(other, "pow") + + def __rpow__(self, other): + return self._binaryop(other, "pow", reflect=True) + + def __floordiv__(self, other): + return self._binaryop(other, "floordiv") + + def __rfloordiv__(self, other): + return self._binaryop(other, "floordiv", reflect=True) + + def __truediv__(self, other): + return self._binaryop(other, "truediv") + + def __rtruediv__(self, other): + return self._binaryop(other, "truediv", reflect=True) + + __div__ = __truediv__ + + def __and__(self, other): + return self._bitwise_binop(other, "and") + + def __or__(self, other): + return self._bitwise_binop(other, "or") + + def __xor__(self, other): + return self._bitwise_binop(other, "xor") + + # Binary rich comparison operations. + def __eq__(self, other): + return self._binaryop(other, "eq") + + def __ne__(self, other): + return self._binaryop(other, "ne") + + def __lt__(self, other): + return self._binaryop(other, "lt") + + def __le__(self, other): + return self._binaryop(other, "le") + + def __gt__(self, other): + return self._binaryop(other, "gt") + + def __ge__(self, other): + return self._binaryop(other, "ge") + + # Unary logical operators + def __neg__(self): + return -1 * self + + def __pos__(self): + return self.copy(deep=True) + + def __abs__(self): + return self._unaryop("abs") + def _get_replacement_values_for_columns( to_replace: Any, value: Any, columns_dtype_map: Dict[Any, Any] diff --git a/python/cudf/cudf/core/index.py b/python/cudf/cudf/core/index.py index 5f390be79e2..2846dc241db 100644 --- a/python/cudf/cudf/core/index.py +++ b/python/cudf/cudf/core/index.py @@ -785,14 +785,63 @@ def difference(self, other, sort=None): return difference - def _apply_op(self, fn, other=None): + def _binaryop(self, other, fn, fill_value=None, reflect=False): + # TODO: Rather than including an allowlist of acceptable types, we + # should instead return NotImplemented for __all__ other types. That + # will allow other types to support binops with cudf objects if they so + # choose, and just as importantly will allow better error messages if + # they don't support it. + if isinstance(other, (cudf.DataFrame, cudf.Series)): + return NotImplemented - idx_series = cudf.Series(self, name=self.name) - op = getattr(idx_series, fn) - if other is not None: - return as_index(op(other)) - else: - return as_index(op()) + return super()._binaryop(other, fn, fill_value, reflect) + + def _copy_construct(self, **kwargs): + # Need to override the parent behavior because pandas allows operations + # on unsigned types to return signed values, forcing us to choose the + # right index type here. + data = kwargs.get("data") + cls = self.__class__ + + if data is not None: + if self.dtype != data.dtype: + # TODO: This logic is largely copied from `as_index`. The two + # should be unified via a centralized type dispatching scheme. + if isinstance(data, NumericalColumn): + try: + cls = _dtype_to_index[data.dtype.type] + except KeyError: + cls = GenericIndex + # TODO: GenericIndex has a different API for __new__ + # than other Index types. Refactoring Index types will + # be necessary to clean this up. + kwargs["values"] = kwargs.pop("data") + elif isinstance(data, StringColumn): + cls = StringIndex + elif isinstance(data, DatetimeColumn): + cls = DatetimeIndex + elif isinstance(data, TimeDeltaColumn): + cls = TimedeltaIndex + elif isinstance(data, CategoricalColumn): + cls = CategoricalIndex + elif cls is RangeIndex: + # RangeIndex must convert to other numerical types for ops + + # TODO: The one exception to the output type selected here is + # that scalar multiplication of a RangeIndex in pandas results + # in another RangeIndex. Propagating that information through + # cudf with the current internals is possible, but requires + # significant hackery since we'd need _copy_construct or some + # other constructor to be intrinsically capable of processing + # operations. We should fix this behavior once we've completed + # a more thorough refactoring of the various Index classes that + # makes it easier to propagate this logic. + try: + cls = _dtype_to_index[data.dtype.type] + except KeyError: + cls = GenericIndex + + return cls(**{**self._copy_construct_defaults, **kwargs}) def sort_values(self, return_indexer=False, ascending=True, key=None): """ @@ -889,74 +938,6 @@ def unique(self): """ return as_index(self._values.unique(), name=self.name) - def __add__(self, other): - return self._apply_op("__add__", other) - - def __radd__(self, other): - return self._apply_op("__radd__", other) - - def __sub__(self, other): - return self._apply_op("__sub__", other) - - def __rsub__(self, other): - return self._apply_op("__rsub__", other) - - def __mul__(self, other): - return self._apply_op("__mul__", other) - - def __rmul__(self, other): - return self._apply_op("__rmul__", other) - - def __mod__(self, other): - return self._apply_op("__mod__", other) - - def __rmod__(self, other): - return self._apply_op("__rmod__", other) - - def __pow__(self, other): - return self._apply_op("__pow__", other) - - def __floordiv__(self, other): - return self._apply_op("__floordiv__", other) - - def __rfloordiv__(self, other): - return self._apply_op("__rfloordiv__", other) - - def __truediv__(self, other): - return self._apply_op("__truediv__", other) - - def __rtruediv__(self, other): - return self._apply_op("__rtruediv__", other) - - __div__ = __truediv__ - - def __and__(self, other): - return self._apply_op("__and__", other) - - def __or__(self, other): - return self._apply_op("__or__", other) - - def __xor__(self, other): - return self._apply_op("__xor__", other) - - def __eq__(self, other): - return self._apply_op("__eq__", other) - - def __ne__(self, other): - return self._apply_op("__ne__", other) - - def __lt__(self, other): - return self._apply_op("__lt__", other) - - def __le__(self, other): - return self._apply_op("__le__", other) - - def __gt__(self, other): - return self._apply_op("__gt__", other) - - def __ge__(self, other): - return self._apply_op("__ge__", other) - def join( self, other, how="left", level=None, return_indexers=False, sort=False ): @@ -1425,6 +1406,10 @@ def _from_table(cls, table): else: return as_index(table) + @property + def _copy_construct_defaults(self): + return {"data": self._column, "name": self.name} + @classmethod def _from_data(cls, data, index=None): return cls._from_table(SingleColumnFrame(data=data)) @@ -1626,9 +1611,6 @@ def __getitem__(self, index): return as_index(self._values[index], name=self.name) - def __eq__(self, other): - return super(type(self), self).__eq__(other) - def equals(self, other): if isinstance(other, RangeIndex): if (self._start, self._stop, self._step) == ( @@ -2959,7 +2941,7 @@ def as_index(arbitrary, **kwargs) -> Index: ) -_dtype_to_index = { +_dtype_to_index: Dict[Any, Type[Index]] = { np.int8: Int8Index, np.int16: Int16Index, np.int32: Int32Index, @@ -2970,7 +2952,7 @@ def as_index(arbitrary, **kwargs) -> Index: np.uint64: UInt64Index, np.float32: Float32Index, np.float64: Float64Index, -} # type: Dict[Any, Type[Index]] +} _index_to_dtype = { Int8Index: np.int8, diff --git a/python/cudf/cudf/core/series.py b/python/cudf/cudf/core/series.py index 5ee40d576b6..e0b84eed0d4 100644 --- a/python/cudf/cudf/core/series.py +++ b/python/cudf/cudf/core/series.py @@ -13,7 +13,6 @@ import cupy import numpy as np import pandas as pd -from nvtx import annotate from pandas._config import get_option from pandas.api.types import is_dict_like @@ -390,16 +389,9 @@ def deserialize(cls, header, frames): return Series(column, index=index, name=name) + @property def _copy_construct_defaults(self): - return dict(data=self._column, index=self._index, name=self.name) - - def _copy_construct(self, **kwargs): - """Shallow copy this object by replacing certain ctor args. - """ - params = self._copy_construct_defaults() - cls = type(self) - params.update(kwargs) - return cls(**params) + return {"data": self._column, "index": self._index, "name": self.name} def _get_columns_by_label(self, labels, downcast=False): """Return the column specified by `labels` @@ -1328,71 +1320,26 @@ def __repr__(self): lines.append(category_memory) return "\n".join(lines) - @annotate("BINARY_OP", color="orange", domain="cudf_python") def _binaryop( self, other, fn, fill_value=None, reflect=False, can_reindex=False ): - """ - Internal util to call a binary operator *fn* on operands *self* - and *other*. Return the output Series. The output dtype is - determined by the input operands. - - If ``reflect`` is ``True``, swap the order of the operands. - """ if isinstance(other, cudf.DataFrame): return NotImplemented if isinstance(other, Series): - if not can_reindex and fn in cudf.utils.utils._EQUALITY_OPS: - if not self.index.equals(other.index): - raise ValueError( - "Can only compare identically-labeled " - "Series objects" - ) - lhs, rhs = _align_indices([self, other], allow_non_unique=True) - else: - lhs, rhs = self, other - rhs = self._normalize_binop_value(rhs) - - if fn == "truediv": - if str(lhs.dtype) in truediv_int_dtype_corrections: - truediv_type = truediv_int_dtype_corrections[str(lhs.dtype)] - lhs = lhs.astype(truediv_type) - - if fill_value is not None: - if is_scalar(rhs): - lhs = lhs.fillna(fill_value) - else: - if lhs.nullable and rhs.nullable: - lmask = Series(data=lhs.nullmask) - rmask = Series(data=rhs.nullmask) - mask = (lmask | rmask).data - lhs = lhs.fillna(fill_value) - rhs = rhs.fillna(fill_value) - result = lhs._binaryop(rhs, fn=fn, reflect=reflect) - data = column.build_column( - data=result.data, dtype=result.dtype, mask=mask - ) - return lhs._copy_construct(data=data) - elif lhs.nullable: - lhs = lhs.fillna(fill_value) - elif rhs.nullable: - rhs = rhs.fillna(fill_value) - - outcol = lhs._column.binary_operator(fn, rhs, reflect=reflect) - - # Get the appropriate name for output operations involving two objects - # that are a mix of pandas and cudf Series and Index. If the two inputs - # are identically named, the output shares this name. - if isinstance(other, (cudf.Series, cudf.Index, pd.Series, pd.Index)): - if self.name == other.name: - result_name = self.name - else: - result_name = None + if ( + not can_reindex + and fn in cudf.utils.utils._EQUALITY_OPS + and not self.index.equals(other.index) + ): + raise ValueError( + "Can only compare identically-labeled " "Series objects" + ) + lhs, other = _align_indices([self, other], allow_non_unique=True) else: - result_name = self.name + lhs = self - return lhs._copy_construct(data=outcol, name=result_name) + return super()._binaryop(other, fn, fill_value, reflect, lhs) def add(self, other, fill_value=None, axis=0): """ @@ -1447,9 +1394,6 @@ def add(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "add", fill_value) - def __add__(self, other): - return self._binaryop(other, "add") - def radd(self, other, fill_value=None, axis=0): """Addition of series and other, element-wise (binary operator radd). @@ -1497,9 +1441,6 @@ def radd(self, other, fill_value=None, axis=0): other, "add", fill_value=fill_value, reflect=True ) - def __radd__(self, other): - return self._binaryop(other, "add", reflect=True) - def subtract(self, other, fill_value=None, axis=0): """Subtraction of series and other, element-wise (binary operator sub). @@ -1548,9 +1489,6 @@ def subtract(self, other, fill_value=None, axis=0): sub = subtract - def __sub__(self, other): - return self._binaryop(other, "sub") - def rsub(self, other, fill_value=None, axis=0): """Subtraction of series and other, element-wise (binary operator rsub). @@ -1596,9 +1534,6 @@ def rsub(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "sub", fill_value, reflect=True) - def __rsub__(self, other): - return self._binaryop(other, "sub", reflect=True) - def multiply(self, other, fill_value=None, axis=0): """Multiplication of series and other, element-wise (binary operator mul). @@ -1646,9 +1581,6 @@ def multiply(self, other, fill_value=None, axis=0): mul = multiply - def __mul__(self, other): - return self._binaryop(other, "mul") - def rmul(self, other, fill_value=None, axis=0): """Multiplication of series and other, element-wise (binary operator rmul). @@ -1697,9 +1629,6 @@ def rmul(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "mul", fill_value, True) - def __rmul__(self, other): - return self._binaryop(other, "mul", reflect=True) - def mod(self, other, fill_value=None, axis=0): """Modulo of series and other, element-wise (binary operator mod). @@ -1735,9 +1664,6 @@ def mod(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "mod", fill_value) - def __mod__(self, other): - return self._binaryop(other, "mod") - def rmod(self, other, fill_value=None, axis=0): """Modulo of series and other, element-wise (binary operator rmod). @@ -1786,9 +1712,6 @@ def rmod(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "mod", fill_value, True) - def __rmod__(self, other): - return self._binaryop(other, "mod", reflect=True) - def pow(self, other, fill_value=None, axis=0): """Exponential power of series and other, element-wise (binary operator pow). @@ -1834,9 +1757,6 @@ def pow(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "pow", fill_value) - def __pow__(self, other): - return self._binaryop(other, "pow") - def rpow(self, other, fill_value=None, axis=0): """Exponential power of series and other, element-wise (binary operator rpow). @@ -1882,9 +1802,6 @@ def rpow(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "pow", fill_value, True) - def __rpow__(self, other): - return self._binaryop(other, "pow", reflect=True) - def floordiv(self, other, fill_value=None, axis=0): """Integer division of series and other, element-wise (binary operator floordiv). @@ -1930,9 +1847,6 @@ def floordiv(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "floordiv", fill_value) - def __floordiv__(self, other): - return self._binaryop(other, "floordiv") - def rfloordiv(self, other, fill_value=None, axis=0): """Integer division of series and other, element-wise (binary operator rfloordiv). @@ -1986,9 +1900,6 @@ def rfloordiv(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "floordiv", fill_value, True) - def __rfloordiv__(self, other): - return self._binaryop(other, "floordiv", reflect=True) - def truediv(self, other, fill_value=None, axis=0): """Floating division of series and other, element-wise (binary operator truediv). @@ -2034,9 +1945,6 @@ def truediv(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "truediv", fill_value) - def __truediv__(self, other): - return self._binaryop(other, "truediv") - def rtruediv(self, other, fill_value=None, axis=0): """Floating division of series and other, element-wise (binary operator rtruediv). @@ -2082,81 +1990,18 @@ def rtruediv(self, other, fill_value=None, axis=0): raise NotImplementedError("Only axis=0 supported at this time.") return self._binaryop(other, "truediv", fill_value, True) - def __rtruediv__(self, other): - return self._binaryop(other, "truediv", reflect=True) - - __div__ = __truediv__ - - def _bitwise_binop(self, other, op): - if ( - np.issubdtype(self.dtype, np.bool_) - or np.issubdtype(self.dtype, np.integer) - ) and ( - np.issubdtype(other.dtype, np.bool_) - or np.issubdtype(other.dtype, np.integer) - ): - # TODO: This doesn't work on Series (op) DataFrame - # because dataframe doesn't have dtype - ser = self._binaryop(other, op) - if np.issubdtype(self.dtype, np.bool_) or np.issubdtype( - other.dtype, np.bool_ - ): - ser = ser.astype(np.bool_) - return ser - else: - raise TypeError( - f"Operation 'bitwise {op}' not supported between " - f"{self.dtype.type.__name__} and {other.dtype.type.__name__}" - ) - - def __and__(self, other): - """Performs vectorized bitwise and (&) on corresponding elements of two - series. - """ - return self._bitwise_binop(other, "and") - - def __or__(self, other): - """Performs vectorized bitwise or (|) on corresponding elements of two - series. - """ - return self._bitwise_binop(other, "or") - - def __xor__(self, other): - """Performs vectorized bitwise xor (^) on corresponding elements of two - series. - """ - return self._bitwise_binop(other, "xor") - def logical_and(self, other): - ser = self._binaryop(other, "l_and") - return ser.astype(np.bool_) + return self._binaryop(other, "l_and").astype(np.bool_) def remainder(self, other): - ser = self._binaryop(other, "mod") - return ser + return self._binaryop(other, "mod") def logical_or(self, other): - ser = self._binaryop(other, "l_or") - return ser.astype(np.bool_) + return self._binaryop(other, "l_or").astype(np.bool_) def logical_not(self): return self._unaryop("not") - def _normalize_binop_value(self, other): - """Returns a *column* (not a Series) or scalar for performing - binary operations with self._column. - """ - if isinstance(other, ColumnBase): - return other - if isinstance(other, Series): - return other._column - elif isinstance(other, Index): - return Series(other)._column - elif other is cudf.NA: - return cudf.Scalar(other, dtype=self.dtype) - else: - return self._column.normalize_binop_value(other) - def eq(self, other, fill_value=None, axis=0): """Equal to of series and other, element-wise (binary operator eq). @@ -2209,9 +2054,6 @@ def eq(self, other, fill_value=None, axis=0): other=other, fn="eq", fill_value=fill_value, can_reindex=True ) - def __eq__(self, other): - return self._binaryop(other, "eq") - def ne(self, other, fill_value=None, axis=0): """Not equal to of series and other, element-wise (binary operator ne). @@ -2264,9 +2106,6 @@ def ne(self, other, fill_value=None, axis=0): other=other, fn="ne", fill_value=fill_value, can_reindex=True ) - def __ne__(self, other): - return self._binaryop(other, "ne") - def lt(self, other, fill_value=None, axis=0): """Less than of series and other, element-wise (binary operator lt). @@ -2319,9 +2158,6 @@ def lt(self, other, fill_value=None, axis=0): other=other, fn="lt", fill_value=fill_value, can_reindex=True ) - def __lt__(self, other): - return self._binaryop(other, "lt") - def le(self, other, fill_value=None, axis=0): """Less than or equal to of series and other, element-wise (binary operator le). @@ -2374,9 +2210,6 @@ def le(self, other, fill_value=None, axis=0): other=other, fn="le", fill_value=fill_value, can_reindex=True ) - def __le__(self, other): - return self._binaryop(other, "le") - def gt(self, other, fill_value=None, axis=0): """Greater than of series and other, element-wise (binary operator gt). @@ -2429,9 +2262,6 @@ def gt(self, other, fill_value=None, axis=0): other=other, fn="gt", fill_value=fill_value, can_reindex=True ) - def __gt__(self, other): - return self._binaryop(other, "gt") - def ge(self, other, fill_value=None, axis=0): """Greater than or equal to of series and other, element-wise (binary operator ge). @@ -2484,15 +2314,8 @@ def ge(self, other, fill_value=None, axis=0): other=other, fn="ge", fill_value=fill_value, can_reindex=True ) - def __ge__(self, other): - return self._binaryop(other, "ge") - def __invert__(self): - """Bitwise invert (~) for each element. - Logical NOT if dtype is bool - - Returns a new Series. - """ + """Bitwise invert (~) for integral dtypes, logical NOT for bools.""" if np.issubdtype(self.dtype, np.integer): return self._unaryop("invert") elif np.issubdtype(self.dtype, np.bool_): @@ -2502,13 +2325,6 @@ def __invert__(self): f"Operation `~` not supported on {self.dtype.type.__name__}" ) - def __neg__(self): - """Negated value (-) for each element - - Returns a new Series. - """ - return self.__mul__(-1) - @copy_docstring(CategoricalAccessor.__init__) # type: ignore @property def cat(self): @@ -5529,9 +5345,6 @@ def abs(self): """ return self._unaryop("abs") - def __abs__(self): - return self.abs() - # Rounding def ceil(self): """ @@ -6332,20 +6145,6 @@ def explode(self, ignore_index=False): return super()._explode(self._column_names[0], ignore_index) -truediv_int_dtype_corrections = { - "int8": "float32", - "int16": "float32", - "int32": "float32", - "int64": "float64", - "uint8": "float32", - "uint16": "float32", - "uint32": "float64", - "uint64": "float64", - "bool": "float32", - "int": "float", -} - - class DatetimeProperties(object): """ Accessor object for datetimelike properties of the Series values.