diff --git a/pandas/core/computation/align.py b/pandas/core/computation/align.py index dfb858d797f41a..197ddd999fd374 100644 --- a/pandas/core/computation/align.py +++ b/pandas/core/computation/align.py @@ -8,10 +8,11 @@ from pandas.errors import PerformanceWarning -import pandas as pd +from pandas.core.dtypes.generic import ABCDataFrame, ABCSeries + from pandas.core.base import PandasObject import pandas.core.common as com -from pandas.core.computation.common import _result_type_many +from pandas.core.computation.common import result_type_many def _align_core_single_unary_op(term): @@ -49,7 +50,7 @@ def wrapper(terms): # we don't have any pandas objects if not _any_pandas_objects(terms): - return _result_type_many(*term_values), None + return result_type_many(*term_values), None return f(terms) @@ -60,7 +61,10 @@ def wrapper(terms): def _align_core(terms): term_index = [i for i, term in enumerate(terms) if hasattr(term.value, "axes")] term_dims = [terms[i].value.ndim for i in term_index] - ndims = pd.Series(dict(zip(term_index, term_dims))) + + from pandas import Series + + ndims = Series(dict(zip(term_index, term_dims))) # initial axes are the axes of the largest-axis'd term biggest = terms[ndims.idxmax()].value @@ -70,7 +74,7 @@ def _align_core(terms): gt_than_one_axis = naxes > 1 for value in (terms[i].value for i in term_index): - is_series = isinstance(value, pd.Series) + is_series = isinstance(value, ABCSeries) is_series_and_gt_one_axis = is_series and gt_than_one_axis for axis, items in enumerate(value.axes): @@ -87,7 +91,7 @@ def _align_core(terms): ti = terms[i].value if hasattr(ti, "reindex"): - transpose = isinstance(ti, pd.Series) and naxes > 1 + transpose = isinstance(ti, ABCSeries) and naxes > 1 reindexer = axes[naxes - 1] if transpose else items term_axis_size = len(ti.axes[axis]) @@ -111,28 +115,28 @@ def _align_core(terms): return typ, _zip_axes_from_type(typ, axes) -def _align(terms): +def align_terms(terms): """Align a set of terms""" try: # flatten the parse tree (a nested list, really) terms = list(com.flatten(terms)) except TypeError: # can't iterate so it must just be a constant or single variable - if isinstance(terms.value, pd.core.generic.NDFrame): + if isinstance(terms.value, (ABCSeries, ABCDataFrame)): typ = type(terms.value) return typ, _zip_axes_from_type(typ, terms.value.axes) return np.result_type(terms.type), None # if all resolved variables are numeric scalars if all(term.is_scalar for term in terms): - return _result_type_many(*(term.value for term in terms)).type, None + return result_type_many(*(term.value for term in terms)).type, None # perform the main alignment typ, axes = _align_core(terms) return typ, axes -def _reconstruct_object(typ, obj, axes, dtype): +def reconstruct_object(typ, obj, axes, dtype): """ Reconstruct an object given its type, raw value, and possibly empty (None) axes. diff --git a/pandas/core/computation/common.py b/pandas/core/computation/common.py index bd32c8bee1cdff..da47449d5e62ee 100644 --- a/pandas/core/computation/common.py +++ b/pandas/core/computation/common.py @@ -15,7 +15,7 @@ def _ensure_decoded(s): return s -def _result_type_many(*arrays_and_dtypes): +def result_type_many(*arrays_and_dtypes): """ wrapper around numpy.result_type which overcomes the NPY_MAXARGS (32) argument limit """ try: diff --git a/pandas/core/computation/engines.py b/pandas/core/computation/engines.py index 513eb0fd7f2a6f..2f3c519d352c60 100644 --- a/pandas/core/computation/engines.py +++ b/pandas/core/computation/engines.py @@ -4,7 +4,7 @@ import abc -from pandas.core.computation.align import _align, _reconstruct_object +from pandas.core.computation.align import align_terms, reconstruct_object from pandas.core.computation.ops import UndefinedVariableError, _mathops, _reductions import pandas.io.formats.printing as printing @@ -67,11 +67,11 @@ def evaluate(self): The result of the passed expression. """ if not self._is_aligned: - self.result_type, self.aligned_axes = _align(self.expr.terms) + self.result_type, self.aligned_axes = align_terms(self.expr.terms) # make sure no names in resolvers and locals/globals clash res = self._evaluate() - return _reconstruct_object( + return reconstruct_object( self.result_type, res, self.aligned_axes, self.expr.terms.return_type ) diff --git a/pandas/core/computation/eval.py b/pandas/core/computation/eval.py index 461561a80a7e54..de2133f64291dc 100644 --- a/pandas/core/computation/eval.py +++ b/pandas/core/computation/eval.py @@ -10,6 +10,7 @@ from pandas.util._validators import validate_bool_kwarg from pandas.core.computation.engines import _engines +from pandas.core.computation.expr import Expr, _parsers, tokenize_string from pandas.core.computation.scope import _ensure_scope from pandas.io.formats.printing import pprint_thing @@ -64,7 +65,7 @@ def _check_engine(engine): return engine -def _check_parser(parser): +def _check_parser(parser: str): """ Make sure a valid parser is passed. @@ -77,7 +78,6 @@ def _check_parser(parser): KeyError * If an invalid parser is passed """ - from pandas.core.computation.expr import _parsers if parser not in _parsers: raise KeyError( @@ -115,7 +115,7 @@ def _check_expression(expr): raise ValueError("expr cannot be an empty string") -def _convert_expression(expr): +def _convert_expression(expr) -> str: """ Convert an object to an expression. @@ -131,7 +131,7 @@ def _convert_expression(expr): Returns ------- - s : unicode + str The string representation of an object. Raises @@ -144,8 +144,7 @@ def _convert_expression(expr): return s -def _check_for_locals(expr, stack_level, parser): - from pandas.core.computation.expr import tokenize_string +def _check_for_locals(expr: str, stack_level: int, parser: str): at_top_of_stack = stack_level == 0 not_pandas_parser = parser != "pandas" @@ -192,7 +191,7 @@ def eval( Parameters ---------- - expr : str or unicode + expr : str The expression to evaluate. This string cannot contain any Python `statements `__, @@ -282,7 +281,6 @@ def eval( See the :ref:`enhancing performance ` documentation for more details. """ - from pandas.core.computation.expr import Expr inplace = validate_bool_kwarg(inplace, "inplace") diff --git a/pandas/core/computation/expr.py b/pandas/core/computation/expr.py index 929c9e69d56ac5..4d1fc42070ea8e 100644 --- a/pandas/core/computation/expr.py +++ b/pandas/core/computation/expr.py @@ -11,7 +11,6 @@ import numpy as np -import pandas as pd import pandas.core.common as com from pandas.core.computation.common import ( _BACKTICK_QUOTED_STRING, @@ -40,7 +39,7 @@ import pandas.io.formats.printing as printing -def tokenize_string(source): +def tokenize_string(source: str): """ Tokenize a Python source code string. @@ -171,7 +170,7 @@ def _compose(*funcs): def _preparse( - source, + source: str, f=_compose( _replace_locals, _replace_booleans, @@ -600,6 +599,8 @@ def visit_Index(self, node, **kwargs): return self.visit(node.value) def visit_Subscript(self, node, **kwargs): + import pandas as pd + value = self.visit(node.value) slobj = self.visit(node.slice) result = pd.eval( diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 0fdbdda30ad355..ce67c3152ecd0b 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -13,7 +13,7 @@ from pandas.core.dtypes.common import is_list_like, is_scalar import pandas.core.common as com -from pandas.core.computation.common import _ensure_decoded, _result_type_many +from pandas.core.computation.common import _ensure_decoded, result_type_many from pandas.core.computation.scope import _DEFAULT_GLOBALS from pandas.io.formats.printing import pprint_thing, pprint_thing_encoded @@ -218,7 +218,7 @@ def return_type(self): # clobber types to bool if the op is a boolean operator if self.op in (_cmp_ops_syms + _bool_ops_syms): return np.bool_ - return _result_type_many(*(term.type for term in com.flatten(self))) + return result_type_many(*(term.type for term in com.flatten(self))) @property def has_invalid_return_type(self) -> bool: