core/maxframe/dataframe/arithmetic/__init__.py (288 lines of code) (raw):
# Copyright 1999-2025 Alibaba Group Holding Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import functools
import pandas as pd
try:
from pandas.core.arraylike import OpsMixin as PdOpsMixin
except ImportError: # pragma: no cover
PdOpsMixin = None
from .abs import DataFrameAbs, abs_
from .add import DataFrameAdd, add, radd
from .arccos import DataFrameArccos
from .arccosh import DataFrameArccosh
from .arcsin import DataFrameArcsin
from .arcsinh import DataFrameArcsinh
from .arctan import DataFrameArctan
from .arctanh import DataFrameArctanh
from .around import DataFrameAround, around
from .bitwise_and import DataFrameAnd, bitand, rbitand
from .bitwise_or import DataFrameOr, bitor, rbitor
from .bitwise_xor import DataFrameXor, bitxor, rbitxor
from .ceil import DataFrameCeil
from .cos import DataFrameCos
from .cosh import DataFrameCosh
from .degrees import DataFrameDegrees
from .equal import DataFrameEqual, eq
from .exp import DataFrameExp
from .exp2 import DataFrameExp2
from .expm1 import DataFrameExpm1
from .floor import DataFrameFloor
from .floordiv import DataFrameFloorDiv, floordiv, rfloordiv
from .greater import DataFrameGreater, gt
from .greater_equal import DataFrameGreaterEqual, ge
from .invert import DataFrameNot, invert
from .is_ufuncs import DataFrameIsFinite, DataFrameIsInf, DataFrameIsNan
from .less import DataFrameLess, lt
from .less_equal import DataFrameLessEqual, le
from .log import DataFrameLog
from .log2 import DataFrameLog2
from .log10 import DataFrameLog10
from .mod import DataFrameMod, mod, rmod
from .multiply import DataFrameMul, mul, rmul
from .negative import DataFrameNegative, negative
from .not_equal import DataFrameNotEqual, ne
from .power import DataFramePower, power, rpower
from .radians import DataFrameRadians
from .sin import DataFrameSin
from .sinh import DataFrameSinh
from .sqrt import DataFrameSqrt
from .subtract import DataFrameSubtract, rsubtract, subtract
from .tan import DataFrameTan
from .tanh import DataFrameTanh
from .truediv import DataFrameTrueDiv, rtruediv, truediv
from .trunc import DataFrameTrunc
def _wrap_eq():
@functools.wraps(eq)
def call(df, other, **kw):
from ..core import is_build_mode
if is_build_mode():
return df._equals(other)
return _wrap_comparison(eq)(df, other, **kw)
return call
def _wrap_comparison(func):
@functools.wraps(func)
def call(df, other, **kw):
from ..core import DATAFRAME_TYPE
from ..utils import wrap_notimplemented_exception
if isinstance(df, DATAFRAME_TYPE) and isinstance(other, DATAFRAME_TYPE):
# index and columns should be identical
for index_type in ["index_value", "columns_value"]:
left, right = getattr(df, index_type), getattr(other, index_type)
if left.has_value() and right.has_value():
# if df and other's index or columns has value
index_eq = left.to_pandas().equals(right.to_pandas())
else:
index_eq = left.key == right.key
if not index_eq:
raise ValueError(
"Can only compare identically-labeled DataFrame object"
)
return wrap_notimplemented_exception(func)(df, other, **kw)
return call
_reverse_magic_names = {
"eq": "eq",
"ne": "ne",
"lt": "ge",
"le": "gt",
"gt": "le",
"ge": "lt",
}
def _wrap_pandas_magics(cls, magic_name: str):
magic_func_name = f"__{magic_name}__"
magic_rfunc_name = _reverse_magic_names.get(magic_name, f"__r{magic_name}__")
try:
raw_method = getattr(cls, magic_func_name)
except AttributeError:
return
@functools.wraps(raw_method)
def wrapped(self, other):
from ..core import DATAFRAME_TYPE, INDEX_TYPE, SERIES_TYPE
if not isinstance(other, (DATAFRAME_TYPE, SERIES_TYPE, INDEX_TYPE)):
return raw_method(self, other)
try:
val = getattr(other, magic_rfunc_name)(self)
except AttributeError: # pragma: no cover
return raw_method(self, other)
if val is NotImplemented: # pragma: no cover
return raw_method(self, other)
return val
setattr(cls, magic_func_name, wrapped)
def _install():
from ..core import DATAFRAME_TYPE, INDEX_TYPE, SERIES_TYPE
from ..ufunc.tensor import register_tensor_ufunc
from ..utils import wrap_notimplemented_exception
def _register_method(cls, name, func, wrapper=None):
from ..core import DATAFRAME_TYPE, SERIES_TYPE
if wrapper is None:
@functools.wraps(func)
def wrapper(df, *args, **kwargs):
return func(df, *args, **kwargs)
try:
if issubclass(cls, DATAFRAME_TYPE):
wrapper.__doc__ = func.__frame_doc__
elif issubclass(cls, SERIES_TYPE):
wrapper.__doc__ = func.__series_doc__
else:
wrapper = func
except AttributeError:
wrapper = func
wrapper.__name__ = func.__name__
setattr(cls, name, wrapper)
def _register_bin_method(cls, name, func):
def call_df_fill(df, other, axis="columns", level=None, fill_value=None):
return func(df, other, axis=axis, level=level, fill_value=fill_value)
def call_df_no_fill(df, other, axis="columns", level=None):
return func(df, other, axis=axis, level=level)
def call_series_fill(df, other, level=None, fill_value=None, axis=0):
return func(df, other, axis=axis, level=level, fill_value=fill_value)
def call_series_no_fill(df, other, level=None, axis=0):
return func(df, other, axis=axis, level=level)
if issubclass(cls, DATAFRAME_TYPE):
call = (
call_df_fill
if "fill_value" in func.__code__.co_varnames
else call_df_no_fill
)
elif issubclass(cls, SERIES_TYPE):
call = (
call_series_fill
if "fill_value" in func.__code__.co_varnames
else call_series_no_fill
)
else:
call = None
return _register_method(cls, name, func, wrapper=call)
# register maxframe tensor ufuncs
ufunc_ops = [
# unary
DataFrameAbs,
DataFrameLog,
DataFrameLog2,
DataFrameLog10,
DataFrameSin,
DataFrameCos,
DataFrameTan,
DataFrameSinh,
DataFrameCosh,
DataFrameTanh,
DataFrameArcsin,
DataFrameArccos,
DataFrameArctan,
DataFrameArcsinh,
DataFrameArccosh,
DataFrameArctanh,
DataFrameRadians,
DataFrameDegrees,
DataFrameCeil,
DataFrameFloor,
DataFrameAround,
DataFrameExp,
DataFrameExp2,
DataFrameExpm1,
DataFrameSqrt,
DataFrameNot,
DataFrameIsNan,
DataFrameIsInf,
DataFrameIsFinite,
DataFrameNegative,
DataFrameTrunc,
# binary
DataFrameAdd,
DataFrameEqual,
DataFrameFloorDiv,
DataFrameGreater,
DataFrameGreaterEqual,
DataFrameLess,
DataFrameLessEqual,
DataFrameAnd,
DataFrameOr,
DataFrameXor,
DataFrameMod,
DataFrameMul,
DataFrameNotEqual,
DataFramePower,
DataFrameSubtract,
DataFrameTrueDiv,
]
for ufunc_op in ufunc_ops:
register_tensor_ufunc(ufunc_op)
for entity in DATAFRAME_TYPE + SERIES_TYPE:
setattr(entity, "__abs__", abs_)
setattr(entity, "abs", abs_)
_register_method(entity, "round", around)
setattr(entity, "__invert__", invert)
setattr(entity, "__add__", wrap_notimplemented_exception(add))
setattr(entity, "__radd__", wrap_notimplemented_exception(radd))
_register_bin_method(entity, "add", add)
_register_bin_method(entity, "radd", radd)
setattr(entity, "__sub__", wrap_notimplemented_exception(subtract))
setattr(entity, "__rsub__", wrap_notimplemented_exception(rsubtract))
_register_bin_method(entity, "sub", subtract)
_register_bin_method(entity, "rsub", rsubtract)
setattr(entity, "__mul__", wrap_notimplemented_exception(mul))
setattr(entity, "__rmul__", wrap_notimplemented_exception(rmul))
_register_bin_method(entity, "mul", mul)
_register_bin_method(entity, "multiply", mul)
_register_bin_method(entity, "rmul", rmul)
setattr(entity, "__floordiv__", wrap_notimplemented_exception(floordiv))
setattr(entity, "__rfloordiv__", wrap_notimplemented_exception(rfloordiv))
setattr(entity, "__truediv__", wrap_notimplemented_exception(truediv))
setattr(entity, "__rtruediv__", wrap_notimplemented_exception(rtruediv))
setattr(entity, "__div__", wrap_notimplemented_exception(truediv))
setattr(entity, "__rdiv__", wrap_notimplemented_exception(rtruediv))
_register_bin_method(entity, "floordiv", floordiv)
_register_bin_method(entity, "rfloordiv", rfloordiv)
_register_bin_method(entity, "truediv", truediv)
_register_bin_method(entity, "rtruediv", rtruediv)
_register_bin_method(entity, "div", truediv)
_register_bin_method(entity, "rdiv", rtruediv)
setattr(entity, "__mod__", wrap_notimplemented_exception(mod))
setattr(entity, "__rmod__", wrap_notimplemented_exception(rmod))
_register_bin_method(entity, "mod", mod)
_register_bin_method(entity, "rmod", rmod)
setattr(entity, "__pow__", wrap_notimplemented_exception(power))
setattr(entity, "__rpow__", wrap_notimplemented_exception(rpower))
_register_bin_method(entity, "pow", power)
_register_bin_method(entity, "rpow", rpower)
setattr(entity, "__eq__", _wrap_eq())
setattr(entity, "__ne__", _wrap_comparison(ne))
setattr(entity, "__lt__", _wrap_comparison(lt))
setattr(entity, "__gt__", _wrap_comparison(gt))
setattr(entity, "__ge__", _wrap_comparison(ge))
setattr(entity, "__le__", _wrap_comparison(le))
_register_bin_method(entity, "eq", eq)
_register_bin_method(entity, "ne", ne)
_register_bin_method(entity, "lt", lt)
_register_bin_method(entity, "gt", gt)
_register_bin_method(entity, "ge", ge)
_register_bin_method(entity, "le", le)
setattr(entity, "__and__", wrap_notimplemented_exception(bitand))
setattr(entity, "__rand__", wrap_notimplemented_exception(rbitand))
setattr(entity, "__or__", wrap_notimplemented_exception(bitor))
setattr(entity, "__ror__", wrap_notimplemented_exception(rbitor))
setattr(entity, "__xor__", wrap_notimplemented_exception(bitxor))
setattr(entity, "__rxor__", wrap_notimplemented_exception(rbitxor))
setattr(entity, "__neg__", wrap_notimplemented_exception(negative))
for entity in INDEX_TYPE:
setattr(entity, "__eq__", _wrap_eq())
if PdOpsMixin is not None and not hasattr(
pd, "_maxframe_df_arith_wrapped"
): # pragma: no branch
# wrap pandas magic functions to intercept reverse operators
for magic_name in [
"add",
"sub",
"mul",
"div",
"truediv",
"floordiv",
"mod",
"pow",
"and",
"or",
"xor",
"eq",
"ne",
"lt",
"le",
"gt",
"ge",
]:
_wrap_pandas_magics(PdOpsMixin, magic_name)
for pd_cls in (pd.DataFrame, pd.Series):
_wrap_pandas_magics(pd_cls, "matmul")
pd._maxframe_df_arith_wrapped = True
_install()
del _install