Skip to content

Commit cbd9184

Browse files
committed
BUG: DatetimeIndex slicing with boolean Index raises TypeError
1 parent a2e5994 commit cbd9184

File tree

9 files changed

+128
-8
lines changed

9 files changed

+128
-8
lines changed

doc/source/whatsnew/v0.24.0.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ Renaming names in a MultiIndex
188188
:func:`DataFrame.rename_axis` now supports ``index`` and ``columns`` arguments
189189
and :func:`Series.rename_axis` supports ``index`` argument (:issue:`19978`)
190190

191-
This change allows a dictionary to be passed so that some of the names
191+
This change allows a dictionary to be passed so that some of the names
192192
of a ``MultiIndex`` can be changed.
193193

194194
Example:
@@ -1150,6 +1150,7 @@ Indexing
11501150
- :class:`Index` no longer mangles ``None``, ``NaN`` and ``NaT``, i.e. they are treated as three different keys. However, for numeric Index all three are still coerced to a ``NaN`` (:issue:`22332`)
11511151
- Bug in `scalar in Index` if scalar is a float while the ``Index`` is of integer dtype (:issue:`22085`)
11521152
- Bug in `MultiIndex.set_levels` when levels value is not subscriptable (:issue:`23273`)
1153+
- Bug in :class:`Index` slicing with boolean :class:`Index` may raise ``TypeError`` (:issue:`22533`)
11531154

11541155
Missing
11551156
^^^^^^^

pandas/core/arrays/datetimelike.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def __getitem__(self, key):
172172
return self._box_func(val)
173173

174174
if com.is_bool_indexer(key):
175-
key = np.asarray(key)
175+
key = np.asarray(key, dtype=bool)
176176
if key.all():
177177
key = slice(0, None, None)
178178
else:

pandas/core/indexes/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2074,7 +2074,7 @@ def __getitem__(self, key):
20742074
return promote(getitem(key))
20752075

20762076
if com.is_bool_indexer(key):
2077-
key = np.asarray(key)
2077+
key = np.asarray(key, dtype=bool)
20782078

20792079
key = com.values_from_object(key)
20802080
result = getitem(key)

pandas/core/indexes/multi.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1612,7 +1612,7 @@ def __getitem__(self, key):
16121612
return tuple(retval)
16131613
else:
16141614
if com.is_bool_indexer(key):
1615-
key = np.asarray(key)
1615+
key = np.asarray(key, dtype=bool)
16161616
sortorder = self.sortorder
16171617
else:
16181618
# cannot be sure whether the result will be sorted

pandas/tests/indexes/datetimes/test_indexing.py

+22
Original file line numberDiff line numberDiff line change
@@ -603,3 +603,25 @@ def test_get_loc_nat(self):
603603
# GH#20464
604604
index = DatetimeIndex(['1/3/2000', 'NaT'])
605605
assert index.get_loc(pd.NaT) == 1
606+
607+
@pytest.mark.parametrize('ind1', [[True] * 5, pd.Index([True] * 5)])
608+
@pytest.mark.parametrize('ind2', [[True, False, True, False, False],
609+
pd.Index([True, False, True, False,
610+
False])])
611+
def test_getitem_bool_index_all(self, ind1, ind2):
612+
# GH#22533
613+
idx = pd.date_range('2011-01-01', '2011-01-05', freq='D', name='idx')
614+
tm.assert_index_equal(idx[ind1], idx)
615+
616+
expected = pd.DatetimeIndex(['2011-01-01', '2011-01-03'], name='idx')
617+
tm.assert_index_equal(idx[ind2], expected)
618+
619+
@pytest.mark.parametrize('ind1', [[True], pd.Index([True])])
620+
@pytest.mark.parametrize('ind2', [[False], pd.Index([False])])
621+
def test_getitem_bool_index_single(self, ind1, ind2):
622+
# GH#22533
623+
idx = pd.DatetimeIndex(['2011-01-01'], name='idx')
624+
tm.assert_index_equal(idx[ind1], idx)
625+
626+
expected = pd.DatetimeIndex([], name='idx')
627+
tm.assert_index_equal(idx[ind2], expected)

