123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- from datetime import datetime
- import operator
- import numpy as np
- import pytest
- from pandas import (
- DataFrame,
- Index,
- Series,
- bdate_range,
- )
- import pandas._testing as tm
- from pandas.core import ops
- class TestSeriesLogicalOps:
- @pytest.mark.parametrize("bool_op", [operator.and_, operator.or_, operator.xor])
- def test_bool_operators_with_nas(self, bool_op):
- # boolean &, |, ^ should work with object arrays and propagate NAs
- ser = Series(bdate_range("1/1/2000", periods=10), dtype=object)
- ser[::2] = np.nan
- mask = ser.isna()
- filled = ser.fillna(ser[0])
- result = bool_op(ser < ser[9], ser > ser[3])
- expected = bool_op(filled < filled[9], filled > filled[3])
- expected[mask] = False
- tm.assert_series_equal(result, expected)
- def test_logical_operators_bool_dtype_with_empty(self):
- # GH#9016: support bitwise op for integer types
- index = list("bca")
- s_tft = Series([True, False, True], index=index)
- s_fff = Series([False, False, False], index=index)
- s_empty = Series([], dtype=object)
- res = s_tft & s_empty
- expected = s_fff
- tm.assert_series_equal(res, expected)
- res = s_tft | s_empty
- expected = s_tft
- tm.assert_series_equal(res, expected)
- def test_logical_operators_int_dtype_with_int_dtype(self):
- # GH#9016: support bitwise op for integer types
- s_0123 = Series(range(4), dtype="int64")
- s_3333 = Series([3] * 4)
- s_4444 = Series([4] * 4)
- res = s_0123 & s_3333
- expected = Series(range(4), dtype="int64")
- tm.assert_series_equal(res, expected)
- res = s_0123 | s_4444
- expected = Series(range(4, 8), dtype="int64")
- tm.assert_series_equal(res, expected)
- s_1111 = Series([1] * 4, dtype="int8")
- res = s_0123 & s_1111
- expected = Series([0, 1, 0, 1], dtype="int64")
- tm.assert_series_equal(res, expected)
- res = s_0123.astype(np.int16) | s_1111.astype(np.int32)
- expected = Series([1, 1, 3, 3], dtype="int32")
- tm.assert_series_equal(res, expected)
- def test_logical_operators_int_dtype_with_int_scalar(self):
- # GH#9016: support bitwise op for integer types
- s_0123 = Series(range(4), dtype="int64")
- res = s_0123 & 0
- expected = Series([0] * 4)
- tm.assert_series_equal(res, expected)
- res = s_0123 & 1
- expected = Series([0, 1, 0, 1])
- tm.assert_series_equal(res, expected)
- def test_logical_operators_int_dtype_with_float(self):
- # GH#9016: support bitwise op for integer types
- s_0123 = Series(range(4), dtype="int64")
- msg = "Cannot perform.+with a dtyped.+array and scalar of type"
- with pytest.raises(TypeError, match=msg):
- s_0123 & np.NaN
- with pytest.raises(TypeError, match=msg):
- s_0123 & 3.14
- msg = "unsupported operand type.+for &:"
- with pytest.raises(TypeError, match=msg):
- s_0123 & [0.1, 4, 3.14, 2]
- with pytest.raises(TypeError, match=msg):
- s_0123 & np.array([0.1, 4, 3.14, 2])
- with pytest.raises(TypeError, match=msg):
- s_0123 & Series([0.1, 4, -3.14, 2])
- def test_logical_operators_int_dtype_with_str(self):
- s_1111 = Series([1] * 4, dtype="int8")
- msg = "Cannot perform 'and_' with a dtyped.+array and scalar of type"
- with pytest.raises(TypeError, match=msg):
- s_1111 & "a"
- with pytest.raises(TypeError, match="unsupported operand.+for &"):
- s_1111 & ["a", "b", "c", "d"]
- def test_logical_operators_int_dtype_with_bool(self):
- # GH#9016: support bitwise op for integer types
- s_0123 = Series(range(4), dtype="int64")
- expected = Series([False] * 4)
- result = s_0123 & False
- tm.assert_series_equal(result, expected)
- result = s_0123 & [False]
- tm.assert_series_equal(result, expected)
- result = s_0123 & (False,)
- tm.assert_series_equal(result, expected)
- result = s_0123 ^ False
- expected = Series([False, True, True, True])
- tm.assert_series_equal(result, expected)
- def test_logical_operators_int_dtype_with_object(self):
- # GH#9016: support bitwise op for integer types
- s_0123 = Series(range(4), dtype="int64")
- result = s_0123 & Series([False, np.NaN, False, False])
- expected = Series([False] * 4)
- tm.assert_series_equal(result, expected)
- s_abNd = Series(["a", "b", np.NaN, "d"])
- with pytest.raises(TypeError, match="unsupported.* 'int' and 'str'"):
- s_0123 & s_abNd
- def test_logical_operators_bool_dtype_with_int(self):
- index = list("bca")
- s_tft = Series([True, False, True], index=index)
- s_fff = Series([False, False, False], index=index)
- res = s_tft & 0
- expected = s_fff
- tm.assert_series_equal(res, expected)
- res = s_tft & 1
- expected = s_tft
- tm.assert_series_equal(res, expected)
- def test_logical_ops_bool_dtype_with_ndarray(self):
- # make sure we operate on ndarray the same as Series
- left = Series([True, True, True, False, True])
- right = [True, False, None, True, np.nan]
- expected = Series([True, False, False, False, False])
- result = left & right
- tm.assert_series_equal(result, expected)
- result = left & np.array(right)
- tm.assert_series_equal(result, expected)
- result = left & Index(right)
- tm.assert_series_equal(result, expected)
- result = left & Series(right)
- tm.assert_series_equal(result, expected)
- expected = Series([True, True, True, True, True])
- result = left | right
- tm.assert_series_equal(result, expected)
- result = left | np.array(right)
- tm.assert_series_equal(result, expected)
- result = left | Index(right)
- tm.assert_series_equal(result, expected)
- result = left | Series(right)
- tm.assert_series_equal(result, expected)
- expected = Series([False, True, True, True, True])
- result = left ^ right
- tm.assert_series_equal(result, expected)
- result = left ^ np.array(right)
- tm.assert_series_equal(result, expected)
- result = left ^ Index(right)
- tm.assert_series_equal(result, expected)
- result = left ^ Series(right)
- tm.assert_series_equal(result, expected)
- def test_logical_operators_int_dtype_with_bool_dtype_and_reindex(self):
- # GH#9016: support bitwise op for integer types
- # with non-matching indexes, logical operators will cast to object
- # before operating
- index = list("bca")
- s_tft = Series([True, False, True], index=index)
- s_tft = Series([True, False, True], index=index)
- s_tff = Series([True, False, False], index=index)
- s_0123 = Series(range(4), dtype="int64")
- # s_0123 will be all false now because of reindexing like s_tft
- expected = Series([False] * 7, index=[0, 1, 2, 3, "a", "b", "c"])
- result = s_tft & s_0123
- tm.assert_series_equal(result, expected)
- expected = Series([False] * 7, index=[0, 1, 2, 3, "a", "b", "c"])
- result = s_0123 & s_tft
- tm.assert_series_equal(result, expected)
- s_a0b1c0 = Series([1], list("b"))
- res = s_tft & s_a0b1c0
- expected = s_tff.reindex(list("abc"))
- tm.assert_series_equal(res, expected)
- res = s_tft | s_a0b1c0
- expected = s_tft.reindex(list("abc"))
- tm.assert_series_equal(res, expected)
- def test_scalar_na_logical_ops_corners(self):
- s = Series([2, 3, 4, 5, 6, 7, 8, 9, 10])
- msg = "Cannot perform.+with a dtyped.+array and scalar of type"
- with pytest.raises(TypeError, match=msg):
- s & datetime(2005, 1, 1)
- s = Series([2, 3, 4, 5, 6, 7, 8, 9, datetime(2005, 1, 1)])
- s[::2] = np.nan
- expected = Series(True, index=s.index)
- expected[::2] = False
- result = s & list(s)
- tm.assert_series_equal(result, expected)
- def test_scalar_na_logical_ops_corners_aligns(self):
- s = Series([2, 3, 4, 5, 6, 7, 8, 9, datetime(2005, 1, 1)])
- s[::2] = np.nan
- d = DataFrame({"A": s})
- expected = DataFrame(False, index=range(9), columns=["A"] + list(range(9)))
- result = s & d
- tm.assert_frame_equal(result, expected)
- result = d & s
- tm.assert_frame_equal(result, expected)
- @pytest.mark.parametrize("op", [operator.and_, operator.or_, operator.xor])
- def test_logical_ops_with_index(self, op):
- # GH#22092, GH#19792
- ser = Series([True, True, False, False])
- idx1 = Index([True, False, True, False])
- idx2 = Index([1, 0, 1, 0])
- expected = Series([op(ser[n], idx1[n]) for n in range(len(ser))])
- result = op(ser, idx1)
- tm.assert_series_equal(result, expected)
- expected = Series([op(ser[n], idx2[n]) for n in range(len(ser))], dtype=bool)
- result = op(ser, idx2)
- tm.assert_series_equal(result, expected)
- def test_reversed_xor_with_index_returns_series(self):
- # GH#22092, GH#19792 pre-2.0 these were aliased to setops
- ser = Series([True, True, False, False])
- idx1 = Index(
- [True, False, True, False], dtype=object
- ) # TODO: raises if bool-dtype
- idx2 = Index([1, 0, 1, 0])
- expected = Series([False, True, True, False])
- result = idx1 ^ ser
- tm.assert_series_equal(result, expected)
- result = idx2 ^ ser
- tm.assert_series_equal(result, expected)
- @pytest.mark.parametrize(
- "op",
- [
- ops.rand_,
- ops.ror_,
- ],
- )
- def test_reversed_logical_op_with_index_returns_series(self, op):
- # GH#22092, GH#19792
- ser = Series([True, True, False, False])
- idx1 = Index([True, False, True, False])
- idx2 = Index([1, 0, 1, 0])
- expected = Series(op(idx1.values, ser.values))
- result = op(ser, idx1)
- tm.assert_series_equal(result, expected)
- expected = op(ser, Series(idx2))
- result = op(ser, idx2)
- tm.assert_series_equal(result, expected)
- @pytest.mark.parametrize(
- "op, expected",
- [
- (ops.rand_, Series([False, False])),
- (ops.ror_, Series([True, True])),
- (ops.rxor, Series([True, True])),
- ],
- )
- def test_reverse_ops_with_index(self, op, expected):
- # https://github.com/pandas-dev/pandas/pull/23628
- # multi-set Index ops are buggy, so let's avoid duplicates...
- # GH#49503
- ser = Series([True, False])
- idx = Index([False, True])
- result = op(ser, idx)
- tm.assert_series_equal(result, expected)
- def test_logical_ops_label_based(self):
- # GH#4947
- # logical ops should be label based
- a = Series([True, False, True], list("bca"))
- b = Series([False, True, False], list("abc"))
- expected = Series([False, True, False], list("abc"))
- result = a & b
- tm.assert_series_equal(result, expected)
- expected = Series([True, True, False], list("abc"))
- result = a | b
- tm.assert_series_equal(result, expected)
- expected = Series([True, False, False], list("abc"))
- result = a ^ b
- tm.assert_series_equal(result, expected)
- # rhs is bigger
- a = Series([True, False, True], list("bca"))
- b = Series([False, True, False, True], list("abcd"))
- expected = Series([False, True, False, False], list("abcd"))
- result = a & b
- tm.assert_series_equal(result, expected)
- expected = Series([True, True, False, False], list("abcd"))
- result = a | b
- tm.assert_series_equal(result, expected)
- # filling
- # vs empty
- empty = Series([], dtype=object)
- result = a & empty.copy()
- expected = Series([False, False, False], list("bca"))
- tm.assert_series_equal(result, expected)
- result = a | empty.copy()
- expected = Series([True, False, True], list("bca"))
- tm.assert_series_equal(result, expected)
- # vs non-matching
- result = a & Series([1], ["z"])
- expected = Series([False, False, False, False], list("abcz"))
- tm.assert_series_equal(result, expected)
- result = a | Series([1], ["z"])
- expected = Series([True, True, False, False], list("abcz"))
- tm.assert_series_equal(result, expected)
- # identity
- # we would like s[s|e] == s to hold for any e, whether empty or not
- for e in [
- empty.copy(),
- Series([1], ["z"]),
- Series(np.nan, b.index),
- Series(np.nan, a.index),
- ]:
- result = a[a | e]
- tm.assert_series_equal(result, a[a])
- for e in [Series(["z"])]:
- result = a[a | e]
- tm.assert_series_equal(result, a[a])
- # vs scalars
- index = list("bca")
- t = Series([True, False, True])
- for v in [True, 1, 2]:
- result = Series([True, False, True], index=index) | v
- expected = Series([True, True, True], index=index)
- tm.assert_series_equal(result, expected)
- msg = "Cannot perform.+with a dtyped.+array and scalar of type"
- for v in [np.nan, "foo"]:
- with pytest.raises(TypeError, match=msg):
- t | v
- for v in [False, 0]:
- result = Series([True, False, True], index=index) | v
- expected = Series([True, False, True], index=index)
- tm.assert_series_equal(result, expected)
- for v in [True, 1]:
- result = Series([True, False, True], index=index) & v
- expected = Series([True, False, True], index=index)
- tm.assert_series_equal(result, expected)
- for v in [False, 0]:
- result = Series([True, False, True], index=index) & v
- expected = Series([False, False, False], index=index)
- tm.assert_series_equal(result, expected)
- msg = "Cannot perform.+with a dtyped.+array and scalar of type"
- for v in [np.nan]:
- with pytest.raises(TypeError, match=msg):
- t & v
- def test_logical_ops_df_compat(self):
- # GH#1134
- s1 = Series([True, False, True], index=list("ABC"), name="x")
- s2 = Series([True, True, False], index=list("ABD"), name="x")
- exp = Series([True, False, False, False], index=list("ABCD"), name="x")
- tm.assert_series_equal(s1 & s2, exp)
- tm.assert_series_equal(s2 & s1, exp)
- # True | np.nan => True
- exp_or1 = Series([True, True, True, False], index=list("ABCD"), name="x")
- tm.assert_series_equal(s1 | s2, exp_or1)
- # np.nan | True => np.nan, filled with False
- exp_or = Series([True, True, False, False], index=list("ABCD"), name="x")
- tm.assert_series_equal(s2 | s1, exp_or)
- # DataFrame doesn't fill nan with False
- tm.assert_frame_equal(s1.to_frame() & s2.to_frame(), exp.to_frame())
- tm.assert_frame_equal(s2.to_frame() & s1.to_frame(), exp.to_frame())
- exp = DataFrame({"x": [True, True, np.nan, np.nan]}, index=list("ABCD"))
- tm.assert_frame_equal(s1.to_frame() | s2.to_frame(), exp_or1.to_frame())
- tm.assert_frame_equal(s2.to_frame() | s1.to_frame(), exp_or.to_frame())
- # different length
- s3 = Series([True, False, True], index=list("ABC"), name="x")
- s4 = Series([True, True, True, True], index=list("ABCD"), name="x")
- exp = Series([True, False, True, False], index=list("ABCD"), name="x")
- tm.assert_series_equal(s3 & s4, exp)
- tm.assert_series_equal(s4 & s3, exp)
- # np.nan | True => np.nan, filled with False
- exp_or1 = Series([True, True, True, False], index=list("ABCD"), name="x")
- tm.assert_series_equal(s3 | s4, exp_or1)
- # True | np.nan => True
- exp_or = Series([True, True, True, True], index=list("ABCD"), name="x")
- tm.assert_series_equal(s4 | s3, exp_or)
- tm.assert_frame_equal(s3.to_frame() & s4.to_frame(), exp.to_frame())
- tm.assert_frame_equal(s4.to_frame() & s3.to_frame(), exp.to_frame())
- tm.assert_frame_equal(s3.to_frame() | s4.to_frame(), exp_or1.to_frame())
- tm.assert_frame_equal(s4.to_frame() | s3.to_frame(), exp_or.to_frame())
|