seleccionar de pandas de índice múltiple

91

Tengo un marco de datos de índices múltiples con columnas 'A' y 'B'.

¿Hay alguna manera de seleccionar filas filtrando en una columna del índice múltiple sin restablecer el índice a un índice de una sola columna?

Por ejemplo.

# has multi-index (A,B)
df
#can I do this? I know this doesn't work because the index is multi-index so I need to     specify a tuple

df.ix[df.A ==1]
silenciador
fuente
Relacionado: seleccione filas en pandas MultiIndex DataFrame (una amplia discusión sobre el mismo tema por mí).
cs95

Respuestas:

136

Una forma es utilizar el get_level_valuesmétodo Index:

In [11]: df
Out[11]:
     0
A B
1 4  1
2 5  2
3 6  3

In [12]: df.iloc[df.index.get_level_values('A') == 1]
Out[12]:
     0
A B
1 4  1

En 0.13 podrás usar xscon drop_levelargumento :

df.xs(1, level='A', drop_level=False) # axis=1 if columns

Nota: si se tratara de una columna MultiIndex en lugar de un índice, podría utilizar la misma técnica:

In [21]: df1 = df.T

In [22]: df1.iloc[:, df1.columns.get_level_values('A') == 1]
Out[22]:
A  1
B  4
0  1
Andy Hayden
fuente
53

También puede usar, queryque es muy legible en mi opinión y fácil de usar:

import pandas as pd

df = pd.DataFrame({'A': [1, 2, 3, 4], 'B': [10, 20, 50, 80], 'C': [6, 7, 8, 9]})
df = df.set_index(['A', 'B'])

      C
A B    
1 10  6
2 20  7
3 50  8
4 80  9

Por lo que tenía en mente, ahora puede simplemente hacer:

df.query('A == 1')

      C
A B    
1 10  6

También puede tener consultas más complejas usando and

df.query('A >= 1 and B >= 50')

      C
A B    
3 50  8
4 80  9

y or

df.query('A == 1 or B >= 50')

      C
A B    
1 10  6
3 50  8
4 80  9

También puede consultar en diferentes niveles de índice , p. Ej.

df.query('A == 1 or C >= 8')

volverá

      C
A B    
1 10  6
3 50  8
4 80  9

Si desea utilizar variables dentro de su consulta, puede utilizar@ :

b_threshold = 20
c_threshold = 8

df.query('B >= @b_threshold and C <= @c_threshold')

      C
A B    
2 20  7
3 50  8
Cleb
fuente
1
Gran respuesta, mucho más legible de hecho. ¿Sabe si es posible consultar dos campos en diferentes niveles de índice como:df.query('A == 1 or C >= 8')
obchardon
@obchardon: Eso parece funcionar bien; Edité mi respuesta usando tu ejemplo.
Cleb
1
Tengo tiempos y cadenas como multiíndice, lo que crea problemas en la expresión de la cadena. Sin embargo, df.query()funciona bien con variables si se hace referencia a ellas con una '@' dentro de la expresión en la consulta, por ejemplo df.query('A == @var) para una variable varen el entorno.
Solly
@Solly: Gracias, agregué esto a la respuesta.
Cleb
Sin embargo, ¿dónde está la indexación múltiple aquí?
Lamma
32

Puede utilizar DataFrame.xs():

In [36]: df = DataFrame(np.random.randn(10, 4))

In [37]: df.columns = [np.random.choice(['a', 'b'], size=4).tolist(), np.random.choice(['c', 'd'], size=4)]

In [38]: df.columns.names = ['A', 'B']

In [39]: df
Out[39]:
A      b             a
B      d      d      d      d
0 -1.406  0.548 -0.635  0.576
1 -0.212 -0.583  1.012 -1.377
2  0.951 -0.349 -0.477 -1.230
3  0.451 -0.168  0.949  0.545
4 -0.362 -0.855  1.676 -2.881
5  1.283  1.027  0.085 -1.282
6  0.583 -1.406  0.327 -0.146
7 -0.518 -0.480  0.139  0.851
8 -0.030 -0.630 -1.534  0.534
9  0.246 -1.558 -1.885 -1.543

In [40]: df.xs('a', level='A', axis=1)
Out[40]:
B      d      d
0 -0.635  0.576
1  1.012 -1.377
2 -0.477 -1.230
3  0.949  0.545
4  1.676 -2.881
5  0.085 -1.282
6  0.327 -0.146
7  0.139  0.851
8 -1.534  0.534
9 -1.885 -1.543

Si desea mantener el Anivel (el drop_levelargumento de palabra clave solo está disponible a partir de v0.13.0):

In [42]: df.xs('a', level='A', axis=1, drop_level=False)
Out[42]:
A      a
B      d      d
0 -0.635  0.576
1  1.012 -1.377
2 -0.477 -1.230
3  0.949  0.545
4  1.676 -2.881
5  0.085 -1.282
6  0.327 -0.146
7  0.139  0.851
8 -1.534  0.534
9 -1.885 -1.543
Phillip Cloud
fuente
1
Ja, acababa de actualizar mi respuesta con eso, Nota: solo disponible en 0.13.
Andy Hayden
Oh, bueno saberlo. Nunca recuerdo qué pequeñas comodidades se agregan en cada versión.
Phillip Cloud
Lol, de hecho, esta pregunta es una burla de la que inspiró esa conveniencia. :)
Andy Hayden
13

