Repository: pandas-dev/pandas

Issue:
BUG: DatetimeIndex.__getitem__ with boolean Index mask with False raises TypeError
```
import pandas as pd
import pytz
import datetime
import functools


def isNotWeekendIfEndBeforeStart (idxDateTime, startTime, endTime):
    if idxDateTime.dayofweek < 4: return True
    if idxDateTime.dayofweek == 5: return False
    if idxDateTime.dayofweek == 4: return idxDateTime.time() <= endTime if endTime else True
    if idxDateTime.dayofweek == 6: return idxDateTime.time() > startTime if startTime else False
    return True

if __name__ == '__main__':
    # pd.show_versions()
    # print(pd.date_range(start=datetime.datetime(2016, 3, 18, 0, 0), end=datetime.datetime(2016, 3, 22, 0, 0), freq='D',
    #                     tz='UTC', closed='right'))
    nytz = pytz.timezone('America/New_York')
    startTimeBkt = nytz.localize(datetime.datetime(2018,6,1,22))
    endTimeBkt = nytz.localize(datetime.datetime(2018,7,31,10,15))

    print(startTimeBkt)
    print(endTimeBkt)

    rng = pd.date_range(start=startTimeBkt, end=endTimeBkt, freq='900S', tz=nytz, closed='right')
    #print(rng)

    startCut=datetime.time(17, tzinfo=nytz)
    endCut=datetime.time(15, tzinfo=nytz)
    print(startCut)
    print(endCut)

    removeWeekends = functools.partial(isNotWeekendIfEndBeforeStart, startTime=startCut, endTime=endCut)
    rng = rng[rng.map(removeWeekends)]
    print(rng)

```
The above code creates a datetimeindex of 15 minute intervals and then removes all rows deemed as weekend times (although weekends are not defined by midnight and use a specific time of day) This works with pandas version 19 but when I looked to upgrade a numpy error is returned. This occurs on both Python 2.7 and Python 3.6

Errors:

Pandas 0.20.3

Traceback (most recent call last):
  File "PandasTest.py", line 41, in <module>
    rng = rng[rng.map(removeWeekends)]
  File "%PY_HOME%\lib\site-packages\pandas\core\indexes\datetimelike.py", line 296, in __getitem__
  result = getitem(key)
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices

Pandas 0.21.1

Traceback (most recent call last):
  File "PandasTest.py", line 41, in <module>
    rng = rng[rng.map(removeWeekends)]
  File "%PY_HOME%\lib\site-packages\pandas\core\indexes\datetimelike.py", line 279, in __getitem__
  key = lib.maybe_booleans_to_slice(key.view(np.uint8))
  File "%PY_HOME%\lib\site-packages\numpy\core\_internal.py", line 365, in _view_is_safe
    raise TypeError("Cannot change data-type for object array.")
TypeError: Cannot change data-type for object array.

Pandas 0.22.0

Traceback (most recent call last):
  File "PandasTest.py", line 41, in <module>
    rng = rng[rng.map(removeWeekends)]
  File "%PY_HOME%\lib\site-packages\pandas\core\indexes\datetimelike.py", line 279, in __getitem__
    key = lib.maybe_booleans_to_slice(key.view(np.uint8))
  File "%PY_HOME%\lib\site-packages\numpy\core\_internal.py", line 365, in _view_is_safe
    raise TypeError("Cannot change data-type for object array.")
TypeError: Cannot change data-type for object array.

Pandas 0.23.4

Traceback (most recent call last):
  File "PandasTest.py", line 41, in <module>
    rng = rng[rng.map(removeWeekends)]
  File "%PY_HOME%\lib\site-packages\pandas\core\indexes\datetimelike.py", line 411, in __getitem__
    key = lib.maybe_booleans_to_slice(key.view(np.uint8))
  File "%PY_HOME%\lib\site-packages\numpy\core\_internal.py", line 365, in _view_is_safe
    raise TypeError("Cannot change data-type for object array.")
