Tengo DataFrame con columnas MultiIndex que tienen este aspecto:
# sample data col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'], ['a', 'b', 'c', 'a', 'b', 'c']]) data = pd.DataFrame(np.random.randn(4, 6), columns=col) data
¿Cuál es la forma correcta y sencilla de seleccionar solo columnas específicas (por ejemplo, ['a', 'c']
, no un rango) desde el segundo nivel?
Actualmente lo estoy haciendo así:
import itertools tuples = [i for i in itertools.product(['one', 'two'], ['a', 'c'])] new_index = pd.MultiIndex.from_tuples(tuples) print(new_index) data.reindex_axis(new_index, axis=1)
Sin embargo, no parece una buena solución, ya que tengo que eliminar las itertools
, crear otro MultiIndex a mano y luego reindexar (y mi código real es aún más desordenado, ya que las listas de columnas no son tan fáciles de obtener). Estoy bastante seguro de que tiene que haber alguna forma ix
o xs
de hacer esto, pero todo lo que probé resultó en errores.
No es genial, pero tal vez:
>>> data one two abcabc 0 -0.927134 -1.204302 0.711426 0.854065 -0.608661 1.140052 1 -0.690745 0.517359 -0.631856 0.178464 -0.312543 -0.418541 2 1.086432 0.194193 0.808235 -0.418109 1.055057 1.886883 3 -0.373822 -0.012812 1.329105 1.774723 -2.229428 -0.617690 >>> data.loc[:,data.columns.get_level_values(1).isin({"a", "c"})] one two acac 0 -0.927134 0.711426 0.854065 1.140052 1 -0.690745 -0.631856 0.178464 -0.418541 2 1.086432 0.808235 -0.418109 1.886883 3 -0.373822 1.329105 1.774723 -0.617690
¿trabajaría?
Creo que hay una forma mucho mejor (ahora), por lo que me molesto en sacar esta pregunta (que fue el resultado principal de Google) de las sombras:
data.select(lambda x: x[1] in ['a', 'b'], axis=1)
da su salida esperada en una rápida y limpia de una sola línea:
one two abab 0 -0.341326 0.374504 0.534559 0.429019 1 0.272518 0.116542 -0.085850 -0.330562 2 1.982431 -0.420668 -0.444052 1.049747 3 0.162984 -0.898307 1.762208 -0.101360
Es principalmente autoexplicativo, el [1]
refiere al nivel.
Puedes usar cualquiera, loc
o ix
voy a mostrar un ejemplo con loc
:
data.loc[:, [('one', 'a'), ('one', 'c'), ('two', 'a'), ('two', 'c')]]
Cuando tiene un DataFrame MultiIndexed, y quiere filtrar solo algunas de las columnas, debe pasar una lista de tuplas que coincidan con esas columnas. Así que el enfoque de itertools estaba bastante bien, pero no es necesario crear un nuevo MultiIndex:
data.loc[:, list(itertools.product(['one', 'two'], ['a', 'c']))]
Para seleccionar todas las columnas denominadas 'a'
y 'c'
en el segundo nivel de su indexador de columnas, puede usar los segmentadores de datos:
>>> data.loc[:, (slice(None), ('a', 'c'))] one two acac 0 -0.983172 -2.495022 -0.967064 0.124740 1 0.282661 -0.729463 -0.864767 1.716009 2 0.942445 1.276769 -0.595756 -0.973924 3 2.182908 -0.267660 0.281916 -0.587835
Aquí puedes leer más sobre los rebanadores.
Un riff un poco más fácil, en mi opinión, sobre la respuesta de Marc P. usando slice :
import pandas as pd col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'], ['a', 'b', 'c', 'a', 'b', 'c']]) data = pd.DataFrame(np.random.randn(4, 6), columns=col) data.loc[:, pd.IndexSlice[:, ['a', 'c']]] one two acac 0 -1.731008 0.718260 -1.088025 -1.489936 1 -0.681189 1.055909 1.825839 0.149438 2 -1.674623 0.769062 1.857317 0.756074 3 0.408313 1.291998 0.833145 -0.471879
A partir de los pandas 0.21 o menos, .select está en desuso a favor de .loc .
v0.23 + Respuesta.
El uso de pd.IndexSlice
hace que loc
sea una opción más preferible para ix
y select
.
Puede encontrar más información sobre la segmentación y el filtrado de MultiIndexes en ¿Cómo puedo segmentar o filtrar los niveles de MultiIndex DataFrame? .
DataFrame.loc
con pd.IndexSlice
# Setup col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'], ['a', 'b', 'c', 'a', 'b', 'c']]) data = pd.DataFrame('x', index=range(4), columns=col) data one two abcabc 0 xxxxxx 1 xxxxxx 2 xxxxxx 3 xxxxxx
data.loc[:, pd.IndexSlice[:, ['a', 'c']]] one two acac 0 xxxx 1 xxxx 2 xxxx 3 xxxx
Alternativamente, puede elegir un parámetro de axis
para loc
de forma explícita desde qué eje está indexando:
data.loc(axis=1)[pd.IndexSlice[:, ['a', 'c']]] one two acac 0 xxxx 1 xxxx 2 xxxx 3 xxxx
MultiIndex.get_level_values
Llamar a data.columns.get_level_values
para filtrar con loc
es otra opción:
data.loc[:, data.columns.get_level_values(1).isin(['a', 'c'])] one two acac 0 xxxx 1 xxxx 2 xxxx 3 xxxx
Esto naturalmente puede permitir el filtrado de cualquier expresión condicional en un solo nivel. Aquí hay un ejemplo aleatorio con filtrado lexicográfico:
data.loc[:, data.columns.get_level_values(1) > 'b'] one two cc 0 xx 1 xx 2 xx 3 xx