@@ -1261,6 +1261,150 @@ def test_invalid_other_comp(self, data, comparison_op):
12611261 comparison_op (data , object ())
12621262
12631263
1264+ class TestLogicalOps :
1265+ """Various Series and DataFrame logical ops methods."""
1266+
1267+ def test_kleene_or (self ):
1268+ a = pd .Series ([True ] * 3 + [False ] * 3 + [None ] * 3 , dtype = "boolean[pyarrow]" )
1269+ b = pd .Series ([True , False , None ] * 3 , dtype = "boolean[pyarrow]" )
1270+ result = a | b
1271+ expected = pd .Series (
1272+ [True , True , True , True , False , None , True , None , None ],
1273+ dtype = "boolean[pyarrow]" ,
1274+ )
1275+ tm .assert_series_equal (result , expected )
1276+
1277+ result = b | a
1278+ tm .assert_series_equal (result , expected )
1279+
1280+ # ensure we haven't mutated anything inplace
1281+ tm .assert_series_equal (
1282+ a ,
1283+ pd .Series ([True ] * 3 + [False ] * 3 + [None ] * 3 , dtype = "boolean[pyarrow]" ),
1284+ )
1285+ tm .assert_series_equal (
1286+ b , pd .Series ([True , False , None ] * 3 , dtype = "boolean[pyarrow]" )
1287+ )
1288+
1289+ @pytest .mark .parametrize (
1290+ "other, expected" ,
1291+ [
1292+ (None , [True , None , None ]),
1293+ (pd .NA , [True , None , None ]),
1294+ (True , [True , True , True ]),
1295+ (np .bool_ (True ), [True , True , True ]),
1296+ (False , [True , False , None ]),
1297+ (np .bool_ (False ), [True , False , None ]),
1298+ ],
1299+ )
1300+ def test_kleene_or_scalar (self , other , expected ):
1301+ a = pd .Series ([True , False , None ], dtype = "boolean[pyarrow]" )
1302+ result = a | other
1303+ expected = pd .Series (expected , dtype = "boolean[pyarrow]" )
1304+ tm .assert_series_equal (result , expected )
1305+
1306+ result = other | a
1307+ tm .assert_series_equal (result , expected )
1308+
1309+ # ensure we haven't mutated anything inplace
1310+ tm .assert_series_equal (
1311+ a , pd .Series ([True , False , None ], dtype = "boolean[pyarrow]" )
1312+ )
1313+
1314+ def test_kleene_and (self ):
1315+ a = pd .Series ([True ] * 3 + [False ] * 3 + [None ] * 3 , dtype = "boolean[pyarrow]" )
1316+ b = pd .Series ([True , False , None ] * 3 , dtype = "boolean[pyarrow]" )
1317+ result = a & b
1318+ expected = pd .Series (
1319+ [True , False , None , False , False , False , None , False , None ],
1320+ dtype = "boolean[pyarrow]" ,
1321+ )
1322+ tm .assert_series_equal (result , expected )
1323+
1324+ result = b & a
1325+ tm .assert_series_equal (result , expected )
1326+
1327+ # ensure we haven't mutated anything inplace
1328+ tm .assert_series_equal (
1329+ a ,
1330+ pd .Series ([True ] * 3 + [False ] * 3 + [None ] * 3 , dtype = "boolean[pyarrow]" ),
1331+ )
1332+ tm .assert_series_equal (
1333+ b , pd .Series ([True , False , None ] * 3 , dtype = "boolean[pyarrow]" )
1334+ )
1335+
1336+ @pytest .mark .parametrize (
1337+ "other, expected" ,
1338+ [
1339+ (None , [None , False , None ]),
1340+ (pd .NA , [None , False , None ]),
1341+ (True , [True , False , None ]),
1342+ (False , [False , False , False ]),
1343+ (np .bool_ (True ), [True , False , None ]),
1344+ (np .bool_ (False ), [False , False , False ]),
1345+ ],
1346+ )
1347+ def test_kleene_and_scalar (self , other , expected ):
1348+ a = pd .Series ([True , False , None ], dtype = "boolean[pyarrow]" )
1349+ result = a & other
1350+ expected = pd .Series (expected , dtype = "boolean[pyarrow]" )
1351+ tm .assert_series_equal (result , expected )
1352+
1353+ result = other & a
1354+ tm .assert_series_equal (result , expected )
1355+
1356+ # ensure we haven't mutated anything inplace
1357+ tm .assert_series_equal (
1358+ a , pd .Series ([True , False , None ], dtype = "boolean[pyarrow]" )
1359+ )
1360+
1361+ def test_kleene_xor (self ):
1362+ a = pd .Series ([True ] * 3 + [False ] * 3 + [None ] * 3 , dtype = "boolean[pyarrow]" )
1363+ b = pd .Series ([True , False , None ] * 3 , dtype = "boolean[pyarrow]" )
1364+ result = a ^ b
1365+ expected = pd .Series (
1366+ [False , True , None , True , False , None , None , None , None ],
1367+ dtype = "boolean[pyarrow]" ,
1368+ )
1369+ tm .assert_series_equal (result , expected )
1370+
1371+ result = b ^ a
1372+ tm .assert_series_equal (result , expected )
1373+
1374+ # ensure we haven't mutated anything inplace
1375+ tm .assert_series_equal (
1376+ a ,
1377+ pd .Series ([True ] * 3 + [False ] * 3 + [None ] * 3 , dtype = "boolean[pyarrow]" ),
1378+ )
1379+ tm .assert_series_equal (
1380+ b , pd .Series ([True , False , None ] * 3 , dtype = "boolean[pyarrow]" )
1381+ )
1382+
1383+ @pytest .mark .parametrize (
1384+ "other, expected" ,
1385+ [
1386+ (None , [None , None , None ]),
1387+ (pd .NA , [None , None , None ]),
1388+ (True , [False , True , None ]),
1389+ (np .bool_ (True ), [False , True , None ]),
1390+ (np .bool_ (False ), [True , False , None ]),
1391+ ],
1392+ )
1393+ def test_kleene_xor_scalar (self , other , expected ):
1394+ a = pd .Series ([True , False , None ], dtype = "boolean[pyarrow]" )
1395+ result = a ^ other
1396+ expected = pd .Series (expected , dtype = "boolean[pyarrow]" )
1397+ tm .assert_series_equal (result , expected )
1398+
1399+ result = other ^ a
1400+ tm .assert_series_equal (result , expected )
1401+
1402+ # ensure we haven't mutated anything inplace
1403+ tm .assert_series_equal (
1404+ a , pd .Series ([True , False , None ], dtype = "boolean[pyarrow]" )
1405+ )
1406+
1407+
12641408def test_arrowdtype_construct_from_string_type_with_unsupported_parameters ():
12651409 with pytest .raises (NotImplementedError , match = "Passing pyarrow type" ):
12661410 ArrowDtype .construct_from_string ("not_a_real_dype[s, tz=UTC][pyarrow]" )
0 commit comments