forked from pool/python-pandas
175 lines
5.9 KiB
Diff
175 lines
5.9 KiB
Diff
|
diff --git a/pandas/_testing/__init__.py b/pandas/_testing/__init__.py
|
||
|
index 361998db8e..87d419e2db 100644
|
||
|
--- a/pandas/_testing/__init__.py
|
||
|
+++ b/pandas/_testing/__init__.py
|
||
|
@@ -111,6 +111,7 @@ ALL_FLOAT_DTYPES: list[Dtype] = [*FLOAT_NUMPY_DTYPES, *FLOAT_EA_DTYPES]
|
||
|
|
||
|
COMPLEX_DTYPES: list[Dtype] = [complex, "complex64", "complex128"]
|
||
|
STRING_DTYPES: list[Dtype] = [str, "str", "U"]
|
||
|
+COMPLEX_FLOAT_DTYPES: list[Dtype] = [*COMPLEX_DTYPES, *FLOAT_NUMPY_DTYPES]
|
||
|
|
||
|
DATETIME64_DTYPES: list[Dtype] = ["datetime64[ns]", "M8[ns]"]
|
||
|
TIMEDELTA64_DTYPES: list[Dtype] = ["timedelta64[ns]", "m8[ns]"]
|
||
|
diff --git a/pandas/conftest.py b/pandas/conftest.py
|
||
|
index 7c35dfdde9..10134c90f8 100644
|
||
|
--- a/pandas/conftest.py
|
||
|
+++ b/pandas/conftest.py
|
||
|
@@ -1403,6 +1403,21 @@ def complex_dtype(request):
|
||
|
return request.param
|
||
|
|
||
|
|
||
|
+@pytest.fixture(params=tm.COMPLEX_FLOAT_DTYPES)
|
||
|
+def complex_or_float_dtype(request):
|
||
|
+ """
|
||
|
+ Parameterized fixture for complex and numpy float dtypes.
|
||
|
+
|
||
|
+ * complex
|
||
|
+ * 'complex64'
|
||
|
+ * 'complex128'
|
||
|
+ * float
|
||
|
+ * 'float32'
|
||
|
+ * 'float64'
|
||
|
+ """
|
||
|
+ return request.param
|
||
|
+
|
||
|
+
|
||
|
@pytest.fixture(params=tm.SIGNED_INT_NUMPY_DTYPES)
|
||
|
def any_signed_int_numpy_dtype(request):
|
||
|
"""
|
||
|
diff --git a/pandas/core/computation/expr.py b/pandas/core/computation/expr.py
|
||
|
index b5861fbaeb..d642c37cea 100644
|
||
|
--- a/pandas/core/computation/expr.py
|
||
|
+++ b/pandas/core/computation/expr.py
|
||
|
@@ -31,7 +31,6 @@ from pandas.core.computation.ops import (
|
||
|
UNARY_OPS_SYMS,
|
||
|
BinOp,
|
||
|
Constant,
|
||
|
- Div,
|
||
|
FuncNode,
|
||
|
Op,
|
||
|
Term,
|
||
|
@@ -370,7 +369,7 @@ class BaseExprVisitor(ast.NodeVisitor):
|
||
|
"Add",
|
||
|
"Sub",
|
||
|
"Mult",
|
||
|
- None,
|
||
|
+ "Div",
|
||
|
"Pow",
|
||
|
"FloorDiv",
|
||
|
"Mod",
|
||
|
@@ -533,9 +532,6 @@ class BaseExprVisitor(ast.NodeVisitor):
|
||
|
left, right = self._maybe_downcast_constants(left, right)
|
||
|
return self._maybe_evaluate_binop(op, op_class, left, right)
|
||
|
|
||
|
- def visit_Div(self, node, **kwargs):
|
||
|
- return lambda lhs, rhs: Div(lhs, rhs)
|
||
|
-
|
||
|
def visit_UnaryOp(self, node, **kwargs):
|
||
|
op = self.visit(node.op)
|
||
|
operand = self.visit(node.operand)
|
||
|
diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py
|
||
|
index 95ac20ba39..ea8b1c0457 100644
|
||
|
--- a/pandas/core/computation/ops.py
|
||
|
+++ b/pandas/core/computation/ops.py
|
||
|
@@ -332,31 +332,6 @@ for d in (_cmp_ops_dict, _bool_ops_dict, _arith_ops_dict):
|
||
|
_binary_ops_dict.update(d)
|
||
|
|
||
|
|
||
|
-def _cast_inplace(terms, acceptable_dtypes, dtype) -> None:
|
||
|
- """
|
||
|
- Cast an expression inplace.
|
||
|
-
|
||
|
- Parameters
|
||
|
- ----------
|
||
|
- terms : Op
|
||
|
- The expression that should cast.
|
||
|
- acceptable_dtypes : list of acceptable numpy.dtype
|
||
|
- Will not cast if term's dtype in this list.
|
||
|
- dtype : str or numpy.dtype
|
||
|
- The dtype to cast to.
|
||
|
- """
|
||
|
- dt = np.dtype(dtype)
|
||
|
- for term in terms:
|
||
|
- if term.type in acceptable_dtypes:
|
||
|
- continue
|
||
|
-
|
||
|
- try:
|
||
|
- new_value = term.value.astype(dt)
|
||
|
- except AttributeError:
|
||
|
- new_value = dt.type(term.value)
|
||
|
- term.update(new_value)
|
||
|
-
|
||
|
-
|
||
|
def is_term(obj) -> bool:
|
||
|
return isinstance(obj, Term)
|
||
|
|
||
|
@@ -516,31 +491,6 @@ class BinOp(Op):
|
||
|
def isnumeric(dtype) -> bool:
|
||
|
return issubclass(np.dtype(dtype).type, np.number)
|
||
|
|
||
|
-
|
||
|
-class Div(BinOp):
|
||
|
- """
|
||
|
- Div operator to special case casting.
|
||
|
-
|
||
|
- Parameters
|
||
|
- ----------
|
||
|
- lhs, rhs : Term or Op
|
||
|
- The Terms or Ops in the ``/`` expression.
|
||
|
- """
|
||
|
-
|
||
|
- def __init__(self, lhs, rhs) -> None:
|
||
|
- super().__init__("/", lhs, rhs)
|
||
|
-
|
||
|
- if not isnumeric(lhs.return_type) or not isnumeric(rhs.return_type):
|
||
|
- raise TypeError(
|
||
|
- f"unsupported operand type(s) for {self.op}: "
|
||
|
- f"'{lhs.return_type}' and '{rhs.return_type}'"
|
||
|
- )
|
||
|
-
|
||
|
- # do not upcast float32s to float64 un-necessarily
|
||
|
- acceptable_dtypes = [np.float32, np.float64]
|
||
|
- _cast_inplace(com.flatten(self), acceptable_dtypes, np.float64)
|
||
|
-
|
||
|
-
|
||
|
UNARY_OPS_SYMS = ("+", "-", "~", "not")
|
||
|
_unary_ops_funcs = (operator.pos, operator.neg, operator.invert, operator.invert)
|
||
|
_unary_ops_dict = dict(zip(UNARY_OPS_SYMS, _unary_ops_funcs))
|
||
|
diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py
|
||
|
index 17630f14b0..e8fad6b8cb 100644
|
||
|
--- a/pandas/tests/computation/test_eval.py
|
||
|
+++ b/pandas/tests/computation/test_eval.py
|
||
|
@@ -747,16 +747,26 @@ class TestTypeCasting:
|
||
|
@pytest.mark.parametrize("op", ["+", "-", "*", "**", "/"])
|
||
|
# maybe someday... numexpr has too many upcasting rules now
|
||
|
# chain(*(np.core.sctypes[x] for x in ['uint', 'int', 'float']))
|
||
|
- @pytest.mark.parametrize("dt", [np.float32, np.float64])
|
||
|
@pytest.mark.parametrize("left_right", [("df", "3"), ("3", "df")])
|
||
|
- def test_binop_typecasting(self, engine, parser, op, dt, left_right):
|
||
|
- df = DataFrame(np.random.default_rng(2).standard_normal((5, 3)), dtype=dt)
|
||
|
+ def test_binop_typecasting(
|
||
|
+ self, engine, parser, op, complex_or_float_dtype, left_right, request
|
||
|
+ ):
|
||
|
+ # GH#21374
|
||
|
+ dtype = complex_or_float_dtype
|
||
|
+ df = DataFrame(np.random.default_rng(2).standard_normal((5, 3)), dtype=dtype)
|
||
|
left, right = left_right
|
||
|
s = f"{left} {op} {right}"
|
||
|
res = pd.eval(s, engine=engine, parser=parser)
|
||
|
- assert df.values.dtype == dt
|
||
|
- assert res.values.dtype == dt
|
||
|
- tm.assert_frame_equal(res, eval(s))
|
||
|
+ if dtype == "complex64" and engine == "numexpr":
|
||
|
+ mark = pytest.mark.xfail(
|
||
|
+ reason="numexpr issue with complex that are upcast "
|
||
|
+ "to complex 128 "
|
||
|
+ "https://github.com/pydata/numexpr/issues/492"
|
||
|
+ )
|
||
|
+ request.applymarker(mark)
|
||
|
+ assert df.values.dtype == dtype
|
||
|
+ assert res.values.dtype == dtype
|
||
|
+ tm.assert_frame_equal(res, eval(s), check_exact=False)
|
||
|
|
||
|
|
||
|
# -------------------------------------
|