2015-06-11 6 views
13

ich the docs about slicers eine Million Mal gelesen habe, aber habe nie meinen Kopf um ihn herum, so dass ich bin immer noch versuchen, herauszufinden, wie loc zu verwenden, um ein DataFrame mit einem MultiIndex zu schneiden .„Zu viele Indexer“ mit DataFrame.loc

Ich werde beginnen mit der DataFrame von 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 

Um nur A0 und C1 Werte auswählen, was ich tun kann:

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 

die ebenfalls aus drei Ebenen funktioniert die Auswahl und sogar mit Tupeln:

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 

Bisher intuitiv und brilliant.

Warum kann ich nicht alle Werte aus der ersten Indexebene auswählen?

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 

Sicher ist dies nicht beabsichtigtes Verhalten?

Hinweis: Ich weiß, dass dies mit df.xs('C1', level='third') möglich ist, aber das aktuelle .loc Verhalten scheint inkonsistent.

Antwort

10

sicher sein (im Sinne: dies in allen Fällen funktionieren wird), müssen Sie beide Zeilenindex und Spalten-Index, für die Sie leicht diese pd.IndexSlice tun können:

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 

Hier idx[:, :, 'C1', :] ist eine einfachere Art zu schreiben [slice(None), slice(None),'C1', slice(None)]. Anstelle von pd.IndexSlice können Sie auch np.s_ verwenden, was etwas kürzer ist.

Der Grund, dass die anderen arbeiten, bin ich mir nicht ganz sicher. Aber sehen Sie hier die Notiz in der Dokumentation: http://pandas.pydata.org/pandas-docs/stable/advanced.html#using-slicers (die erste rote Warnfeld), in dem es heißt, dass:

Sie sollten alle Achsen im .loc Spezifizierer angeben, was bedeutet, den Indexer für den Index und für die Spalten. Sie sind einige mehrdeutige Fälle, in denen der übergebene Indexer falsch interpretiert werden könnte als beide Achsen, anstatt in den MuliIndex für die Zeilen.

+0

Dies ist das erste Mal, dass ich wirklich verstanden habe, worum es in der Sektion der Dokumentation geht. – LondonRob

4

Der Grund, warum dies nicht funktioniert, hängt von der Notwendigkeit ab, die Achse der Indizierung anzugeben (erwähnt in http://pandas.pydata.org/pandas-docs/stable/advanced.html). Eine alternative Lösung für Ihr Problem ist einfach, dies zu tun:

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

Pandas manchmal verwirrt wird, wenn Indizes ähnlich oder enthält Ähnliche Werte. Wenn Sie eine Spalte mit dem Namen "C1" oder etwas haben würden, müssten Sie dies auch unter diesem Schnitt-/Auswahlstil tun.