Comprender cómo acceder a pandas DataFrame con múltiples índices puede ayudarlo con todo tipo de tareas como esa.

Copie y pegue esto en su código para generar un ejemplo:

# hierarchical indices and columns
index = pd.MultiIndex.from_product([[2013, 2014], [1, 2]],
                                   names=['year', 'visit'])
columns = pd.MultiIndex.from_product([['Bob', 'Guido', 'Sue'], ['HR', 'Temp']],
                                     names=['subject', 'type'])

# mock some data
data = np.round(np.random.randn(4, 6), 1)
data[:, ::2] *= 10
data += 37

# create the DataFrame
health_data = pd.DataFrame(data, index=index, columns=columns)
health_data

Te dará una mesa como esta:

ingrese la descripción de la imagen aquí

Acceso estándar por columna

health_data['Bob']
type       HR   Temp
year visit      
2013    1   22.0    38.6
        2   52.0    38.3
2014    1   30.0    38.9
        2   31.0    37.3


health_data['Bob']['HR']
year  visit
2013  1        22.0
      2        52.0
2014  1        30.0
      2        31.0
Name: HR, dtype: float64

# filtering by column/subcolumn - your case:
health_data['Bob']['HR']==22
year  visit
2013  1         True
      2        False
2014  1        False
      2        False

health_data['Bob']['HR'][2013]    
visit
1    22.0
2    52.0
Name: HR, dtype: float64

health_data['Bob']['HR'][2013][1]
22.0

Acceso por fila

health_data.loc[2013]
subject Bob Guido   Sue
type    HR  Temp    HR  Temp    HR  Temp
visit                       
1   22.0    38.6    40.0    38.9    53.0    37.5
2   52.0    38.3    42.0    34.6    30.0    37.7

health_data.loc[2013,1] 
subject  type
Bob      HR      22.0
         Temp    38.6
Guido    HR      40.0
         Temp    38.9
Sue      HR      53.0
         Temp    37.5
Name: (2013, 1), dtype: float64

health_data.loc[2013,1]['Bob']
type
HR      22.0
Temp    38.6
Name: (2013, 1), dtype: float64

health_data.loc[2013,1]['Bob']['HR']
22.0

Rebanado de múltiples índices

idx=pd.IndexSlice
health_data.loc[idx[:,1], idx[:,'HR']]
    subject Bob Guido   Sue
type    HR  HR  HR
year    visit           
2013    1   22.0    40.0    53.0
2014    1   30.0    52.0    45.0
Harvey
fuente
esto da ValueError: cannot handle a non-unique multi-index!error
Coddy
5

Puede utilizar DataFrame.loc:

>>> df.loc[1]

Ejemplo

>>> print(df)
       result
A B C        
1 1 1       6
    2       9
  2 1       8
    2      11
2 1 1       7
    2      10
  2 1       9
    2      12

>>> print(df.loc[1])
     result
B C        
1 1       6
  2       9
2 1       8
  2      11

>>> print(df.loc[2, 1])
   result
C        
1       7
2      10
umn
fuente
Este es el mejor de los enfoques modernos IMO, donde df.loc [2, 1] ['result'] manejará columnas múltiples
Michael
esto funciona con cualquier número de enteros por alguna razón. por ejemplodf.loc[0], df.loc[1]....df.loc[n]
Coddy
2

Otra opcion es:

filter1 = df.index.get_level_values('A') == 1
filter2 = df.index.get_level_values('B') == 4

df.iloc[filter1 & filter2]
Out[11]:
     0
A B
1 4  1
Gonzalo Goral
fuente