pandas/tests/indexes/multi/test_indexing.py

+27
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,33 @@ def test_get_indexer_consistency(idx):
226226
assert indexer.dtype == np.intp
227227

228228

229+
@pytest.mark.parametrize('ind1', [[True] * 5, pd.Index([True] * 5)])
230+
@pytest.mark.parametrize('ind2', [[True, False, True, False, False],
231+
pd.Index([True, False, True, False,
232+
False])])
233+
def test_getitem_bool_index_all(ind1, ind2):
234+
# GH#22533
235+
idx = MultiIndex.from_tuples([(10, 1), (20, 2), (30, 3),
236+
(40, 4), (50, 5)])
237+
tm.assert_index_equal(idx[ind1], idx)
238+
239+
expected = MultiIndex.from_tuples([(10, 1), (30, 3)])
240+
tm.assert_index_equal(idx[ind2], expected)
241+
242+
243+
@pytest.mark.parametrize('ind1', [[True], pd.Index([True])])
244+
@pytest.mark.parametrize('ind2', [[False], pd.Index([False])])
245+
def test_getitem_bool_index_single(ind1, ind2):
246+
# GH#22533
247+
idx = MultiIndex.from_tuples([(10, 1)])
248+
tm.assert_index_equal(idx[ind1], idx)
249+
250+
expected = pd.MultiIndex(levels=[np.array([], dtype=np.int64),
251+
np.array([], dtype=np.int64)],
252+
labels=[[], []])
253+
tm.assert_index_equal(idx[ind2], expected)
254+
255+
229256
def test_get_loc(idx):
230257
assert idx.get_loc(('foo', 'two')) == 1
231258
assert idx.get_loc(('baz', 'two')) == 3

pandas/tests/indexes/test_base.py

+22
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,28 @@ def test_getitem_error(self, indices, itm):
718718
with pytest.raises(IndexError):
719719
indices[itm]
720720

721+
@pytest.mark.parametrize('ind1', [[True] * 5, pd.Index([True] * 5)])
722+
@pytest.mark.parametrize('ind2', [[True, False, True, False, False],
723+
pd.Index([True, False, True, False,
724+
False])])
725+
def test_getitem_bool_index_all(self, ind1, ind2):
726+
# GH#22533
727+
idx = pd.Index(['a', 'b', 'c', 'd', 'e'], name='idx')
728+
tm.assert_index_equal(idx[ind1], idx)
729+
730+
expected = pd.Index(['a', 'c'], name='idx')
731+
tm.assert_index_equal(idx[ind2], expected)
732+
733+
@pytest.mark.parametrize('ind1', [[True], pd.Index([True])])
734+
@pytest.mark.parametrize('ind2', [[False], pd.Index([False])])
735+
def test_getitem_bool_index_single(self, ind1, ind2):
736+
# GH#22533
737+
idx = pd.Index(['a'], name='idx')
738+
tm.assert_index_equal(idx[ind1], idx)
739+
740+
expected = pd.Index([], name='idx')
741+
tm.assert_index_equal(idx[ind2], expected)
742+
721743
def test_intersection(self):
722744
first = self.strIndex[:20]
723745
second = self.strIndex[:10]

pandas/tests/indexes/test_numeric.py

+22
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,28 @@ def test_join_outer(self):
839839
tm.assert_numpy_array_equal(lidx, elidx)
840840
tm.assert_numpy_array_equal(ridx, eridx)
841841

