@@ -693,35 +693,39 @@ def _convert_for_reindex(self, key, axis=0):
693
693
694
694
return keyarr
695
695
696
- def _getitem_lowerdim (self , tup ):
696
+ def _handle_lowerdim_multi_index_axis0 (self , tup ):
697
+ # we have an axis0 multi-index, handle or raise
697
698
698
- ax0 = self .obj ._get_axis (0 )
699
- # a bit kludgy
700
- if isinstance (ax0 , MultiIndex ):
701
- try :
702
- # fast path for series or for tup devoid of slices
703
- return self ._get_label (tup , axis = 0 )
704
- except TypeError :
705
- # slices are unhashable
706
- pass
707
- except Exception as e1 :
708
- if isinstance (tup [0 ], (slice , Index )):
709
- raise IndexingError ("Handle elsewhere" )
699
+ try :
700
+ # fast path for series or for tup devoid of slices
701
+ return self ._get_label (tup , axis = 0 )
702
+ except TypeError :
703
+ # slices are unhashable
704
+ pass
705
+ except Exception as e1 :
706
+ if isinstance (tup [0 ], (slice , Index )):
707
+ raise IndexingError ("Handle elsewhere" )
710
708
711
- # raise the error if we are not sorted
712
- if not ax0 .is_lexsorted_for_tuple (tup ):
713
- raise e1
709
+ # raise the error if we are not sorted
710
+ ax0 = self .obj ._get_axis (0 )
711
+ if not ax0 .is_lexsorted_for_tuple (tup ):
712
+ raise e1
714
713
715
- # GH911 introduced this clause, but the regression test
716
- # added for it now passes even without it. Let's rock the boat.
717
- # 2014/01/27
714
+ return None
718
715
719
- # # should we abort, or keep going?
720
- # try:
721
- # loc = ax0.get_loc(tup[0])
722
- # except KeyError:
723
- # raise e1
716
+ def _getitem_lowerdim (self , tup ):
724
717
718
+ # we may have a nested tuples indexer here
719
+ if any ([ isinstance (ax , MultiIndex ) for ax in self .obj .axes ]):
720
+ if any ([ _is_nested_tuple (tup ,ax ) for ax in self .obj .axes ]):
721
+ return self ._getitem_nested_tuple (tup )
722
+
723
+ # we maybe be using a tuple to represent multiple dimensions here
724
+ ax0 = self .obj ._get_axis (0 )
725
+ if isinstance (ax0 , MultiIndex ):
726
+ result = self ._handle_lowerdim_multi_index_axis0 (tup )
727
+ if result is not None :
728
+ return result
725
729
726
730
if len (tup ) > self .obj .ndim :
727
731
raise IndexingError ("Too many indexers. handle elsewhere" )
@@ -760,7 +764,31 @@ def _getitem_lowerdim(self, tup):
760
764
761
765
raise IndexingError ('not applicable' )
762
766
763
- def _getitem_axis (self , key , axis = 0 ):
767
+ def _getitem_nested_tuple (self , tup ):
768
+ # we have a nested tuple so have at least 1 multi-index level
769
+ # we should be able to match up the dimensionaility here
770
+
771
+ # we have too many indexers for our dim, but have at least 1
772
+ # multi-index dimension, try to see if we have something like
773
+ # a tuple passed to a series with a multi-index
774
+ if len (tup ) > self .ndim :
775
+ return self ._handle_lowerdim_multi_index_axis0 (tup )
776
+
777
+ # handle the multi-axis by taking sections and reducing
778
+ # this is iterative
779
+ obj = self .obj
780
+ axis = 0
781
+ for key in tup :
782
+
783
+ obj = getattr (obj , self .name )._getitem_axis (key , axis = axis , validate_iterable = True )
784
+ axis += 1
785
+
786
+ if obj .ndim < self .ndim :
787
+ axis -= 1
788
+
789
+ return obj
790
+
791
+ def _getitem_axis (self , key , axis = 0 , validate_iterable = False ):
764
792
765
793
self ._has_valid_type (key , axis )
766
794
labels = self .obj ._get_axis (axis )
@@ -1058,7 +1086,7 @@ def __getitem__(self, key):
1058
1086
else :
1059
1087
return self ._getitem_axis (key , axis = 0 )
1060
1088
1061
- def _getitem_axis (self , key , axis = 0 ):
1089
+ def _getitem_axis (self , key , axis = 0 , validate_iterable = False ):
1062
1090
raise NotImplementedError ()
1063
1091
1064
1092
def _getbool_axis (self , key , axis = 0 ):
@@ -1135,6 +1163,7 @@ def _has_valid_type(self, key, axis):
1135
1163
# require all elements in the index
1136
1164
idx = _ensure_index (key )
1137
1165
if not idx .isin (ax ).all ():
1166
+
1138
1167
raise KeyError ("[%s] are not in ALL in the [%s]" %
1139
1168
(key , self .obj ._get_axis_name (axis )))
1140
1169
@@ -1164,7 +1193,7 @@ def error():
1164
1193
1165
1194
return True
1166
1195
1167
- def _getitem_axis (self , key , axis = 0 ):
1196
+ def _getitem_axis (self , key , axis = 0 , validate_iterable = False ):
1168
1197
labels = self .obj ._get_axis (axis )
1169
1198
1170
1199
if isinstance (key , slice ):
@@ -1178,12 +1207,15 @@ def _getitem_axis(self, key, axis=0):
1178
1207
if hasattr (key , 'ndim' ) and key .ndim > 1 :
1179
1208
raise ValueError ('Cannot index with multidimensional key' )
1180
1209
1210
+ if validate_iterable :
1211
+ self ._has_valid_type (key , axis )
1181
1212
return self ._getitem_iterable (key , axis = axis )
1182
- elif isinstance (key , tuple ) and isinstance (labels , MultiIndex ) and \
1183
- any ([isinstance (x ,slice ) for x in key ]):
1184
- locs = labels .get_locs (key )
1185
- g = labels .locs_to_indexer (locs )
1186
- return self .obj .iloc [g ]
1213
+ elif _is_nested_tuple (key , labels ):
1214
+ specs = labels .get_specs (key )
1215
+ g = labels .specs_to_indexer (specs )
1216
+ indexer = [ slice (None ) ] * self .ndim
1217
+ indexer [axis ] = g
1218
+ return self .obj .iloc [tuple (indexer )]
1187
1219
else :
1188
1220
self ._has_valid_type (key , axis )
1189
1221
return self ._get_label (key , axis = axis )
@@ -1256,7 +1288,7 @@ def _get_slice_axis(self, slice_obj, axis=0):
1256
1288
else :
1257
1289
return self .obj .take (slice_obj , axis = axis , convert = False )
1258
1290
1259
- def _getitem_axis (self , key , axis = 0 ):
1291
+ def _getitem_axis (self , key , axis = 0 , validate_iterable = False ):
1260
1292
1261
1293
if isinstance (key , slice ):
1262
1294
self ._has_valid_type (key , axis )
@@ -1515,6 +1547,24 @@ def _maybe_convert_ix(*args):
1515
1547
return args
1516
1548
1517
1549
1550
+ def _is_nested_tuple (tup , labels ):
1551
+ # check for a compatiable nested tuple and multiindexes among the axes
1552
+
1553
+ if not isinstance (tup , tuple ):
1554
+ return False
1555
+
1556
+ # are we nested tuple of: tuple,list,slice
1557
+ for i , k in enumerate (tup ):
1558
+
1559
+ #if i > len(axes):
1560
+ # raise IndexingError("invalid indxing tuple passed, has too many indexers for this object")
1561
+ #ax = axes[i]
1562
+ if isinstance (k , (tuple , list , slice )):
1563
+ return isinstance (labels , MultiIndex )
1564
+
1565
+ return False
1566
+
1567
+
1518
1568
def _is_null_slice (obj ):
1519
1569
return (isinstance (obj , slice ) and obj .start is None and
1520
1570
obj .stop is None and obj .step is None )
0 commit comments