2015-06-11 2 views
13

я прочитал the docs about slicers миллион раз, но никогда не получил мою голову вокруг него, так что я до сих пор пытаются выяснить, как использовать loc, чтобы нарезать DataFrame с MultiIndex ,«Слишком много индексаторах» с DataFrame.loc

Начну с DataFrame из this SO answer:

      value 
first second third fourth  
A0 B0  C1 D0   2 
        D1   3 
      C3 D0   6 
        D1   7 
     B1  C1 D0   10 
        D1   11 
      C3 D0   14 
        D1   15 
A1 B0  C1 D0   18 
        D1   19 
      C3 D0   22 
        D1   23 
     B1  C1 D0   26 
        D1   27 
      C3 D0   30 
        D1   31 
A2 B0  C1 D0   34 
        D1   35 
      C3 D0   38 
        D1   39 
     B1  C1 D0   42 
        D1   43 
      C3 D0   46 
        D1   47 
A3 B0  C1 D0   50 
        D1   51 
      C3 D0   54 
        D1   55 
     B1  C1 D0   58 
        D1   59 
      C3 D0   62 
        D1   63 

Чтобы выбрать только A0 и C1 значения, я могу сделать:

In [26]: df.loc['A0', :, 'C1', :] 
Out[26]: 
          value 
first second third fourth  
A0 B0  C1 D0   2 
        D1   3 
     B1  C1 D0   10 
        D1   11 

Который также работает, выбирая из трех уровней, и даже с кортежами:

In [28]: df.loc['A0', :, ('C1', 'C2'), 'D1'] 
Out[28]: 
          value 
first second third fourth  
A0 B0  C1 D1   3 
      C2 D1   5 
     B1  C1 D1   11 
      C2 D1   13 

До сих пор интуитивно понятный и блестящий.

Итак, почему я не могу выбрать все значения с первого уровня индекса?

In [30]: df.loc[:, :, 'C1', :] 
--------------------------------------------------------------------------- 
IndexingError        Traceback (most recent call last) 
<ipython-input-30-57b56108d941> in <module>() 
----> 1 df.loc[:, :, 'C1', :] 

/usr/local/lib/python2.7/dist-packages/pandas/core/indexing.pyc in __getitem__(self, key) 
    1176  def __getitem__(self, key): 
    1177   if type(key) is tuple: 
-> 1178    return self._getitem_tuple(key) 
    1179   else: 
    1180    return self._getitem_axis(key, axis=0) 

/usr/local/lib/python2.7/dist-packages/pandas/core/indexing.pyc in _getitem_tuple(self, tup) 
    694 
    695   # no multi-index, so validate all of the indexers 
--> 696   self._has_valid_tuple(tup) 
    697 
    698   # ugly hack for GH #836 

/usr/local/lib/python2.7/dist-packages/pandas/core/indexing.pyc in _has_valid_tuple(self, key) 
    125   for i, k in enumerate(key): 
    126    if i >= self.obj.ndim: 
--> 127     raise IndexingError('Too many indexers') 
    128    if not self._has_valid_type(k, i): 
    129     raise ValueError("Location based indexing can only have [%s] " 

IndexingError: Too many indexers 

Наверняка это не намеренное поведение?

Примечание: Я знаю, что это возможно с df.xs('C1', level='third'), но текущее поведение .loc кажется непоследовательным.

ответ

10

Чтобы обезопасить себя (в смысле: это будет работать во всех случаях), необходимо индексировать как индекс строки и столбцы, для которых можно использовать pd.IndexSlice, чтобы сделать это легко:

In [26]: idx = pd.IndexSlice 

In [27]: df.loc[idx[:, :, 'C1', :],:] 
Out[27]: 
          value 
first second third fourth 
A0 B0  C1 D0   2 
        D1   3 
     B1  C1 D0   10 
        D1   11 
A1 B0  C1 D0   18 
        D1   19 
     B1  C1 D0   26 
        D1   27 
A2 B0  C1 D0   34 
        D1   35 
     B1  C1 D0   42 
        D1   43 
A3 B0  C1 D0   50 
        D1   51 
     B1  C1 D0   58 
        D1   59 

Здесь idx[:, :, 'C1', :] - это более простой способ написать [slice(None), slice(None),'C1', slice(None)]. Вместо pd.IndexSlice вы также можете использовать np.s_, который немного короче.

Причина, по которой другие работают, я не совсем уверен. Но смотрите примечание в документации здесь: http://pandas.pydata.org/pandas-docs/stable/advanced.html#using-slicers (первый красный окно с предупреждением), где говорится, что:

Вы должны указать все оси в .loc спецификатора, то есть индексатор для индекса и для столбцов. Их некоторые неоднозначные случаи, когда прошедший индексатор может быть неправильно интерпретирован как индексирование и осей, а не как MuliIndex для строк.

+0

Это первый раз, когда я действительно понял, о чем идет речь в разделе документации. – LondonRob

4

Причина, по которой это не работает, связана с необходимостью указания оси индексации (см. http://pandas.pydata.org/pandas-docs/stable/advanced.html). Альтернативное решение вашей проблемы, чтобы просто сделать это:

df.loc(axis=0)[:, :, 'C1', :] 

Панда путается иногда, когда индексы похожи или содержат одинаковые значения. Если у вас должен быть столбец с именем «C1» или что-то, что вам также нужно будет сделать в этом стиле отрезки/выбора.

Смежные вопросы