web-dev-qa-db-fra.com

Sélection de lignes dans une trame de données Pandas avec un index composé (hiérarchique)

Je soupçonne que cela est trivial, mais je n'ai pas encore découvert l'incantation qui me permettra de sélectionner des lignes dans une trame de données Pandas basée sur les valeurs d'une clé hiérarchique. Donc, par exemple, imaginez nous avons le dataframe suivant:

import pandas
df = pandas.DataFrame({'group1': ['a','a','a','b','b','b'],
                       'group2': ['c','c','d','d','d','e'],
                       'value1': [1.1,2,3,4,5,6],
                       'value2': [7.1,8,9,10,11,12]
})
df = df.set_index(['group1', 'group2'])

df ressemble comme on peut s'y attendre:

enter image description here

Si df n'était pas indexé sur le groupe 1, je pourrais faire ce qui suit:

df['group1' == 'a']

Mais cela échoue sur cette trame de données avec un index. Alors peut-être que je devrais penser à cela comme une série Pandas avec un index hiérarchique:

df['a','c']

Nan. Cela échoue également.

Alors, comment puis-je sélectionner toutes les lignes où:

  1. group1 == 'a'
  2. group1 == 'a' & group2 == 'c'
  3. group2 == 'c'
  4. groupe1 dans ['a', 'b', 'c']
33
JD Long

Essayez d'utiliser xs pour être très précis:

In [5]: df.xs('a', level=0)
Out[5]: 
        value1  value2
group2                
c          1.1     7.1
c          2.0     8.0
d          3.0     9.0

In [6]: df.xs('c', level='group2')
Out[6]: 
        value1  value2
group1                
a          1.1     7.1
a          2.0     8.0
49
Wes McKinney

Une syntaxe comme celle-ci fonctionnera:

df.ix['a']
df.ix['a'].ix['c']

puisque group1 et group2 sont des indices. Veuillez pardonner ma tentative précédente!

Pour arriver au deuxième indice seulement, je pense que vous devez échanger les indices:

df.swaplevel(0,1).ix['c']

Mais je suis sûr que Wes me corrigera si je me trompe.

9
Chris Fonnesbeck

Dans Python 0.19.0 il y a une nouvelle approche suggérée, qui est expliquée ici 1 . Je crois que l'exemple le plus clair qu'ils donnent est le suivant, dans lequel ils partent de une indexation à quatre niveaux. Voici comment la trame de données est réalisée:

In [46]: def mklbl(prefix,n):
   ....:     return ["%s%s" % (prefix,i)  for i in range(n)]
   ....: 

In [47]: miindex = pd.MultiIndex.from_product([mklbl('A',4),
   ....:                                       mklbl('B',2),
   ....:                                       mklbl('C',4),
   ....:                                       mklbl('D',2)])
   ....: 

In [48]: micolumns = pd.MultiIndex.from_tuples([('a','foo'),('a','bar'),
   ....:                                        ('b','foo'),('b','bah')],
   ....:                                       names=['lvl0', 'lvl1'])
   ....: 

In [49]: dfmi = pd.DataFrame(np.arange(len(miindex)*len(micolumns)).reshape((len(miindex),len(micolumns))),
   ....:                     index=miindex,
   ....:                     columns=micolumns).sort_index().sort_index(axis=1)
   ....: 

In [50]: dfmi
Out[50]: 
lvl0           a         b     
lvl1         bar  foo  bah  foo
A0 B0 C0 D0    1    0    3    2
         D1    5    4    7    6
      C1 D0    9    8   11   10
         D1   13   12   15   14
      C2 D0   17   16   19   18
         D1   21   20   23   22
      C3 D0   25   24   27   26
...          ...  ...  ...  ...
A3 B1 C0 D1  229  228  231  230
      C1 D0  233  232  235  234
         D1  237  236  239  238
      C2 D0  241  240  243  242
         D1  245  244  247  246
      C3 D0  249  248  251  250
         D1  253  252  255  254

Et voici comment ils sélectionnent les différentes lignes:

In [51]: dfmi.loc[(slice('A1','A3'),slice(None), ['C1','C3']),:]
Out[51]: 
lvl0           a         b     
lvl1         bar  foo  bah  foo
A1 B0 C1 D0   73   72   75   74
         D1   77   76   79   78
      C3 D0   89   88   91   90
         D1   93   92   95   94
   B1 C1 D0  105  104  107  106
         D1  109  108  111  110
      C3 D0  121  120  123  122
...          ...  ...  ...  ...
A3 B0 C1 D1  205  204  207  206
      C3 D0  217  216  219  218
         D1  221  220  223  222
   B1 C1 D0  233  232  235  234
         D1  237  236  239  238
      C3 D0  249  248  251  250
         D1  253  252  255  254

Donc tout simplement, dans df.loc[(indices),:], vous spécifiez les indices que vous souhaitez sélectionner par niveau, du plus haut au plus bas. Si vous ne souhaitez pas sélectionner les niveaux d'indice les plus bas, vous pouvez omettre de les spécifier. Si vous ne voulez pas faire de tranche entre les autres niveaux spécifiés, vous ajoutez slice(None). Les deux cas sont illustrés dans l'exemple, où le niveau D est omis et le niveau B est spécifié entre A et C.

1
Robin Kramer