@@ -2670,68 +2670,78 @@ def _ixs(self, i, axis=0):
2670
2670
def __getitem__ (self , key ):
2671
2671
key = com ._apply_if_callable (key , self )
2672
2672
2673
- # shortcut if we are an actual column
2674
- is_mi_columns = isinstance (self .columns , MultiIndex )
2673
+ # shortcut if the key is in columns
2675
2674
try :
2676
- if key in self .columns and not is_mi_columns :
2677
- return self ._getitem_column (key )
2678
- except :
2675
+ if self .columns .is_unique and key in self .columns :
2676
+ if self .columns .nlevels > 1 :
2677
+ return self ._getitem_multilevel (key )
2678
+ return self ._get_item_cache (key )
2679
+ except (ValueError , TypeError ):
2679
2680
pass
2680
2681
2681
- # see if we can slice the rows
2682
+ # Do we have a slicer (on rows)?
2682
2683
indexer = convert_to_index_sliceable (self , key )
2683
2684
if indexer is not None :
2684
- return self ._getitem_slice (indexer )
2685
+ return self ._slice (indexer , axis = 0 )
2685
2686
2686
- if isinstance (key , (Series , np .ndarray , Index , list )):
2687
- # either boolean or fancy integer index
2688
- return self ._getitem_array (key )
2689
- elif isinstance (key , DataFrame ):
2687
+ # Do we have a (boolean) DataFrame?
2688
+ if isinstance (key , DataFrame ):
2690
2689
return self ._getitem_frame (key )
2691
- elif is_mi_columns :
2692
- return self ._getitem_multilevel (key )
2693
- else :
2694
- return self ._getitem_column (key )
2695
2690
2696
- def _getitem_column (self , key ):
2697
- """ return the actual column """
2691
+ # Do we have a (boolean) 1d indexer?
2692
+ if com .is_bool_indexer (key ):
2693
+ return self ._getitem_bool_array (key )
2694
+
2695
+ # We are left with two options: a single key, and a collection of keys,
2696
+ # We interpret tuples as collections only for non-MultiIndex
2697
+ is_single_key = isinstance (key , tuple ) or not is_list_like (key )
2698
+
2699
+ if is_single_key :
2700
+ if self .columns .nlevels > 1 :
2701
+ return self ._getitem_multilevel (key )
2702
+ indexer = self .columns .get_loc (key )
2703
+ if is_integer (indexer ):
2704
+ indexer = [indexer ]
2705
+ else :
2706
+ if is_iterator (key ):
2707
+ key = list (key )
2708
+ indexer = self .loc ._convert_to_indexer (key , axis = 1 ,
2709
+ raise_missing = True )
2698
2710
2699
- # get column
2700
- if self . columns . is_unique :
2701
- return self . _get_item_cache ( key )
2711
+ # take() does not accept boolean indexers
2712
+ if getattr ( indexer , "dtype" , None ) == bool :
2713
+ indexer = np . where ( indexer )[ 0 ]
2702
2714
2703
- # duplicate columns & possible reduce dimensionality
2704
- result = self ._constructor (self ._data .get (key ))
2705
- if result .columns .is_unique :
2706
- result = result [key ]
2715
+ data = self ._take (indexer , axis = 1 )
2707
2716
2708
- return result
2717
+ if is_single_key :
2718
+ # What does looking for a single key in a non-unique index return?
2719
+ # The behavior is inconsistent. It returns a Series, except when
2720
+ # - the key itself is repeated (test on data.shape, #9519), or
2721
+ # - we have a MultiIndex on columns (test on self.columns, #21309)
2722
+ if data .shape [1 ] == 1 and not isinstance (self .columns , MultiIndex ):
2723
+ data = data [key ]
2709
2724
2710
- def _getitem_slice (self , key ):
2711
- return self ._slice (key , axis = 0 )
2725
+ return data
2712
2726
2713
- def _getitem_array (self , key ):
2727
+ def _getitem_bool_array (self , key ):
2714
2728
# also raises Exception if object array with NA values
2715
- if com .is_bool_indexer (key ):
2716
- # warning here just in case -- previously __setitem__ was
2717
- # reindexing but __getitem__ was not; it seems more reasonable to
2718
- # go with the __setitem__ behavior since that is more consistent
2719
- # with all other indexing behavior
2720
- if isinstance (key , Series ) and not key .index .equals (self .index ):
2721
- warnings .warn ("Boolean Series key will be reindexed to match "
2722
- "DataFrame index." , UserWarning , stacklevel = 3 )
2723
- elif len (key ) != len (self .index ):
2724
- raise ValueError ('Item wrong length %d instead of %d.' %
2725
- (len (key ), len (self .index )))
2726
- # check_bool_indexer will throw exception if Series key cannot
2727
- # be reindexed to match DataFrame rows
2728
- key = check_bool_indexer (self .index , key )
2729
- indexer = key .nonzero ()[0 ]
2730
- return self ._take (indexer , axis = 0 )
2731
- else :
2732
- indexer = self .loc ._convert_to_indexer (key , axis = 1 ,
2733
- raise_missing = True )
2734
- return self ._take (indexer , axis = 1 )
2729
+ # warning here just in case -- previously __setitem__ was
2730
+ # reindexing but __getitem__ was not; it seems more reasonable to
2731
+ # go with the __setitem__ behavior since that is more consistent
2732
+ # with all other indexing behavior
2733
+ if isinstance (key , Series ) and not key .index .equals (self .index ):
2734
+ warnings .warn ("Boolean Series key will be reindexed to match "
2735
+ "DataFrame index." , UserWarning , stacklevel = 3 )
2736
+ elif len (key ) != len (self .index ):
2737
+ raise ValueError ('Item wrong length %d instead of %d.' %
2738
+ (len (key ), len (self .index )))
2739
+
2740
+ # check_bool_indexer will throw exception if Series key cannot
2741
+ # be reindexed to match DataFrame rows
2742
+ key = check_bool_indexer (self .index , key )
2743
+ indexer = key .nonzero ()[0 ]
2744
+ return self ._take (indexer , axis = 0 )
2735
2745
2736
2746
def _getitem_multilevel (self , key ):
2737
2747
loc = self .columns .get_loc (key )
0 commit comments