Repository: pandas-dev/pandas

Issue:
BUG: multi-index slicing buggy with datetime selectors
http://stackoverflow.com/questions/24152509/slicing-a-pandas-multiindex-using-datetime-datatype

```
dates = pd.DatetimeIndex([datetime.datetime(2012,1,1,12,12,12)+datetime.timedelta(days = i) for i in range(6)])
freq = [1,2]
iterables = [dates, freq]

index = pd.MultiIndex.from_product(iterables, names=['date','frequency'])
df = pd.DataFrame(np.random.randn(6*2,4),index=index,columns=list('ABCD'))
```
- This should work in a single step
- show this using `pd.IndexSlice` notation
- accept strings / partial strings as indexers

```
df_temp = df.loc[(slice(pd.Timestamp('2012-01-01 12:12:12'),pd.Timestamp('2012-01-03 12:12:12'))), slice('A','B')]
df_temp.loc[(slice(None),slice(1,1)),:]
```

After #7430, the following works (just using `IndexSlice` as a conven)

```
idx = pd.IndexSlice
df.loc[(idx[pd.Timestamp('2012-01-01 12:12:12'),pd.Timestamp('2012-01-03 12:12:12'),idx[1:1]], idx['A','B']]
```

as well as partial string slicing

```
df.loc[idx['2012-01-01 12:12:12':'2012-01-03 12:12:12',1],idx['A':'B']]
```



Discussion:

Resolving patch:
diff --git a/pandas/core/index.py b/pandas/core/index.py
--- a/pandas/core/index.py
+++ b/pandas/core/index.py
@@ -12,7 +12,6 @@
 import pandas.index as _index
 from pandas.lib import Timestamp, is_datetime_array
 from pandas.core.base import FrozenList, FrozenNDArray, IndexOpsMixin
-
 from pandas.util.decorators import cache_readonly, deprecate
 from pandas.core.common import isnull, array_equivalent
 import pandas.core.common as com
@@ -3532,7 +3531,16 @@ def _get_level_indexer(self, key, level=0):
             stop  = level_index.get_loc(key.stop or len(level_index)-1)
             step = key.step
 
-            if level > 0 or self.lexsort_depth == 0 or step is not None:
+            if isinstance(start,slice) or isinstance(stop,slice):
+                # we have a slice for start and/or stop
+                # a partial date slicer on a DatetimeIndex generates a slice
+                # note that the stop ALREADY includes the stopped point (if
+                # it was a string sliced)
+                m = np.zeros(len(labels),dtype=bool)
+                m[np.in1d(labels,np.arange(start.start,stop.stop,step))] = True
+                return m
+
+            elif level > 0 or self.lexsort_depth == 0 or step is not None:
                 # need to have like semantics here to right
                 # searching as when we are using a slice
                 # so include the stop+1 (so we include stop)
@@ -3571,6 +3579,8 @@ def get_locs(self, tup):
                for passing to iloc
         """
 
+        from pandas.core.indexing import _is_null_slice
+
         # must be lexsorted to at least as many levels
         if not self.is_lexsorted_for_tuple(tup):
             raise KeyError('MultiIndex Slicing requires the index to be fully lexsorted'
@@ -3598,10 +3608,12 @@ def _convert_indexer(r):
                 ranges.append(reduce(
                     np.logical_or,[ _convert_indexer(self._get_level_indexer(x, level=i)
                                                      ) for x in k ]))
-            elif k == slice(None):
-                # include all from this level
+            elif _is_null_slice(k):
+                # empty slice
                 pass
+
             elif isinstance(k,slice):
+
                 # a slice, include BOTH of the labels
                 ranges.append(self._get_level_indexer(k,level=i))
             else:
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -1635,16 +1635,12 @@ def _maybe_convert_ix(*args):
 
 def _is_nested_tuple(tup, labels):
     # check for a compatiable nested tuple and multiindexes among the axes
-
     if not isinstance(tup, tuple):
         return False
 
     # are we nested tuple of: tuple,list,slice
     for i, k in enumerate(tup):
 
-        #if i > len(axes):
-        #    raise IndexingError("invalid indxing tuple passed, has too many indexers for this object")
-        #ax = axes[i]
         if isinstance(k, (tuple, list, slice)):
             return isinstance(labels, MultiIndex)
 


Test patch:
diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py
--- a/pandas/tests/test_indexing.py
+++ b/pandas/tests/test_indexing.py
@@ -1565,6 +1565,35 @@ def test_multiindex_slicers_non_unique(self):
         self.assertFalse(result.index.is_unique)
         assert_frame_equal(result, expected)
 
+    def test_multiindex_slicers_datetimelike(self):
+
+        # GH 7429
+        # buggy/inconsistent behavior when slicing with datetime-like
+        import datetime
+        dates = [datetime.datetime(2012,1,1,12,12,12) + datetime.timedelta(days=i) for i in range(6)]
+        freq = [1,2]
+        index = MultiIndex.from_product([dates,freq], names=['date','frequency'])
+
+        df = DataFrame(np.arange(6*2*4,dtype='int64').reshape(-1,4),index=index,columns=list('ABCD'))
+
+        # multi-axis slicing
+        idx = pd.IndexSlice
+        expected = df.iloc[[0,2,4],[0,1]]
+        result = df.loc[(slice(Timestamp('2012-01-01 12:12:12'),Timestamp('2012-01-03 12:12:12')),slice(1,1)), slice('A','B')]
+        assert_frame_equal(result,expected)
+
+        result = df.loc[(idx[Timestamp('2012-01-01 12:12:12'):Timestamp('2012-01-03 12:12:12')],idx[1:1]), slice('A','B')]
+        assert_frame_equal(result,expected)
+
+        result = df.loc[(slice(Timestamp('2012-01-01 12:12:12'),Timestamp('2012-01-03 12:12:12')),1), slice('A','B')]
+        assert_frame_equal(result,expected)
+
+        # with strings
+        result = df.loc[(slice('2012-01-01 12:12:12','2012-01-03 12:12:12'),slice(1,1)), slice('A','B')]
+        assert_frame_equal(result,expected)
+
+        result = df.loc[(idx['2012-01-01 12:12:12':'2012-01-03 12:12:12'],1), idx['A','B']]
+        assert_frame_equal(result,expected)
 
     def test_per_axis_per_level_doc_examples(self):