TypeError: Cannot change data-type for object array.


Discussion:
Here's a similar reproducible example:

```
In [27]: pd.__version__
Out[27]: '0.24.0.dev0+523.gbf6763458'

In [28]: dti = pd.DatetimeIndex(['2012-01-01'])

# Interestingly works for True
In [29]: dti[pd.Index([True])]
Out[29]: DatetimeIndex(['2012-01-01'], dtype='datetime64[ns]', freq=None)

In [30]: dti[pd.Index([False])]
TypeError: Cannot change data-type for object array.
```

Investigations and PR's welcome!
Note, this works if you convert the resulting Index from `map` to a numpy array:

```
In [45]: rng[rng.map(removeWeekends).values.astype(bool)]
Out[45]:
DatetimeIndex(['2018-06-03 17:15:00-04:00', '2018-06-03 17:30:00-04:00',
               '2018-06-03 17:45:00-04:00', '2018-06-03 18:00:00-04:00',
               '2018-06-03 18:15:00-04:00', '2018-06-03 18:30:00-04:00',
               '2018-06-03 18:45:00-04:00', '2018-06-03 19:00:00-04:00',
               '2018-06-03 19:15:00-04:00', '2018-06-03 19:30:00-04:00',
               ...
               '2018-07-31 08:00:00-04:00', '2018-07-31 08:15:00-04:00',
               '2018-07-31 08:30:00-04:00', '2018-07-31 08:45:00-04:00',
               '2018-07-31 09:00:00-04:00', '2018-07-31 09:15:00-04:00',
               '2018-07-31 09:30:00-04:00', '2018-07-31 09:45:00-04:00',
               '2018-07-31 10:00:00-04:00', '2018-07-31 10:15:00-04:00'],
              dtype='datetime64[ns, America/New_York]', length=3941, freq=None)
```

Resolving patch:
diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py
--- a/pandas/core/arrays/datetimelike.py
+++ b/pandas/core/arrays/datetimelike.py
@@ -161,7 +161,7 @@ def __getitem__(self, key):
             return self._box_func(val)
 
         if com.is_bool_indexer(key):
-            key = np.asarray(key)
+            key = np.asarray(key, dtype=bool)
             if key.all():
                 key = slice(0, None, None)
             else:
diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py
--- a/pandas/core/indexes/base.py
+++ b/pandas/core/indexes/base.py
@@ -2078,7 +2078,7 @@ def __getitem__(self, key):
             return promote(getitem(key))
 
         if com.is_bool_indexer(key):
-            key = np.asarray(key)
+            key = np.asarray(key, dtype=bool)
 
         key = com.values_from_object(key)
         result = getitem(key)
diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py
--- a/pandas/core/indexes/multi.py
+++ b/pandas/core/indexes/multi.py
@@ -1614,7 +1614,7 @@ def __getitem__(self, key):
             return tuple(retval)
         else:
             if com.is_bool_indexer(key):
-                key = np.asarray(key)
+                key = np.asarray(key, dtype=bool)
                 sortorder = self.sortorder
             else:
                 # cannot be sure whether the result will be sorted


Test patch:
diff --git a/pandas/tests/indexes/multi/test_indexing.py b/pandas/tests/indexes/multi/test_indexing.py
--- a/pandas/tests/indexes/multi/test_indexing.py
+++ b/pandas/tests/indexes/multi/test_indexing.py
@@ -226,6 +226,33 @@ def test_get_indexer_consistency(idx):
     assert indexer.dtype == np.intp
 
 
