2015-06-11 11 views
13

che ho letto the docs about slicers un milione di volte, ma non ho mai avuto la mia testa rotonda, quindi sto ancora cercando di capire come utilizzare loc a tagliare un DataFrame con un MultiIndex ."Troppi indicizzatori" con DataFrame.loc

Comincerò con il DataFrame da 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 

selezionare solo A0 e C1 valori, non posso fare:

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 

che funziona anche scegliendo tra tre livelli, e anche con tuples:

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 

Fin qui, intuitivo e brillante.

Quindi, perché non è possibile selezionare tutti i valori dal primo livello di indice?

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 

Sicuramente questo comportamento non è previsto?

Nota: so che questo è possibile con df.xs('C1', level='third') ma il comportamento corrente .loc sembra incoerente.

risposta

10

di essere al sicuro (nel senso: questo funzionerà in tutti i casi), è necessario indice sia indice di riga e colonne, per il quale è possibile utilizzare pd.IndexSlice di farlo facilmente:

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 

Qui idx[:, :, 'C1', :] è un modo più semplice per scrivere [slice(None), slice(None),'C1', slice(None)]. Invece di pd.IndexSlice, puoi anche utilizzare np.s_ un po 'più breve.

La ragione per cui gli altri funzionano, non ne sono del tutto sicuro. Ma vedere la nota nella documentazione qui: http://pandas.pydata.org/pandas-docs/stable/advanced.html#using-slicers (la prima finestra di avvertimento rossa) in cui si afferma che:

è necessario specificare tutti gli assi nella .loc specificatore, il che significa l'indicizzatore per l'indice e per le colonne. Sono alcuni casi ambigui in cui l'indicizzatore passato potrebbe essere interpretato erroneamente come indicizzazione entrambi gli assi, piuttosto che dire il MuliIndex per le righe.

+0

Questa è la prima volta che ho capito veramente di cosa tratta la sezione dei documenti. – LondonRob

4

Il motivo per cui questo non funziona è legato alla necessità di specificare l'asse di indicizzazione (indicato in http://pandas.pydata.org/pandas-docs/stable/advanced.html). Una soluzione alternativa al vostro problema è quello di fare semplicemente questo:

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

Panda viene a volte confuso quando gli indici sono simili o contengono valori simili. Se dovessi avere una colonna chiamata "C1" o qualcosa dovresti farlo anche con questo stile di affettare/selezionare.