diff --git a/pandas/core/computation/expressions.py b/pandas/core/computation/expressions.py index dc4e6e85f6e7d..ea61467080291 100644 --- a/pandas/core/computation/expressions.py +++ b/pandas/core/computation/expressions.py @@ -12,9 +12,10 @@ from pandas._config import get_option +from pandas._libs.lib import values_from_object + from pandas.core.dtypes.generic import ABCDataFrame -import pandas.core.common as com from pandas.core.computation.check import _NUMEXPR_INSTALLED if _NUMEXPR_INSTALLED: @@ -129,9 +130,7 @@ def _evaluate_numexpr(op, op_str, a, b, truediv=True, reversed=False, **eval_kwa def _where_standard(cond, a, b): return np.where( - com.values_from_object(cond), - com.values_from_object(a), - com.values_from_object(b), + values_from_object(cond), values_from_object(a), values_from_object(b) ) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index bf6ebf1abe760..5785dbfbd6cac 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -397,10 +397,6 @@ def fillna(self, value, limit=None, inplace=False, downcast=None): raise ValueError("Limit must be an integer") if limit < 1: raise ValueError("Limit must be greater than 0") - if self.ndim > 2: - raise NotImplementedError( - "number of dimensions for 'fillna' is currently limited to 2" - ) mask[mask.cumsum(self.ndim - 1) > limit] = False if not self._can_hold_na: @@ -853,6 +849,8 @@ def setitem(self, indexer, value): `indexer` is a direct slice/positional indexer. `value` must be a compatible shape. """ + transpose = self.ndim == 2 + # coerce None values, if appropriate if value is None: if self.is_numeric: @@ -901,8 +899,8 @@ def setitem(self, indexer, value): dtype, _ = maybe_promote(arr_value.dtype) values = values.astype(dtype) - transf = (lambda x: x.T) if self.ndim == 2 else (lambda x: x) - values = transf(values) + if transpose: + values = values.T # length checking check_setitem_lengths(indexer, value, values) @@ -961,7 +959,9 @@ def _is_empty_indexer(indexer): # coerce and try to infer the dtypes of the result values = self._try_coerce_and_cast_result(values, dtype) - block = self.make_block(transf(values)) + if transpose: + values = values.T + block = self.make_block(values) return block def putmask(self, mask, new, align=True, inplace=False, axis=0, transpose=False): diff --git a/pandas/core/internals/managers.py b/pandas/core/internals/managers.py index b3c74aaaa5701..cd678a235cfc1 100644 --- a/pandas/core/internals/managers.py +++ b/pandas/core/internals/managers.py @@ -583,8 +583,9 @@ def astype(self, dtype, **kwargs): def convert(self, **kwargs): return self.apply("convert", **kwargs) - def replace(self, **kwargs): - return self.apply("replace", **kwargs) + def replace(self, value, **kwargs): + assert np.ndim(value) == 0, value + return self.apply("replace", value=value, **kwargs) def replace_list(self, src_list, dest_list, inplace=False, regex=False): """ do a list replace """ @@ -617,6 +618,7 @@ def comp(s, regex=False): # replace ALWAYS will return a list rb = [blk if inplace else blk.copy()] for i, (s, d) in enumerate(zip(src_list, dest_list)): + # TODO: assert/validate that `d` is always a scalar? new_rb = [] for b in rb: m = masks[i][b.mgr_locs.indexer] diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index f9112dbb1e4ab..d735ab3ad2535 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -11,7 +11,7 @@ import numpy as np -from pandas._libs import lib, ops as libops +from pandas._libs import Timedelta, Timestamp, lib, ops as libops from pandas.errors import NullFrequencyError from pandas.util._decorators import Appender @@ -87,7 +87,7 @@ def get_op_result_name(left, right): Usually a string """ # `left` is always a pd.Series when called from within ops - if isinstance(right, (ABCSeries, pd.Index)): + if isinstance(right, (ABCSeries, ABCIndexClass)): name = _maybe_match_name(left, right) else: name = left.name @@ -151,14 +151,14 @@ def maybe_upcast_for_op(obj): # GH#22390 cast up to Timedelta to rely on Timedelta # implementation; otherwise operation against numeric-dtype # raises TypeError - return pd.Timedelta(obj) + return Timedelta(obj) elif isinstance(obj, np.timedelta64) and not isna(obj): # In particular non-nanosecond timedelta64 needs to be cast to # nanoseconds, or else we get undesired behavior like # np.timedelta64(3, 'D') / 2 == np.timedelta64(1, 'D') # The isna check is to avoid casting timedelta64("NaT"), which would # return NaT and incorrectly be treated as a datetime-NaT. - return pd.Timedelta(obj) + return Timedelta(obj) elif isinstance(obj, np.ndarray) and is_timedelta64_dtype(obj): # GH#22390 Unfortunately we need to special-case right-hand # timedelta64 dtypes because numpy casts integer dtypes to @@ -1864,7 +1864,7 @@ def wrapper(self, other, axis=None): ) msg = "\n".join(textwrap.wrap(msg.format(future=future))) warnings.warn(msg, FutureWarning, stacklevel=2) - other = pd.Timestamp(other) + other = Timestamp(other) res_values = dispatch_to_index_op(op, self, other, pd.DatetimeIndex) @@ -1890,7 +1890,7 @@ def wrapper(self, other, axis=None): res_values, index=self.index, name=res_name ).rename(res_name) - elif isinstance(other, (np.ndarray, pd.Index)): + elif isinstance(other, (np.ndarray, ABCIndexClass)): # do not check length of zerodim array # as it will broadcast if other.ndim != 0 and len(self) != len(other): diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index 37a885e33847f..49d11f58ebe08 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -411,7 +411,7 @@ def check_single_invert_op(self, lhs, cmp1, rhs): ) def check_compound_invert_op(self, lhs, cmp1, rhs): - skip_these = "in", "not in" + skip_these = ["in", "not in"] ex = "~(lhs {0} rhs)".format(cmp1) msg = ( diff --git a/pandas/tests/io/json/test_pandas.py b/pandas/tests/io/json/test_pandas.py index 970fd465fd4ec..9c687f036aa68 100644 --- a/pandas/tests/io/json/test_pandas.py +++ b/pandas/tests/io/json/test_pandas.py @@ -1432,6 +1432,7 @@ def test_to_jsonl(self): assert result == expected assert_frame_equal(pd.read_json(result, lines=True), df) + # TODO: there is a near-identical test for pytables; can we share? def test_latin_encoding(self): # GH 13774 pytest.skip("encoding not implemented in .to_json(), xref #13774") diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 5b57b5ba2dbae..89557445cafb4 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -24,7 +24,7 @@ class TestSeriesFlexArithmetic: ], ) @pytest.mark.parametrize( - "opname", ["add", "sub", "mul", "floordiv", "truediv", "div", "pow"] + "opname", ["add", "sub", "mul", "floordiv", "truediv", "pow"] ) def test_flex_method_equivalence(self, opname, ts): # check that Series.{opname} behaves like Series.__{opname}__, @@ -34,15 +34,8 @@ def test_flex_method_equivalence(self, opname, ts): other = ts[1](tser) check_reverse = ts[2] - if opname == "div": - pytest.skip("div test only for Py3") - op = getattr(Series, opname) - - if op == "div": - alt = operator.truediv - else: - alt = getattr(operator, opname) + alt = getattr(operator, opname) result = op(series, other) expected = alt(series, other)