+@pytest.mark.parametrize('ind1', [[True] * 5, pd.Index([True] * 5)])
+@pytest.mark.parametrize('ind2', [[True, False, True, False, False],
+                                  pd.Index([True, False, True, False,
+                                            False])])
+def test_getitem_bool_index_all(ind1, ind2):
+    # GH#22533
+    idx = MultiIndex.from_tuples([(10, 1), (20, 2), (30, 3),
+                                  (40, 4), (50, 5)])
+    tm.assert_index_equal(idx[ind1], idx)
+
+    expected = MultiIndex.from_tuples([(10, 1), (30, 3)])
+    tm.assert_index_equal(idx[ind2], expected)
+
+
+@pytest.mark.parametrize('ind1', [[True], pd.Index([True])])
+@pytest.mark.parametrize('ind2', [[False], pd.Index([False])])
+def test_getitem_bool_index_single(ind1, ind2):
+    # GH#22533
+    idx = MultiIndex.from_tuples([(10, 1)])
+    tm.assert_index_equal(idx[ind1], idx)
+
+    expected = pd.MultiIndex(levels=[np.array([], dtype=np.int64),
+                                     np.array([], dtype=np.int64)],
+                             labels=[[], []])
+    tm.assert_index_equal(idx[ind2], expected)
+
+
 def test_get_loc(idx):
     assert idx.get_loc(('foo', 'two')) == 1
     assert idx.get_loc(('baz', 'two')) == 3
diff --git a/pandas/tests/test_base.py b/pandas/tests/test_base.py
--- a/pandas/tests/test_base.py
+++ b/pandas/tests/test_base.py
@@ -178,19 +178,20 @@ def setup_method(self, method):
         self.unicode_index = tm.makeUnicodeIndex(10, name='a')
 
         arr = np.random.randn(10)
+        self.bool_series = Series(arr, index=self.bool_index, name='a')
         self.int_series = Series(arr, index=self.int_index, name='a')
         self.float_series = Series(arr, index=self.float_index, name='a')
         self.dt_series = Series(arr, index=self.dt_index, name='a')
         self.dt_tz_series = self.dt_tz_index.to_series(keep_tz=True)
         self.period_series = Series(arr, index=self.period_index, name='a')
         self.string_series = Series(arr, index=self.string_index, name='a')
+        self.unicode_series = Series(arr, index=self.unicode_index, name='a')
 
         types = ['bool', 'int', 'float', 'dt', 'dt_tz', 'period', 'string',
                  'unicode']
-        fmts = ["{0}_{1}".format(t, f)
-                for t in types for f in ['index', 'series']]
-        self.objs = [getattr(self, f)
-                     for f in fmts if getattr(self, f, None) is not None]
+        self.indexes = [getattr(self, '{}_index'.format(t)) for t in types]
+        self.series = [getattr(self, '{}_series'.format(t)) for t in types]
+        self.objs = self.indexes + self.series
 
     def check_ops_properties(self, props, filter=None, ignore_failures=False):
         for op in props:
@@ -997,6 +998,31 @@ def test_validate_bool_args(self):
             with pytest.raises(ValueError):
                 self.int_series.drop_duplicates(inplace=value)
 
+    def test_getitem(self):
+        for i in self.indexes:
+            s = pd.Series(i)
+
+            assert i[0] == s.iloc[0]
+            assert i[5] == s.iloc[5]
+            assert i[-1] == s.iloc[-1]
+
+            assert i[-1] == i[9]
+
+            pytest.raises(IndexError, i.__getitem__, 20)
+            pytest.raises(IndexError, s.iloc.__getitem__, 20)
+
+    @pytest.mark.parametrize('indexer_klass', [list, pd.Index])
+    @pytest.mark.parametrize('indexer', [[True] * 10, [False] * 10,
+                                         [True, False, True, True, False,
+                                          False, True, True, False, True]])
+    def test_bool_indexing(self, indexer_klass, indexer):
+        # GH 22533
+        for idx in self.indexes:
+            exp_idx = [i for i in range(len(indexer)) if indexer[i]]
+            tm.assert_index_equal(idx[indexer_klass(indexer)], idx[exp_idx])
+            s = pd.Series(idx)
+            tm.assert_series_equal(s[indexer_klass(indexer)], s.iloc[exp_idx])
+
 
 class TestTranspose(Ops):
     errmsg = "the 'axes' parameter is not supported"