842+
@pytest.mark.parametrize('ind1', [[True] * 5, pd.Index([True] * 5)])
843+
@pytest.mark.parametrize('ind2', [[True, False, True, False, False],
844+
pd.Index([True, False, True, False,
845+
False])])
846+
def test_getitem_bool_index_all(self, ind1, ind2):
847+
# GH#22533
848+
idx = pd.Int64Index([1, 2, 3, 4, 5], name='idx')
849+
tm.assert_index_equal(idx[ind1], idx)
850+
851+
expected = pd.Int64Index([1, 3], name='idx')
852+
tm.assert_index_equal(idx[ind2], expected)
853+
854+
@pytest.mark.parametrize('ind1', [[True], pd.Index([True])])
855+
@pytest.mark.parametrize('ind2', [[False], pd.Index([False])])
856+
def test_getitem_bool_index_single(self, ind1, ind2):
857+
# GH#22533
858+
idx = pd.Int64Index([1], name='idx')
859+
tm.assert_index_equal(idx[ind1], idx)
860+
861+
expected = pd.Int64Index([], name='idx')
862+
tm.assert_index_equal(idx[ind2], expected)
863+
842864

843865
class TestUInt64Index(NumericInt):
844866

pandas/tests/test_base.py

+30-4
Original file line numberDiff line numberDiff line change
@@ -178,19 +178,20 @@ def setup_method(self, method):
178178
self.unicode_index = tm.makeUnicodeIndex(10, name='a')
179179

180180
arr = np.random.randn(10)
181+
self.bool_series = Series(arr, index=self.bool_index, name='a')
181182
self.int_series = Series(arr, index=self.int_index, name='a')
182183
self.float_series = Series(arr, index=self.float_index, name='a')
183184
self.dt_series = Series(arr, index=self.dt_index, name='a')
184185
self.dt_tz_series = self.dt_tz_index.to_series(keep_tz=True)
185186
self.period_series = Series(arr, index=self.period_index, name='a')
186187
self.string_series = Series(arr, index=self.string_index, name='a')
188+
self.unicode_series = Series(arr, index=self.unicode_index, name='a')
187189

188190
types = ['bool', 'int', 'float', 'dt', 'dt_tz', 'period', 'string',
189191
'unicode']
190-
fmts = ["{0}_{1}".format(t, f)
191-
for t in types for f in ['index', 'series']]
192-
self.objs = [getattr(self, f)
193-
for f in fmts if getattr(self, f, None) is not None]
192+
self.indexes = [getattr(self, '{}_index'.format(t)) for t in types]
193+
self.series = [getattr(self, '{}_series'.format(t)) for t in types]
194+
self.objs = self.indexes + self.series
194195

195196
def check_ops_properties(self, props, filter=None, ignore_failures=False):
196197
for op in props:
@@ -997,6 +998,31 @@ def test_validate_bool_args(self):
997998
with pytest.raises(ValueError):
998999
self.int_series.drop_duplicates(inplace=value)
9991000

1001+
def test_getitem(self):
1002+
for i in self.indexes:
1003+
s = pd.Series(i)
1004+
1005+
assert i[0] == s.iloc[0]
1006+
assert i[5] == s.iloc[5]
1007+
assert i[-1] == s.iloc[-1]
1008+
1009+
assert i[-1] == i[9]
1010+
1011+
pytest.raises(IndexError, i.__getitem__, 20)
1012+
pytest.raises(IndexError, s.iloc.__getitem__, 20)
1013+
1014+
@pytest.mark.parametrize('indexer_klass', [list, pd.Index])
1015+
@pytest.mark.parametrize('indexer', [[True] * 10, [False] * 10,
1016+
[True, False, True, True, False,
1017+
False, True, True, False, True]])
1018+
def test_bool_indexing(self, indexer_klass, indexer):
1019+
# GH 22533
1020+
for idx in self.indexes:
1021+
exp_idx = [i for i in range(len(indexer)) if indexer[i]]
1022+
tm.assert_index_equal(idx[indexer_klass(indexer)], idx[exp_idx])
1023+
s = pd.Series(idx)
1024+
tm.assert_series_equal(s[indexer_klass(indexer)], s.iloc[exp_idx])
1025+
10001026

10011027
class TestTranspose(Ops):
10021028
errmsg = "the 'axes' parameter is not supported"

0 commit comments

Comments
 (0)