"Слишком много индексировщиков" с DataFrame.loc
Я прочитал документы о слайсерах миллион раз, но никогда не обнимал его, поэтому я все еще пытаюсь чтобы определить, как использовать loc
, чтобы нарезать a DataFrame
с помощью MultiIndex
.
Я начну с DataFrame
из этого SO-ответа:
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
кажется непоследовательным.
Ответы
Ответ 1
Чтобы быть в безопасности (в смысле: это будет работать во всех случаях), вам нужно индексировать индекс строки и столбцы, для которых вы можете легко использовать 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 для строк.
Ответ 2
Причина, по которой это не работает, связана с необходимостью указания оси индексации (упомянутой в http://pandas.pydata.org/pandas-docs/stable/advanced.html). Альтернативное решение вашей проблемы состоит в том, чтобы просто сделать это:
df.loc(axis=0)[:, :, 'C1', :]
Pandas иногда путается, когда индексы похожи или содержат одинаковые значения. Если у вас должен быть столбец с именем "C1" или что-то, что вам также нужно будет сделать в этом стиле для нарезки/выбора.