Skip to content

Commit 2dac793

Browse files
dhirschfeldjreback
authored andcommitted
BUG: Fixed handling of boolean indexing with 2-d ndarrays (pandas-dev#18645)
1 parent ad043d1 commit 2dac793

File tree

3 files changed

+35
-7
lines changed

3 files changed

+35
-7
lines changed

doc/source/whatsnew/v0.23.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ Indexing
316316
- :func:`DatetimeIndex.to_series` now accepts ``index`` and ``name`` kwargs (:issue:`18699`)
317317
- Bug in indexing non-scalar value from ``Series`` having non-unique ``Index`` will return value flattened (:issue:`17610`)
318318
- Bug in :func:`DatetimeIndex.insert` where inserting ``NaT`` into a timezone-aware index incorrectly raised (:issue:`16357`)
319+
- Bug in ``__setitem__`` when indexing a :class:`DataFrame` with a 2-d boolean ndarray (:issue:`18582`)
319320

320321

321322
I/O

pandas/core/frame.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -2532,10 +2532,10 @@ def __setitem__(self, key, value):
25322532
if indexer is not None:
25332533
return self._setitem_slice(indexer, value)
25342534

2535-
if isinstance(key, (Series, np.ndarray, list, Index)):
2536-
self._setitem_array(key, value)
2537-
elif isinstance(key, DataFrame):
2535+
if isinstance(key, DataFrame) or getattr(key, 'ndim', None) == 2:
25382536
self._setitem_frame(key, value)
2537+
elif isinstance(key, (Series, np.ndarray, list, Index)):
2538+
self._setitem_array(key, value)
25392539
else:
25402540
# set column
25412541
self._set_item(key, value)
@@ -2568,8 +2568,17 @@ def _setitem_array(self, key, value):
25682568
def _setitem_frame(self, key, value):
25692569
# support boolean setting with DataFrame input, e.g.
25702570
# df[df > df2] = 0
2571+
if isinstance(key, np.ndarray):
2572+
if key.shape != self.shape:
2573+
raise ValueError(
2574+
'Array conditional must be same shape as self'
2575+
)
2576+
key = self._constructor(key, **self._construct_axes_dict())
2577+
25712578
if key.values.size and not is_bool_dtype(key.values):
2572-
raise TypeError('Must pass DataFrame with boolean values only')
2579+
raise TypeError(
2580+
'Must pass DataFrame or 2-d ndarray with boolean values only'
2581+
)
25732582

25742583
self._check_inplace_setting(value)
25752584
self._check_setitem_copy()

pandas/tests/frame/test_indexing.py

+21-3
Original file line numberDiff line numberDiff line change
@@ -524,9 +524,8 @@ def test_setitem_boolean(self):
524524
values[values == 2] = 3
525525
assert_almost_equal(df.values, values)
526526

527-
with tm.assert_raises_regex(TypeError, 'Must pass '
528-
'DataFrame with '
529-
'boolean values only'):
527+
msg = "Must pass DataFrame or 2-d ndarray with boolean values only"
528+
with tm.assert_raises_regex(TypeError, msg):
530529
df[df * 0] = 2
531530

532531
# index with DataFrame
@@ -542,6 +541,25 @@ def test_setitem_boolean(self):
542541
np.putmask(expected.values, mask.values, df.values * 2)
543542
assert_frame_equal(df, expected)
544543

544+
@pytest.mark.parametrize(
545+
"mask_type",
546+
[lambda df: df > np.abs(df) / 2,
547+
lambda df: (df > np.abs(df) / 2).values],
548+
ids=['dataframe', 'array'])
549+
def test_setitem_boolean_mask(self, mask_type):
550+
551+
# Test for issue #18582
552+
df = self.frame.copy()
553+
mask = mask_type(df)
554+
555+
# index with boolean mask
556+
result = df.copy()
557+
result[mask] = np.nan
558+
559+
expected = df.copy()
560+
expected.values[np.array(mask)] = np.nan
561+
assert_frame_equal(result, expected)
562+
545563
def test_setitem_cast(self):
546564
self.frame['D'] = self.frame['D'].astype('i8')
547565
assert self.frame['D'].dtype == np.int64

0 commit comments

Comments
 (0)