Skip to content

Commit 698e460

Browse files
committed
Fixing for OR operations
1 parent 6583fab commit 698e460

File tree

2 files changed

+26
-14
lines changed

2 files changed

+26
-14
lines changed

pandas/core/ops/array_ops.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -462,25 +462,36 @@ def alignOutputWithKleene(left, right, op):
462462
result = np.zeros_like(res_values, dtype=bool) # all False, bool dtype
463463
return result
464464

465-
if op.__name__ in {"and_", "rand_"}:
465+
elif op.__name__ in {"and_", "rand_"}:
466466
res_values[:] = lvalues & rvalues
467-
mask[:] = (
468-
(left_mask & rvalues) | (right_mask & lvalues) | (left_mask & right_mask)
469-
)
467+
mask[:] = False
468+
469+
# Case 1: True & NA → NA
470+
mask[lvalues & right_mask] = True
471+
mask[rvalues & left_mask] = True
472+
473+
# Case 2: False & NA → False (keep False, no NA)
474+
# Case 3: NA & NA → NA
475+
mask[left_mask & right_mask] = True
470476

471477
# --- OR logic ---
472478
elif op.__name__ in {"or_", "ror_"}:
473479
res_values[:] = lvalues | rvalues
474-
# Unknown only if both sides are NA
475-
mask[:] = left_mask & right_mask
476480

477-
# Handle cases where NA OR False → False, NA OR True → True
478-
# Pandas convention: np.nan | False -> False, np.nan | True -> True
479-
res_values[left_mask & ~rvalues] = False
480-
res_values[right_mask & ~lvalues] = False
481+
# Start fresh mask
482+
mask[:] = False
483+
484+
# Case 1: NA | True → True (keep, no NA)
481485
res_values[left_mask & rvalues] = True
482486
res_values[right_mask & lvalues] = True
483487

488+
# Case 2: NA | False → NA
489+
mask[left_mask & ~rvalues] = True
490+
mask[right_mask & ~lvalues] = True
491+
492+
# Case 3: NA | NA → NA
493+
mask[left_mask & right_mask] = True
494+
484495
# --- XOR logic ---
485496
elif op.__name__ in {"xor", "rxor"}:
486497
res_values[:] = lvalues ^ rvalues

pandas/tests/series/test_logical_ops.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ def test_logical_operators_bool_dtype_with_empty(self):
4646
tm.assert_series_equal(res, expected)
4747

4848
res = s_tft | s_empty
49-
expected = s_tft.sort_index()
49+
# changed the test case output to align with kleene principle
50+
expected = Series([True, np.nan, True], index=index).sort_index()
5051
tm.assert_series_equal(res, expected)
5152

5253
def test_logical_operators_int_dtype_with_int_dtype(self):
@@ -389,7 +390,7 @@ def test_logical_ops_label_based(self, using_infer_string):
389390
tm.assert_series_equal(result, expected)
390391

391392
result = a | empty
392-
expected = Series([True, True, False], list("abc"))
393+
expected = Series([True, True, np.nan], list("abc"))
393394
tm.assert_series_equal(result, expected)
394395

395396
# vs non-matching
@@ -469,10 +470,10 @@ def test_logical_ops_df_compat(self):
469470
tm.assert_series_equal(s2 & s1, exp)
470471

471472
# True | np.nan => True
472-
exp_or1 = Series([True, True, True, False], index=list("ABCD"), name="x")
473+
exp_or1 = Series([True, True, True, np.nan], index=list("ABCD"), name="x")
473474
tm.assert_series_equal(s1 | s2, exp_or1)
474475
# np.nan | True => True (should be)
475-
exp_or = Series([True, True, True, False], index=list("ABCD"), name="x")
476+
exp_or = Series([True, True, True, np.nan], index=list("ABCD"), name="x")
476477
tm.assert_series_equal(s2 | s1, exp_or)
477478

478479
# DataFrame doesn't fill nan with False

0 commit comments

Comments
 (0)