python - multiindex - pandas pivot
Cómo dividir un DataFrame de MultiIndex con el MultiIndex de otro (3)
Aquí hay una manera de obtener esta porción:
df.sort_index(inplace=True)
idx = pd.IndexSlice
df.loc[idx[:, (''foo'',''bar''), ''can''], :]
flexible
hi
a b c
1 bar can 3
foo can 1
2 bar can 7
foo can 5
3 bar can 11
foo can 9
Tenga en cuenta que es posible que necesite ordenar MultiIndex antes de poder cortarlo. Los pandas bien tienen la amabilidad de advertirte si necesitas hacerlo:
KeyError: ''MultiIndex Slicing requires the index to be fully lexsorted tuple len (3), lexsort depth (1)''
Puede leer más sobre cómo usar slicers en los documentos
Si por alguna razón, usar slicers no es una opción, aquí hay una manera de obtener el mismo sector utilizando el método .isin()
:
df[df.index.get_level_values(''b'').isin(ix_use.get_level_values(0)) & df.index.get_level_values(''c'').isin(ix_use.get_level_values(1))]
Lo cual claramente no es tan conciso.
ACTUALIZAR:
Para las condiciones que ha actualizado aquí hay una forma de hacerlo:
cond1 = (df.index.get_level_values(''b'').isin([''foo''])) & (df.index.get_level_values(''c'').isin([''can'']))
cond2 = (df.index.get_level_values(''b'').isin([''bar''])) & (df.index.get_level_values(''c'').isin([''baz'']))
df[cond1 | cond2]
productor:
hi
a b c
1 foo can 1
bar baz 2
2 foo can 5
bar baz 6
3 foo can 9
bar baz 10
Tengo un marco de datos de pandas con 3 niveles de un MultiIndex. Estoy tratando de extraer filas de este marco de datos de acuerdo con una lista de valores que corresponden a dos de los niveles.
Tengo algo como esto:
ix = pd.MultiIndex.from_product([[1, 2, 3], [''foo'', ''bar''], [''baz'', ''can'']], names=[''a'', ''b'', ''c''])
data = np.arange(len(ix))
df = pd.DataFrame(data, index=ix, columns=[''hi''])
print(df)
hi
a b c
1 foo baz 0
can 1
bar baz 2
can 3
2 foo baz 4
can 5
bar baz 6
can 7
3 foo baz 8
can 9
bar baz 10
can 11
Ahora quiero tomar todas las filas donde los niveles de índice ''b'' y ''c'' están en este índice:
ix_use = pd.MultiIndex.from_tuples([(''foo'', ''can''), (''bar'', ''baz'')], names=[''b'', ''c''])
es decir, los valores de hi
having (''foo'', ''can'')
o (''bar'', ''baz'')
en los niveles b
: (1, 2, 5, 6, 9, 10)
.
Así que me gustaría tomar una slice(None)
en el primer nivel, y sacar tuplas específicas en el segundo y tercer niveles.
Inicialmente pensé que pasar un objeto de varios índices a .loc eliminaría los valores / niveles que quería, pero esto no está funcionando. ¿Cuál es la mejor manera de hacer algo como esto?
Me parece interesante que esto no funcione:
In [45]: df.loc[(idx[:, ''foo'', ''can''], idx[:, ''bar'', ''baz'']), ]
Out[45]:
hi
a b c
1 bar baz 2
can 3
foo baz 0
can 1
2 bar baz 6
can 7
foo baz 4
can 5
3 bar baz 10
can 11
foo baz 8
can 9
De alguna manera, parece que "debería". En cualquier caso, aquí hay una solución razonable:
Supongamos que las tuplas que desea DataFrame
están en el índice de otro DataFrame
(¡ya que parece que probablemente estén en su caso!).
In [53]: ix_use = pd.MultiIndex.from_tuples([(''foo'', ''can''), (''bar'', ''baz'')], names=[''b'', ''c''])
In [55]: other = pd.DataFrame(dict(a=1), index=ix_use)
In [56]: other
Out[56]:
a
b c
foo can 1
bar baz 1
Ahora, para cortar df
por el índice de other
, podemos usar el hecho de que .loc
/ .ix
permite dar una lista de tuplas (vea el último ejemplo aquí ).
Primero construyamos la lista de tuplas que queremos:
In [13]: idx = [(x, ) + y for x in df.index.levels[0] for y in other.index.values]
In [14]: idx
Out[14]:
[(1, ''foo'', ''can''),
(1, ''bar'', ''baz''),
(2, ''foo'', ''can''),
(2, ''bar'', ''baz''),
(3, ''foo'', ''can''),
(3, ''bar'', ''baz'')]
Ahora podemos pasar esta lista a .ix
o .loc
:
In [17]: df.ix[idx]
Out[17]:
hi
a b c
1 foo can 1
bar baz 2
2 foo can 5
bar baz 6
3 foo can 9
bar baz 10
Recomendaría el método query()
al igual que en este Q & A.
Simplemente usando esto, que creo que es una forma más natural de expresar:
In [27]: df.query("(b == ''foo'' and c == ''can'') or (b == ''bar'' and c == ''baz'')")
Out[27]:
hi
a b c
1 foo can 1
bar baz 2
2 foo can 5
bar baz 6
3 foo can 9
bar baz 10