from datetime import ( date, timedelta, timezone, ) from decimal import Decimal import operator import numpy as np import pytest from pandas._libs import lib from pandas._libs.tslibs import IncompatibleFrequency import pandas as pd from pandas import ( Categorical, DatetimeTZDtype, Index, Series, Timedelta, bdate_range, date_range, isna, ) import pandas._testing as tm from pandas.core import ops from pandas.core.computation import expressions as expr from pandas.core.computation.check import NUMEXPR_INSTALLED @pytest.fixture(autouse=True, params=[0, 1000000], ids=["numexpr", "python"]) def switch_numexpr_min_elements(request, monkeypatch): with monkeypatch.context() as m: m.setattr(expr, "_MIN_ELEMENTS", request.param) yield def _permute(obj): return obj.take(np.random.default_rng(2).permutation(len(obj))) class TestSeriesFlexArithmetic: @pytest.mark.parametrize( "ts", [ (lambda x: x, lambda x: x * 2, False), (lambda x: x, lambda x: x[::2], False), (lambda x: x, lambda x: 5, True), ( lambda x: Series(range(10), dtype=np.float64), lambda x: Series(range(10), dtype=np.float64), True, ), ], ) @pytest.mark.parametrize( "opname", ["add", "sub", "mul", "floordiv", "truediv", "pow"] ) def test_flex_method_equivalence(self, opname, ts): # check that Series.{opname} behaves like Series.__{opname}__, tser = Series( np.arange(20, dtype=np.float64), index=date_range("2020-01-01", periods=20), name="ts", ) series = ts[0](tser) other = ts[1](tser) check_reverse = ts[2] op = getattr(Series, opname) alt = getattr(operator, opname) result = op(series, other) expected = alt(series, other) tm.assert_almost_equal(result, expected) if check_reverse: rop = getattr(Series, "r" + opname) result = rop(series, other) expected = alt(other, series) tm.assert_almost_equal(result, expected) def test_flex_method_subclass_metadata_preservation(self, all_arithmetic_operators): # GH 13208 class MySeries(Series): _metadata = ["x"] @property def _constructor(self): return MySeries opname = all_arithmetic_operators op = getattr(Series, opname) m = MySeries([1, 2, 3], name="test") m.x = 42 result = op(m, 1) assert result.x == 42 def test_flex_add_scalar_fill_value(self): # GH12723 ser = Series([0, 1, np.nan, 3, 4, 5]) exp = ser.fillna(0).add(2) res = ser.add(2, fill_value=0) tm.assert_series_equal(res, exp) pairings = [(Series.div, operator.truediv, 1), (Series.rdiv, ops.rtruediv, 1)] for op in ["add", "sub", "mul", "pow", "truediv", "floordiv"]: fv = 0 lop = getattr(Series, op) lequiv = getattr(operator, op) rop = getattr(Series, "r" + op) # bind op at definition time... requiv = lambda x, y, op=op: getattr(operator, op)(y, x) pairings.append((lop, lequiv, fv)) pairings.append((rop, requiv, fv)) @pytest.mark.parametrize("op, equiv_op, fv", pairings) def test_operators_combine(self, op, equiv_op, fv): def _check_fill(meth, op, a, b, fill_value=0): exp_index = a.index.union(b.index) a = a.reindex(exp_index) b = b.reindex(exp_index) amask = isna(a) bmask = isna(b) exp_values = [] for i in range(len(exp_index)): with np.errstate(all="ignore"): if amask[i]: if bmask[i]: exp_values.append(np.nan) continue exp_values.append(op(fill_value, b[i])) elif bmask[i]: if amask[i]: exp_values.append(np.nan) continue exp_values.append(op(a[i], fill_value)) else: exp_values.append(op(a[i], b[i])) result = meth(a, b, fill_value=fill_value) expected = Series(exp_values, exp_index) tm.assert_series_equal(result, expected) a = Series([np.nan, 1.0, 2.0, 3.0, np.nan], index=np.arange(5)) b = Series([np.nan, 1, np.nan, 3, np.nan, 4.0], index=np.arange(6)) result = op(a, b) exp = equiv_op(a, b) tm.assert_series_equal(result, exp) _check_fill(op, equiv_op, a, b, fill_value=fv) # should accept axis=0 or axis='rows' op(a, b, axis=0) class TestSeriesArithmetic: # Some of these may end up in tests/arithmetic, but are not yet sorted def test_add_series_with_period_index(self): rng = pd.period_range("1/1/2000", "1/1/2010", freq="Y") ts = Series(np.random.default_rng(2).standard_normal(len(rng)), index=rng) result = ts + ts[::2] expected = ts + ts expected.iloc[1::2] = np.nan tm.assert_series_equal(result, expected) result = ts + _permute(ts[::2]) tm.assert_series_equal(result, expected) msg = "Input has different freq=D from Period\\(freq=Y-DEC\\)" with pytest.raises(IncompatibleFrequency, match=msg): ts + ts.asfreq("D", how="end") @pytest.mark.parametrize( "target_add,input_value,expected_value", [ ("!", ["hello", "world"], ["hello!", "world!"]), ("m", ["hello", "world"], ["hellom", "worldm"]), ], ) def test_string_addition(self, target_add, input_value, expected_value): # GH28658 - ensure adding 'm' does not raise an error a = Series(input_value) result = a + target_add expected = Series(expected_value) tm.assert_series_equal(result, expected) def test_divmod(self): # GH#25557 a = Series([1, 1, 1, np.nan], index=["a", "b", "c", "d"]) b = Series([2, np.nan, 1, np.nan], index=["a", "b", "d", "e"]) result = a.divmod(b) expected = divmod(a, b) tm.assert_series_equal(result[0], expected[0]) tm.assert_series_equal(result[1], expected[1]) result = a.rdivmod(b) expected = divmod(b, a) tm.assert_series_equal(result[0], expected[0]) tm.assert_series_equal(result[1], expected[1]) @pytest.mark.parametrize("index", [None, range(9)]) def test_series_integer_mod(self, index): # GH#24396 s1 = Series(range(1, 10)) s2 = Series("foo", index=index) msg = "not all arguments converted during string formatting|mod not" with pytest.raises((TypeError, NotImplementedError), match=msg): s2 % s1 def test_add_with_duplicate_index(self): # GH14227 s1 = Series([1, 2], index=[1, 1]) s2 = Series([10, 10], index=[1, 2]) result = s1 + s2 expected = Series([11, 12, np.nan], index=[1, 1, 2]) tm.assert_series_equal(result, expected) def test_add_na_handling(self): ser = Series( [Decimal("1.3"), Decimal("2.3")], index=[date(2012, 1, 1), date(2012, 1, 2)] ) result = ser + ser.shift(1) result2 = ser.shift(1) + ser assert isna(result.iloc[0]) assert isna(result2.iloc[0]) def test_add_corner_cases(self, datetime_series): empty = Series([], index=Index([]), dtype=np.float64) result = datetime_series + empty assert np.isnan(result).all() result = empty + empty.copy() assert len(result) == 0 def test_add_float_plus_int(self, datetime_series): # float + int int_ts = datetime_series.astype(int)[:-5] added = datetime_series + int_ts expected = Series( datetime_series.values[:-5] + int_ts.values, index=datetime_series.index[:-5], name="ts", ) tm.assert_series_equal(added[:-5], expected) def test_mul_empty_int_corner_case(self): s1 = Series([], [], dtype=np.int32) s2 = Series({"x": 0.0}) tm.assert_series_equal(s1 * s2, Series([np.nan], index=["x"])) def test_sub_datetimelike_align(self): # GH#7500 # datetimelike ops need to align dt = Series(date_range("2012-1-1", periods=3, freq="D")) dt.iloc[2] = np.nan dt2 = dt[::-1] expected = Series([timedelta(0), timedelta(0), pd.NaT]) # name is reset result = dt2 - dt tm.assert_series_equal(result, expected) expected = Series(expected, name=0) result = (dt2.to_frame() - dt.to_frame())[0] tm.assert_series_equal(result, expected) def test_alignment_doesnt_change_tz(self): # GH#33671 dti = date_range("2016-01-01", periods=10, tz="CET") dti_utc = dti.tz_convert("UTC") ser = Series(10, index=dti) ser_utc = Series(10, index=dti_utc) # we don't care about the result, just that original indexes are unchanged ser * ser_utc assert ser.index is dti assert ser_utc.index is dti_utc def test_alignment_categorical(self): # GH13365 cat = Categorical(["3z53", "3z53", "LoJG", "LoJG", "LoJG", "N503"]) ser1 = Series(2, index=cat) ser2 = Series(2, index=cat[:-1]) result = ser1 * ser2 exp_index = ["3z53"] * 4 + ["LoJG"] * 9 + ["N503"] exp_index = pd.CategoricalIndex(exp_index, categories=cat.categories) exp_values = [4.0] * 13 + [np.nan] expected = Series(exp_values, exp_index) tm.assert_series_equal(result, expected) def test_arithmetic_with_duplicate_index(self): # GH#8363 # integer ops with a non-unique index index = [2, 2, 3, 3, 4] ser = Series(np.arange(1, 6, dtype="int64"), index=index) other = Series(np.arange(5, dtype="int64"), index=index) result = ser - other expected = Series(1, index=[2, 2, 3, 3, 4]) tm.assert_series_equal(result, expected) # GH#8363 # datetime ops with a non-unique index ser = Series(date_range("20130101 09:00:00", periods=5), index=index) other = Series(date_range("20130101", periods=5), index=index) result = ser - other expected = Series(Timedelta("9 hours"), index=[2, 2, 3, 3, 4]) tm.assert_series_equal(result, expected) def test_masked_and_non_masked_propagate_na(self): # GH#45810 ser1 = Series([0, np.nan], dtype="float") ser2 = Series([0, 1], dtype="Int64") result = ser1 * ser2 expected = Series([0, pd.NA], dtype="Float64") tm.assert_series_equal(result, expected) def test_mask_div_propagate_na_for_non_na_dtype(self): # GH#42630 ser1 = Series([15, pd.NA, 5, 4], dtype="Int64") ser2 = Series([15, 5, np.nan, 4]) result = ser1 / ser2 expected = Series([1.0, pd.NA, pd.NA, 1.0], dtype="Float64") tm.assert_series_equal(result, expected) result = ser2 / ser1 tm.assert_series_equal(result, expected) @pytest.mark.parametrize("val, dtype", [(3, "Int64"), (3.5, "Float64")]) def test_add_list_to_masked_array(self, val, dtype): # GH#22962 ser = Series([1, None, 3], dtype="Int64") result = ser + [1, None, val] expected = Series([2, None, 3 + val], dtype=dtype) tm.assert_series_equal(result, expected) result = [1, None, val] + ser tm.assert_series_equal(result, expected) def test_add_list_to_masked_array_boolean(self, request): # GH#22962 warning = ( UserWarning if request.node.callspec.id == "numexpr" and NUMEXPR_INSTALLED else None ) ser = Series([True, None, False], dtype="boolean") with tm.assert_produces_warning(warning): result = ser + [True, None, True] expected = Series([True, None, True], dtype="boolean") tm.assert_series_equal(result, expected) with tm.assert_produces_warning(warning): result = [True, None, True] + ser tm.assert_series_equal(result, expected) # ------------------------------------------------------------------ # Comparisons class TestSeriesFlexComparison: @pytest.mark.parametrize("axis", [0, None, "index"]) def test_comparison_flex_basic(self, axis, comparison_op): left = Series(np.random.default_rng(2).standard_normal(10)) right = Series(np.random.default_rng(2).standard_normal(10)) result = getattr(left, comparison_op.__name__)(right, axis=axis) expected = comparison_op(left, right) tm.assert_series_equal(result, expected) def test_comparison_bad_axis(self, comparison_op): left = Series(np.random.default_rng(2).standard_normal(10)) right = Series(np.random.default_rng(2).standard_normal(10)) msg = "No axis named 1 for object type" with pytest.raises(ValueError, match=msg): getattr(left, comparison_op.__name__)(right, axis=1) @pytest.mark.parametrize( "values, op", [ ([False, False, True, False], "eq"), ([True, True, False, True], "ne"), ([False, False, True, False], "le"), ([False, False, False, False], "lt"), ([False, True, True, False], "ge"), ([False, True, False, False], "gt"), ], ) def test_comparison_flex_alignment(self, values, op): left = Series([1, 3, 2], index=list("abc")) right = Series([2, 2, 2], index=list("bcd")) result = getattr(left, op)(right) expected = Series(values, index=list("abcd")) tm.assert_series_equal(result, expected) @pytest.mark.parametrize( "values, op, fill_value", [ ([False, False, True, True], "eq", 2), ([True, True, False, False], "ne", 2), ([False, False, True, True], "le", 0), ([False, False, False, True], "lt", 0), ([True, True, True, False], "ge", 0), ([True, True, False, False], "gt", 0), ], ) def test_comparison_flex_alignment_fill(self, values, op, fill_value): left = Series([1, 3, 2], index=list("abc")) right = Series([2, 2, 2], index=list("bcd")) result = getattr(left, op)(right, fill_value=fill_value) expected = Series(values, index=list("abcd")) tm.assert_series_equal(result, expected) class TestSeriesComparison: def test_comparison_different_length(self): a = Series(["a", "b", "c"]) b = Series(["b", "a"]) msg = "only compare identically-labeled Series" with pytest.raises(ValueError, match=msg): a < b a = Series([1, 2]) b = Series([2, 3, 4]) with pytest.raises(ValueError, match=msg): a == b @pytest.mark.parametrize("opname", ["eq", "ne", "gt", "lt", "ge", "le"]) def test_ser_flex_cmp_return_dtypes(self, opname): # GH#15115 ser = Series([1, 3, 2], index=range(3)) const = 2 result = getattr(ser, opname)(const).dtypes expected = np.dtype("bool") assert result == expected @pytest.mark.parametrize("opname", ["eq", "ne", "gt", "lt", "ge", "le"]) def test_ser_flex_cmp_return_dtypes_empty(self, opname): # GH#15115 empty Series case ser = Series([1, 3, 2], index=range(3)) empty = ser.iloc[:0] const = 2 result = getattr(empty, opname)(const).dtypes expected = np.dtype("bool") assert result == expected @pytest.mark.parametrize( "names", [(None, None, None), ("foo", "bar", None), ("baz", "baz", "baz")] ) def test_ser_cmp_result_names(self, names, comparison_op): # datetime64 dtype op = comparison_op dti = date_range("1949-06-07 03:00:00", freq="h", periods=5, name=names[0]) ser = Series(dti).rename(names[1]) result = op(ser, dti) assert result.name == names[2] # datetime64tz dtype dti = dti.tz_localize("US/Central") dti = pd.DatetimeIndex(dti, freq="infer") # freq not preserved by tz_localize ser = Series(dti).rename(names[1]) result = op(ser, dti) assert result.name == names[2] # timedelta64 dtype tdi = dti - dti.shift(1) ser = Series(tdi).rename(names[1]) result = op(ser, tdi) assert result.name == names[2] # interval dtype if op in [operator.eq, operator.ne]: # interval dtype comparisons not yet implemented ii = pd.interval_range(start=0, periods=5, name=names[0]) ser = Series(ii).rename(names[1]) result = op(ser, ii) assert result.name == names[2] # categorical if op in [operator.eq, operator.ne]: # categorical dtype comparisons raise for inequalities cidx = tdi.astype("category") ser = Series(cidx).rename(names[1]) result = op(ser, cidx) assert result.name == names[2] def test_comparisons(self, using_infer_string): s = Series(["a", "b", "c"]) s2 = Series([False, True, False]) # it works! exp = Series([False, False, False]) if using_infer_string: import pyarrow as pa msg = "has no kernel" # TODO(3.0) GH56008 with pytest.raises(pa.lib.ArrowNotImplementedError, match=msg): s == s2 with tm.assert_produces_warning( DeprecationWarning, match="comparison", check_stacklevel=False ): with pytest.raises(pa.lib.ArrowNotImplementedError, match=msg): s2 == s else: tm.assert_series_equal(s == s2, exp) tm.assert_series_equal(s2 == s, exp) # ----------------------------------------------------------------- # Categorical Dtype Comparisons def test_categorical_comparisons(self): # GH#8938 # allow equality comparisons a = Series(list("abc"), dtype="category") b = Series(list("abc"), dtype="object") c = Series(["a", "b", "cc"], dtype="object") d = Series(list("acb"), dtype="object") e = Categorical(list("abc")) f = Categorical(list("acb")) # vs scalar assert not (a == "a").all() assert ((a != "a") == ~(a == "a")).all() assert not ("a" == a).all() assert (a == "a")[0] assert ("a" == a)[0] assert not ("a" != a)[0] # vs list-like assert (a == a).all() assert not (a != a).all() assert (a == list(a)).all() assert (a == b).all() assert (b == a).all() assert ((~(a == b)) == (a != b)).all() assert ((~(b == a)) == (b != a)).all() assert not (a == c).all() assert not (c == a).all() assert not (a == d).all() assert not (d == a).all() # vs a cat-like assert (a == e).all() assert (e == a).all() assert not (a == f).all() assert not (f == a).all() assert (~(a == e) == (a != e)).all() assert (~(e == a) == (e != a)).all() assert (~(a == f) == (a != f)).all() assert (~(f == a) == (f != a)).all() # non-equality is not comparable msg = "can only compare equality or not" with pytest.raises(TypeError, match=msg): a < b with pytest.raises(TypeError, match=msg): b < a with pytest.raises(TypeError, match=msg): a > b with pytest.raises(TypeError, match=msg): b > a def test_unequal_categorical_comparison_raises_type_error(self): # unequal comparison should raise for unordered cats cat = Series(Categorical(list("abc"))) msg = "can only compare equality or not" with pytest.raises(TypeError, match=msg): cat > "b" cat = Series(Categorical(list("abc"), ordered=False)) with pytest.raises(TypeError, match=msg): cat > "b" # https://github.com/pandas-dev/pandas/issues/9836#issuecomment-92123057 # and following comparisons with scalars not in categories should raise # for unequal comps, but not for equal/not equal cat = Series(Categorical(list("abc"), ordered=True)) msg = "Invalid comparison between dtype=category and str" with pytest.raises(TypeError, match=msg): cat < "d" with pytest.raises(TypeError, match=msg): cat > "d" with pytest.raises(TypeError, match=msg): "d" < cat with pytest.raises(TypeError, match=msg): "d" > cat tm.assert_series_equal(cat == "d", Series([False, False, False])) tm.assert_series_equal(cat != "d", Series([True, True, True])) # ----------------------------------------------------------------- def test_comparison_tuples(self): # GH#11339 # comparisons vs tuple s = Series([(1, 1), (1, 2)]) result = s == (1, 2) expected = Series([False, True]) tm.assert_series_equal(result, expected) result = s != (1, 2) expected = Series([True, False]) tm.assert_series_equal(result, expected) result = s == (0, 0) expected = Series([False, False]) tm.assert_series_equal(result, expected) result = s != (0, 0) expected = Series([True, True]) tm.assert_series_equal(result, expected) s = Series([(1, 1), (1, 1)]) result = s == (1, 1) expected = Series([True, True]) tm.assert_series_equal(result, expected) result = s != (1, 1) expected = Series([False, False]) tm.assert_series_equal(result, expected) def test_comparison_frozenset(self): ser = Series([frozenset([1]), frozenset([1, 2])]) result = ser == frozenset([1]) expected = Series([True, False]) tm.assert_series_equal(result, expected) def test_comparison_operators_with_nas(self, comparison_op): ser = Series(bdate_range("1/1/2000", periods=10), dtype=object) ser[::2] = np.nan # test that comparisons work val = ser[5] result = comparison_op(ser, val) expected = comparison_op(ser.dropna(), val).reindex(ser.index) msg = "Downcasting object dtype arrays" with tm.assert_produces_warning(FutureWarning, match=msg): if comparison_op is operator.ne: expected = expected.fillna(True).astype(bool) else: expected = expected.fillna(False).astype(bool) tm.assert_series_equal(result, expected) def test_ne(self): ts = Series([3, 4, 5, 6, 7], [3, 4, 5, 6, 7], dtype=float) expected = np.array([True, True, False, True, True]) tm.assert_numpy_array_equal(ts.index != 5, expected) tm.assert_numpy_array_equal(~(ts.index == 5), expected) @pytest.mark.parametrize( "left, right", [ ( Series([1, 2, 3], index=list("ABC"), name="x"), Series([2, 2, 2], index=list("ABD"), name="x"), ), ( Series([1, 2, 3], index=list("ABC"), name="x"), Series([2, 2, 2, 2], index=list("ABCD"), name="x"), ), ], ) def test_comp_ops_df_compat(self, left, right, frame_or_series): # GH 1134 # GH 50083 to clarify that index and columns must be identically labeled if frame_or_series is not Series: msg = ( rf"Can only compare identically-labeled \(both index and columns\) " f"{frame_or_series.__name__} objects" ) left = left.to_frame() right = right.to_frame() else: msg = ( f"Can only compare identically-labeled {frame_or_series.__name__} " f"objects" ) with pytest.raises(ValueError, match=msg): left == right with pytest.raises(ValueError, match=msg): right == left with pytest.raises(ValueError, match=msg): left != right with pytest.raises(ValueError, match=msg): right != left with pytest.raises(ValueError, match=msg): left < right with pytest.raises(ValueError, match=msg): right < left def test_compare_series_interval_keyword(self): # GH#25338 ser = Series(["IntervalA", "IntervalB", "IntervalC"]) result = ser == "IntervalA" expected = Series([True, False, False]) tm.assert_series_equal(result, expected) # ------------------------------------------------------------------ # Unsorted # These arithmetic tests were previously in other files, eventually # should be parametrized and put into tests.arithmetic class TestTimeSeriesArithmetic: def test_series_add_tz_mismatch_converts_to_utc(self): rng = date_range("1/1/2011", periods=100, freq="h", tz="utc") perm = np.random.default_rng(2).permutation(100)[:90] ser1 = Series( np.random.default_rng(2).standard_normal(90), index=rng.take(perm).tz_convert("US/Eastern"), ) perm = np.random.default_rng(2).permutation(100)[:90] ser2 = Series( np.random.default_rng(2).standard_normal(90), index=rng.take(perm).tz_convert("Europe/Berlin"), ) result = ser1 + ser2 uts1 = ser1.tz_convert("utc") uts2 = ser2.tz_convert("utc") expected = uts1 + uts2 # sort since input indexes are not equal expected = expected.sort_index() assert result.index.tz is timezone.utc tm.assert_series_equal(result, expected) def test_series_add_aware_naive_raises(self): rng = date_range("1/1/2011", periods=10, freq="h") ser = Series(np.random.default_rng(2).standard_normal(len(rng)), index=rng) ser_utc = ser.tz_localize("utc") msg = "Cannot join tz-naive with tz-aware DatetimeIndex" with pytest.raises(Exception, match=msg): ser + ser_utc with pytest.raises(Exception, match=msg): ser_utc + ser # TODO: belongs in tests/arithmetic? def test_datetime_understood(self, unit): # Ensures it doesn't fail to create the right series # reported in issue#16726 series = Series(date_range("2012-01-01", periods=3, unit=unit)) offset = pd.offsets.DateOffset(days=6) result = series - offset exp_dti = pd.to_datetime(["2011-12-26", "2011-12-27", "2011-12-28"]).as_unit( unit ) expected = Series(exp_dti) tm.assert_series_equal(result, expected) def test_align_date_objects_with_datetimeindex(self): rng = date_range("1/1/2000", periods=20) ts = Series(np.random.default_rng(2).standard_normal(20), index=rng) ts_slice = ts[5:] ts2 = ts_slice.copy() ts2.index = [x.date() for x in ts2.index] result = ts + ts2 result2 = ts2 + ts expected = ts + ts[5:] expected.index = expected.index._with_freq(None) tm.assert_series_equal(result, expected) tm.assert_series_equal(result2, expected) class TestNamePreservation: @pytest.mark.parametrize("box", [list, tuple, np.array, Index, Series, pd.array]) @pytest.mark.parametrize("flex", [True, False]) def test_series_ops_name_retention(self, flex, box, names, all_binary_operators): # GH#33930 consistent name-retention op = all_binary_operators left = Series(range(10), name=names[0]) right = Series(range(10), name=names[1]) name = op.__name__.strip("_") is_logical = name in ["and", "rand", "xor", "rxor", "or", "ror"] msg = ( r"Logical ops \(and, or, xor\) between Pandas objects and " "dtype-less sequences" ) warn = None if box in [list, tuple] and is_logical: warn = FutureWarning right = box(right) if flex: if is_logical: # Series doesn't have these as flex methods return result = getattr(left, name)(right) else: # GH#37374 logical ops behaving as set ops deprecated with tm.assert_produces_warning(warn, match=msg): result = op(left, right) assert isinstance(result, Series) if box in [Index, Series]: assert result.name is names[2] or result.name == names[2] else: assert result.name is names[0] or result.name == names[0] def test_binop_maybe_preserve_name(self, datetime_series): # names match, preserve result = datetime_series * datetime_series assert result.name == datetime_series.name result = datetime_series.mul(datetime_series) assert result.name == datetime_series.name result = datetime_series * datetime_series[:-2] assert result.name == datetime_series.name # names don't match, don't preserve cp = datetime_series.copy() cp.name = "something else" result = datetime_series + cp assert result.name is None result = datetime_series.add(cp) assert result.name is None ops = ["add", "sub", "mul", "div", "truediv", "floordiv", "mod", "pow"] ops = ops + ["r" + op for op in ops] for op in ops: # names match, preserve ser = datetime_series.copy() result = getattr(ser, op)(ser) assert result.name == datetime_series.name # names don't match, don't preserve cp = datetime_series.copy() cp.name = "changed" result = getattr(ser, op)(cp) assert result.name is None def test_scalarop_preserve_name(self, datetime_series): result = datetime_series * 2 assert result.name == datetime_series.name class TestInplaceOperations: @pytest.mark.parametrize( "dtype1, dtype2, dtype_expected, dtype_mul", ( ("Int64", "Int64", "Int64", "Int64"), ("float", "float", "float", "float"), ("Int64", "float", "Float64", "Float64"), ("Int64", "Float64", "Float64", "Float64"), ), ) def test_series_inplace_ops(self, dtype1, dtype2, dtype_expected, dtype_mul): # GH 37910 ser1 = Series([1], dtype=dtype1) ser2 = Series([2], dtype=dtype2) ser1 += ser2 expected = Series([3], dtype=dtype_expected) tm.assert_series_equal(ser1, expected) ser1 -= ser2 expected = Series([1], dtype=dtype_expected) tm.assert_series_equal(ser1, expected) ser1 *= ser2 expected = Series([2], dtype=dtype_mul) tm.assert_series_equal(ser1, expected) def test_none_comparison(request, series_with_simple_index): series = series_with_simple_index if len(series) < 1: request.applymarker( pytest.mark.xfail(reason="Test doesn't make sense on empty data") ) # bug brought up by #1079 # changed from TypeError in 0.17.0 series.iloc[0] = np.nan # noinspection PyComparisonWithNone result = series == None # noqa: E711 assert not result.iat[0] assert not result.iat[1] # noinspection PyComparisonWithNone result = series != None # noqa: E711 assert result.iat[0] assert result.iat[1] result = None == series # noqa: E711 assert not result.iat[0] assert not result.iat[1] result = None != series # noqa: E711 assert result.iat[0] assert result.iat[1] if lib.is_np_dtype(series.dtype, "M") or isinstance(series.dtype, DatetimeTZDtype): # Following DatetimeIndex (and Timestamp) convention, # inequality comparisons with Series[datetime64] raise msg = "Invalid comparison" with pytest.raises(TypeError, match=msg): None > series with pytest.raises(TypeError, match=msg): series > None else: result = None > series assert not result.iat[0] assert not result.iat[1] result = series < None assert not result.iat[0] assert not result.iat[1] def test_series_varied_multiindex_alignment(): # GH 20414 s1 = Series( range(8), index=pd.MultiIndex.from_product( [list("ab"), list("xy"), [1, 2]], names=["ab", "xy", "num"] ), ) s2 = Series( [1000 * i for i in range(1, 5)], index=pd.MultiIndex.from_product([list("xy"), [1, 2]], names=["xy", "num"]), ) result = s1.loc[pd.IndexSlice[["a"], :, :]] + s2 expected = Series( [1000, 2001, 3002, 4003], index=pd.MultiIndex.from_tuples( [("a", "x", 1), ("a", "x", 2), ("a", "y", 1), ("a", "y", 2)], names=["ab", "xy", "num"], ), ) tm.assert_series_equal(result, expected) def test_rmod_consistent_large_series(): # GH 29602 result = Series([2] * 10001).rmod(-1) expected = Series([1] * 10001) tm.assert_series_equal(result, expected)