test_logical_ops.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. from datetime import datetime
  2. import operator
  3. import numpy as np
  4. import pytest
  5. from pandas import (
  6. DataFrame,
  7. Index,
  8. Series,
  9. bdate_range,
  10. )
  11. import pandas._testing as tm
  12. from pandas.core import ops
  13. class TestSeriesLogicalOps:
  14. @pytest.mark.parametrize("bool_op", [operator.and_, operator.or_, operator.xor])
  15. def test_bool_operators_with_nas(self, bool_op):
  16. # boolean &, |, ^ should work with object arrays and propagate NAs
  17. ser = Series(bdate_range("1/1/2000", periods=10), dtype=object)
  18. ser[::2] = np.nan
  19. mask = ser.isna()
  20. filled = ser.fillna(ser[0])
  21. result = bool_op(ser < ser[9], ser > ser[3])
  22. expected = bool_op(filled < filled[9], filled > filled[3])
  23. expected[mask] = False
  24. tm.assert_series_equal(result, expected)
  25. def test_logical_operators_bool_dtype_with_empty(self):
  26. # GH#9016: support bitwise op for integer types
  27. index = list("bca")
  28. s_tft = Series([True, False, True], index=index)
  29. s_fff = Series([False, False, False], index=index)
  30. s_empty = Series([], dtype=object)
  31. res = s_tft & s_empty
  32. expected = s_fff
  33. tm.assert_series_equal(res, expected)
  34. res = s_tft | s_empty
  35. expected = s_tft
  36. tm.assert_series_equal(res, expected)
  37. def test_logical_operators_int_dtype_with_int_dtype(self):
  38. # GH#9016: support bitwise op for integer types
  39. s_0123 = Series(range(4), dtype="int64")
  40. s_3333 = Series([3] * 4)
  41. s_4444 = Series([4] * 4)
  42. res = s_0123 & s_3333
  43. expected = Series(range(4), dtype="int64")
  44. tm.assert_series_equal(res, expected)
  45. res = s_0123 | s_4444
  46. expected = Series(range(4, 8), dtype="int64")
  47. tm.assert_series_equal(res, expected)
  48. s_1111 = Series([1] * 4, dtype="int8")
  49. res = s_0123 & s_1111
  50. expected = Series([0, 1, 0, 1], dtype="int64")
  51. tm.assert_series_equal(res, expected)
  52. res = s_0123.astype(np.int16) | s_1111.astype(np.int32)
  53. expected = Series([1, 1, 3, 3], dtype="int32")
  54. tm.assert_series_equal(res, expected)
  55. def test_logical_operators_int_dtype_with_int_scalar(self):
  56. # GH#9016: support bitwise op for integer types
  57. s_0123 = Series(range(4), dtype="int64")
  58. res = s_0123 & 0
  59. expected = Series([0] * 4)
  60. tm.assert_series_equal(res, expected)
  61. res = s_0123 & 1
  62. expected = Series([0, 1, 0, 1])
  63. tm.assert_series_equal(res, expected)
  64. def test_logical_operators_int_dtype_with_float(self):
  65. # GH#9016: support bitwise op for integer types
  66. s_0123 = Series(range(4), dtype="int64")
  67. msg = "Cannot perform.+with a dtyped.+array and scalar of type"
  68. with pytest.raises(TypeError, match=msg):
  69. s_0123 & np.NaN
  70. with pytest.raises(TypeError, match=msg):
  71. s_0123 & 3.14
  72. msg = "unsupported operand type.+for &:"
  73. with pytest.raises(TypeError, match=msg):
  74. s_0123 & [0.1, 4, 3.14, 2]
  75. with pytest.raises(TypeError, match=msg):
  76. s_0123 & np.array([0.1, 4, 3.14, 2])
  77. with pytest.raises(TypeError, match=msg):
  78. s_0123 & Series([0.1, 4, -3.14, 2])
  79. def test_logical_operators_int_dtype_with_str(self):
  80. s_1111 = Series([1] * 4, dtype="int8")
  81. msg = "Cannot perform 'and_' with a dtyped.+array and scalar of type"
  82. with pytest.raises(TypeError, match=msg):
  83. s_1111 & "a"
  84. with pytest.raises(TypeError, match="unsupported operand.+for &"):
  85. s_1111 & ["a", "b", "c", "d"]
  86. def test_logical_operators_int_dtype_with_bool(self):
  87. # GH#9016: support bitwise op for integer types
  88. s_0123 = Series(range(4), dtype="int64")
  89. expected = Series([False] * 4)
  90. result = s_0123 & False
  91. tm.assert_series_equal(result, expected)
  92. result = s_0123 & [False]
  93. tm.assert_series_equal(result, expected)
  94. result = s_0123 & (False,)
  95. tm.assert_series_equal(result, expected)
  96. result = s_0123 ^ False
  97. expected = Series([False, True, True, True])
  98. tm.assert_series_equal(result, expected)
  99. def test_logical_operators_int_dtype_with_object(self):
  100. # GH#9016: support bitwise op for integer types
  101. s_0123 = Series(range(4), dtype="int64")
  102. result = s_0123 & Series([False, np.NaN, False, False])
  103. expected = Series([False] * 4)
  104. tm.assert_series_equal(result, expected)
  105. s_abNd = Series(["a", "b", np.NaN, "d"])
  106. with pytest.raises(TypeError, match="unsupported.* 'int' and 'str'"):
  107. s_0123 & s_abNd
  108. def test_logical_operators_bool_dtype_with_int(self):
  109. index = list("bca")
  110. s_tft = Series([True, False, True], index=index)
  111. s_fff = Series([False, False, False], index=index)
  112. res = s_tft & 0
  113. expected = s_fff
  114. tm.assert_series_equal(res, expected)
  115. res = s_tft & 1
  116. expected = s_tft
  117. tm.assert_series_equal(res, expected)
  118. def test_logical_ops_bool_dtype_with_ndarray(self):
  119. # make sure we operate on ndarray the same as Series
  120. left = Series([True, True, True, False, True])
  121. right = [True, False, None, True, np.nan]
  122. expected = Series([True, False, False, False, False])
  123. result = left & right
  124. tm.assert_series_equal(result, expected)
  125. result = left & np.array(right)
  126. tm.assert_series_equal(result, expected)
  127. result = left & Index(right)
  128. tm.assert_series_equal(result, expected)
  129. result = left & Series(right)
  130. tm.assert_series_equal(result, expected)
  131. expected = Series([True, True, True, True, True])
  132. result = left | right
  133. tm.assert_series_equal(result, expected)
  134. result = left | np.array(right)
  135. tm.assert_series_equal(result, expected)
  136. result = left | Index(right)
  137. tm.assert_series_equal(result, expected)
  138. result = left | Series(right)
  139. tm.assert_series_equal(result, expected)
  140. expected = Series([False, True, True, True, True])
  141. result = left ^ right
  142. tm.assert_series_equal(result, expected)
  143. result = left ^ np.array(right)
  144. tm.assert_series_equal(result, expected)
  145. result = left ^ Index(right)
  146. tm.assert_series_equal(result, expected)
  147. result = left ^ Series(right)
  148. tm.assert_series_equal(result, expected)
  149. def test_logical_operators_int_dtype_with_bool_dtype_and_reindex(self):
  150. # GH#9016: support bitwise op for integer types
  151. # with non-matching indexes, logical operators will cast to object
  152. # before operating
  153. index = list("bca")
  154. s_tft = Series([True, False, True], index=index)
  155. s_tft = Series([True, False, True], index=index)
  156. s_tff = Series([True, False, False], index=index)
  157. s_0123 = Series(range(4), dtype="int64")
  158. # s_0123 will be all false now because of reindexing like s_tft
  159. expected = Series([False] * 7, index=[0, 1, 2, 3, "a", "b", "c"])
  160. result = s_tft & s_0123
  161. tm.assert_series_equal(result, expected)
  162. expected = Series([False] * 7, index=[0, 1, 2, 3, "a", "b", "c"])
  163. result = s_0123 & s_tft
  164. tm.assert_series_equal(result, expected)
  165. s_a0b1c0 = Series([1], list("b"))
  166. res = s_tft & s_a0b1c0
  167. expected = s_tff.reindex(list("abc"))
  168. tm.assert_series_equal(res, expected)
  169. res = s_tft | s_a0b1c0
  170. expected = s_tft.reindex(list("abc"))
  171. tm.assert_series_equal(res, expected)
  172. def test_scalar_na_logical_ops_corners(self):
  173. s = Series([2, 3, 4, 5, 6, 7, 8, 9, 10])
  174. msg = "Cannot perform.+with a dtyped.+array and scalar of type"
  175. with pytest.raises(TypeError, match=msg):
  176. s & datetime(2005, 1, 1)
  177. s = Series([2, 3, 4, 5, 6, 7, 8, 9, datetime(2005, 1, 1)])
  178. s[::2] = np.nan
  179. expected = Series(True, index=s.index)
  180. expected[::2] = False
  181. result = s & list(s)
  182. tm.assert_series_equal(result, expected)
  183. def test_scalar_na_logical_ops_corners_aligns(self):
  184. s = Series([2, 3, 4, 5, 6, 7, 8, 9, datetime(2005, 1, 1)])
  185. s[::2] = np.nan
  186. d = DataFrame({"A": s})
  187. expected = DataFrame(False, index=range(9), columns=["A"] + list(range(9)))
  188. result = s & d
  189. tm.assert_frame_equal(result, expected)
  190. result = d & s
  191. tm.assert_frame_equal(result, expected)
  192. @pytest.mark.parametrize("op", [operator.and_, operator.or_, operator.xor])
  193. def test_logical_ops_with_index(self, op):
  194. # GH#22092, GH#19792
  195. ser = Series([True, True, False, False])
  196. idx1 = Index([True, False, True, False])
  197. idx2 = Index([1, 0, 1, 0])
  198. expected = Series([op(ser[n], idx1[n]) for n in range(len(ser))])
  199. result = op(ser, idx1)
  200. tm.assert_series_equal(result, expected)
  201. expected = Series([op(ser[n], idx2[n]) for n in range(len(ser))], dtype=bool)
  202. result = op(ser, idx2)
  203. tm.assert_series_equal(result, expected)
  204. def test_reversed_xor_with_index_returns_series(self):
  205. # GH#22092, GH#19792 pre-2.0 these were aliased to setops
  206. ser = Series([True, True, False, False])
  207. idx1 = Index(
  208. [True, False, True, False], dtype=object
  209. ) # TODO: raises if bool-dtype
  210. idx2 = Index([1, 0, 1, 0])
  211. expected = Series([False, True, True, False])
  212. result = idx1 ^ ser
  213. tm.assert_series_equal(result, expected)
  214. result = idx2 ^ ser
  215. tm.assert_series_equal(result, expected)
  216. @pytest.mark.parametrize(
  217. "op",
  218. [
  219. ops.rand_,
  220. ops.ror_,
  221. ],
  222. )
  223. def test_reversed_logical_op_with_index_returns_series(self, op):
  224. # GH#22092, GH#19792
  225. ser = Series([True, True, False, False])
  226. idx1 = Index([True, False, True, False])
  227. idx2 = Index([1, 0, 1, 0])
  228. expected = Series(op(idx1.values, ser.values))
  229. result = op(ser, idx1)
  230. tm.assert_series_equal(result, expected)
  231. expected = op(ser, Series(idx2))
  232. result = op(ser, idx2)
  233. tm.assert_series_equal(result, expected)
  234. @pytest.mark.parametrize(
  235. "op, expected",
  236. [
  237. (ops.rand_, Series([False, False])),
  238. (ops.ror_, Series([True, True])),
  239. (ops.rxor, Series([True, True])),
  240. ],
  241. )
  242. def test_reverse_ops_with_index(self, op, expected):
  243. # https://github.com/pandas-dev/pandas/pull/23628
  244. # multi-set Index ops are buggy, so let's avoid duplicates...
  245. # GH#49503
  246. ser = Series([True, False])
  247. idx = Index([False, True])
  248. result = op(ser, idx)
  249. tm.assert_series_equal(result, expected)
  250. def test_logical_ops_label_based(self):
  251. # GH#4947
  252. # logical ops should be label based
  253. a = Series([True, False, True], list("bca"))
  254. b = Series([False, True, False], list("abc"))
  255. expected = Series([False, True, False], list("abc"))
  256. result = a & b
  257. tm.assert_series_equal(result, expected)
  258. expected = Series([True, True, False], list("abc"))
  259. result = a | b
  260. tm.assert_series_equal(result, expected)
  261. expected = Series([True, False, False], list("abc"))
  262. result = a ^ b
  263. tm.assert_series_equal(result, expected)
  264. # rhs is bigger
  265. a = Series([True, False, True], list("bca"))
  266. b = Series([False, True, False, True], list("abcd"))
  267. expected = Series([False, True, False, False], list("abcd"))
  268. result = a & b
  269. tm.assert_series_equal(result, expected)
  270. expected = Series([True, True, False, False], list("abcd"))
  271. result = a | b
  272. tm.assert_series_equal(result, expected)
  273. # filling
  274. # vs empty
  275. empty = Series([], dtype=object)
  276. result = a & empty.copy()
  277. expected = Series([False, False, False], list("bca"))
  278. tm.assert_series_equal(result, expected)
  279. result = a | empty.copy()
  280. expected = Series([True, False, True], list("bca"))
  281. tm.assert_series_equal(result, expected)
  282. # vs non-matching
  283. result = a & Series([1], ["z"])
  284. expected = Series([False, False, False, False], list("abcz"))
  285. tm.assert_series_equal(result, expected)
  286. result = a | Series([1], ["z"])
  287. expected = Series([True, True, False, False], list("abcz"))
  288. tm.assert_series_equal(result, expected)
  289. # identity
  290. # we would like s[s|e] == s to hold for any e, whether empty or not
  291. for e in [
  292. empty.copy(),
  293. Series([1], ["z"]),
  294. Series(np.nan, b.index),
  295. Series(np.nan, a.index),
  296. ]:
  297. result = a[a | e]
  298. tm.assert_series_equal(result, a[a])
  299. for e in [Series(["z"])]:
  300. result = a[a | e]
  301. tm.assert_series_equal(result, a[a])
  302. # vs scalars
  303. index = list("bca")
  304. t = Series([True, False, True])
  305. for v in [True, 1, 2]:
  306. result = Series([True, False, True], index=index) | v
  307. expected = Series([True, True, True], index=index)
  308. tm.assert_series_equal(result, expected)
  309. msg = "Cannot perform.+with a dtyped.+array and scalar of type"
  310. for v in [np.nan, "foo"]:
  311. with pytest.raises(TypeError, match=msg):
  312. t | v
  313. for v in [False, 0]:
  314. result = Series([True, False, True], index=index) | v
  315. expected = Series([True, False, True], index=index)
  316. tm.assert_series_equal(result, expected)
  317. for v in [True, 1]:
  318. result = Series([True, False, True], index=index) & v
  319. expected = Series([True, False, True], index=index)
  320. tm.assert_series_equal(result, expected)
  321. for v in [False, 0]:
  322. result = Series([True, False, True], index=index) & v
  323. expected = Series([False, False, False], index=index)
  324. tm.assert_series_equal(result, expected)
  325. msg = "Cannot perform.+with a dtyped.+array and scalar of type"
  326. for v in [np.nan]:
  327. with pytest.raises(TypeError, match=msg):
  328. t & v
  329. def test_logical_ops_df_compat(self):
  330. # GH#1134
  331. s1 = Series([True, False, True], index=list("ABC"), name="x")
  332. s2 = Series([True, True, False], index=list("ABD"), name="x")
  333. exp = Series([True, False, False, False], index=list("ABCD"), name="x")
  334. tm.assert_series_equal(s1 & s2, exp)
  335. tm.assert_series_equal(s2 & s1, exp)
  336. # True | np.nan => True
  337. exp_or1 = Series([True, True, True, False], index=list("ABCD"), name="x")
  338. tm.assert_series_equal(s1 | s2, exp_or1)
  339. # np.nan | True => np.nan, filled with False
  340. exp_or = Series([True, True, False, False], index=list("ABCD"), name="x")
  341. tm.assert_series_equal(s2 | s1, exp_or)
  342. # DataFrame doesn't fill nan with False
  343. tm.assert_frame_equal(s1.to_frame() & s2.to_frame(), exp.to_frame())
  344. tm.assert_frame_equal(s2.to_frame() & s1.to_frame(), exp.to_frame())
  345. exp = DataFrame({"x": [True, True, np.nan, np.nan]}, index=list("ABCD"))
  346. tm.assert_frame_equal(s1.to_frame() | s2.to_frame(), exp_or1.to_frame())
  347. tm.assert_frame_equal(s2.to_frame() | s1.to_frame(), exp_or.to_frame())
  348. # different length
  349. s3 = Series([True, False, True], index=list("ABC"), name="x")
  350. s4 = Series([True, True, True, True], index=list("ABCD"), name="x")
  351. exp = Series([True, False, True, False], index=list("ABCD"), name="x")
  352. tm.assert_series_equal(s3 & s4, exp)
  353. tm.assert_series_equal(s4 & s3, exp)
  354. # np.nan | True => np.nan, filled with False
  355. exp_or1 = Series([True, True, True, False], index=list("ABCD"), name="x")
  356. tm.assert_series_equal(s3 | s4, exp_or1)
  357. # True | np.nan => True
  358. exp_or = Series([True, True, True, True], index=list("ABCD"), name="x")
  359. tm.assert_series_equal(s4 | s3, exp_or)
  360. tm.assert_frame_equal(s3.to_frame() & s4.to_frame(), exp.to_frame())
  361. tm.assert_frame_equal(s4.to_frame() & s3.to_frame(), exp.to_frame())
  362. tm.assert_frame_equal(s3.to_frame() | s4.to_frame(), exp_or1.to_frame())
  363. tm.assert_frame_equal(s4.to_frame() | s3.to_frame(), exp_or.to_frame())