Tengo un marco de datos con un índice jerárquico en el eje 1 (columnas) (de una groupby.agg
operación):
USAF WBAN year month day s_PC s_CL s_CD s_CNT tempf
sum sum sum sum amax amin
0 702730 26451 1993 1 1 1 0 12 13 30.92 24.98
1 702730 26451 1993 1 2 0 0 13 13 32.00 24.98
2 702730 26451 1993 1 3 1 10 2 13 23.00 6.98
3 702730 26451 1993 1 4 1 0 12 13 10.04 3.92
4 702730 26451 1993 1 5 3 0 10 13 19.94 10.94
Quiero aplanarlo, para que se vea así (los nombres no son críticos, podría cambiar el nombre):
USAF WBAN year month day s_PC s_CL s_CD s_CNT tempf_amax tmpf_amin
0 702730 26451 1993 1 1 1 0 12 13 30.92 24.98
1 702730 26451 1993 1 2 0 0 13 13 32.00 24.98
2 702730 26451 1993 1 3 1 10 2 13 23.00 6.98
3 702730 26451 1993 1 4 1 0 12 13 10.04 3.92
4 702730 26451 1993 1 5 3 0 10 13 19.94 10.94
¿Cómo hago esto? (Lo he intentado mucho, fue en vano).
Por una sugerencia, aquí está la cabeza en forma dict
{('USAF', ''): {0: '702730',
1: '702730',
2: '702730',
3: '702730',
4: '702730'},
('WBAN', ''): {0: '26451', 1: '26451', 2: '26451', 3: '26451', 4: '26451'},
('day', ''): {0: 1, 1: 2, 2: 3, 3: 4, 4: 5},
('month', ''): {0: 1, 1: 1, 2: 1, 3: 1, 4: 1},
('s_CD', 'sum'): {0: 12.0, 1: 13.0, 2: 2.0, 3: 12.0, 4: 10.0},
('s_CL', 'sum'): {0: 0.0, 1: 0.0, 2: 10.0, 3: 0.0, 4: 0.0},
('s_CNT', 'sum'): {0: 13.0, 1: 13.0, 2: 13.0, 3: 13.0, 4: 13.0},
('s_PC', 'sum'): {0: 1.0, 1: 0.0, 2: 1.0, 3: 1.0, 4: 3.0},
('tempf', 'amax'): {0: 30.920000000000002,
1: 32.0,
2: 23.0,
3: 10.039999999999999,
4: 19.939999999999998},
('tempf', 'amin'): {0: 24.98,
1: 24.98,
2: 6.9799999999999969,
3: 3.9199999999999982,
4: 10.940000000000001},
('year', ''): {0: 1993, 1: 1993, 2: 1993, 3: 1993, 4: 1993}}
df[:5].to_dict()
como ejemplo para que otros la lean en su conjunto de datos?pandas
rastreador de problemas para implementar un método dedicado para esto.dat.columns = dat.columns.to_flat_index()
. Función de pandas incorporada.Respuestas:
Creo que la forma más fácil de hacer esto sería establecer las columnas en el nivel superior:
Nota: si el nivel to tiene un nombre, también puede acceder a él por este, en lugar de 0.
.
Si desea combinar /
join
su MultiIndex en un índice (suponiendo que solo tenga entradas de cadena en sus columnas) puede:Nota: debemos
strip
el espacio en blanco para cuando no hay un segundo índice.fuente
['_'.join(col).rstrip('_') for col in df.columns.values]
sum s_CD
lugar des_CD sum
, uno puede hacerlodf.columns = ['_'.join(col).rstrip('_') for col in [c[::-1] for c in df.columns.values]]
.fuente
pd.DataFrame(df.to_records(), columns=df.index.names + list(df.columns))
pd.DataFrame(df_volume.to_records(), index=df_volume.index).drop('index', axis=1)
Todas las respuestas actuales en este hilo deben haber sido un poco anticuadas. A partir de la
pandas
versión 0.24.0,.to_flat_index()
hace lo que necesita.De la propia documentación de panda :
Un ejemplo simple de su documentación:
Aplicando
to_flat_index()
:Usándolo para reemplazar la
pandas
columna existenteUn ejemplo de cómo lo usaría
dat
, que es un DataFrame con unaMultiIndex
columna:fuente
La respuesta de Andy Hayden es sin duda la forma más fácil: si desea evitar las etiquetas de columna duplicadas, debe modificar un poco
fuente
fuente
Y si desea conservar la información de agregación del segundo nivel del índice múltiple, puede probar esto:
fuente
new_cols
no está definidoLa forma más pitónica de hacer esto para usar la
map
función.Salida
print(df.columns)
:Actualización usando Python 3.6+ con cadena f:
Salida:
fuente
La solución más fácil e intuitiva para mí fue combinar los nombres de columna usando get_level_values . Esto evita nombres de columna duplicados cuando realiza más de una agregación en la misma columna:
Si desea un separador entre columnas, puede hacerlo. Esto devolverá lo mismo que el comentario de Seiji Armstrong sobre la respuesta aceptada que solo incluye guiones bajos para columnas con valores en ambos niveles de índice:
Sé que esto hace lo mismo que la gran respuesta de Andy Hayden anterior, pero creo que es un poco más intuitivo de esta manera y es más fácil de recordar (por lo que no tengo que seguir refiriéndome a este hilo), especialmente para los usuarios novatos de pandas .
Este método también es más extensible en el caso de que pueda tener 3 niveles de columna.
fuente
Después de leer todas las respuestas, se me ocurrió esto:
Uso:
Dado un marco de datos:
Método de agregación única : las variables resultantes reciben el mismo nombre que la fuente :
df.groupby(by="grouper",
as_index = False)
o.agg(...)
.reset_index ()Variable de fuente única, agregaciones múltiples : variables resultantes con nombres de estadísticas :
a = df.groupby(..).agg(..); a.columns = a.columns.droplevel(0); a.reset_index()
.Múltiples variables, múltiples agregaciones : variables resultantes llamadas (varname) _ (statname) :
a.columns = ["_".join(filter(None, map(str, levels))) for levels in a.columns.values]
bajo el capó (ya que esta forma deagg()
resultados en lasMultiIndex
columnas).my_flatten_cols
ayudante, podría ser más fácil escribir la solución sugerida por @Seigi :a.columns = ["_".join(t).rstrip("_") for t in a.columns.values]
que funciona de manera similar en este caso (pero falla si tiene etiquetas numéricas en las columnas)a.columns = ["_".join(tuple(map(str, t))).rstrip("_") for t in a.columns.values]
), pero no entiendo por quétuple()
es necesaria la llamada, y creorstrip()
que solo es necesaria si algunas columnas tienen un descriptor como("colname", "")
( que puede suceder sireset_index()
antes de intentar arreglarlo.columns
)Desea nombrar las variables resultantes de forma manual: (esto es obsoleta desde pandas 0.20.0 con otra alternativa adecuada a partir de 0,23 )
res.columns = ['A_sum', 'B_sum', 'count']
o.join()
ing múltiplesgroupby
declaraciones.Casos manejados por la función auxiliar
map(str, ..)
filter(None, ..)
columns.values
devuelve los nombres (str
no las tuplas).agg()
es posible que deba mantener la etiqueta de la parte inferior de una columna o concatenar varias etiquetasreset_index()
poder trabajar con las columnas de agrupación de forma regular, por lo que lo hace de manera predeterminadafuente
tuple()
es necesario, puede comentar la publicación de jxstanford. De lo contrario, podría ser útil para inspeccionar el.columns.values
en el ejemplo dado:[('val1', 'min'), (2, 'sum'), (2, 'size')]
. 1)for t in a.columns.values
recorre las columnas, para la segunda columnat == (2, 'sum')
; 2) semap(str, t)
aplicastr()
a cada "nivel", lo que resulta en('2', 'sum')
; 3)"_".join(('2','sum'))
resulta en "2_sum",Una solución general que maneja múltiples niveles y tipos mixtos:
fuente
df.columns = ['_'.join(tuple(map(str, t))).rstrip('_') for t in df.columns.values]
Tal vez un poco tarde, pero si no le preocupan los nombres de columna duplicados:
fuente
(year, )
y(tempf, amax)
En caso de que desee tener un separador en el nombre entre niveles, esta función funciona bien.
fuente
df.columns = ["_".join(filter(None, c)) for c in df.columns]
Siguiendo @jxstanford y @ tvt173, escribí una función rápida que debería funcionar, independientemente de los nombres de columna string / int:
fuente
También puedes hacer lo siguiente. Considere
df
ser su marco de datos y asuma un índice de dos niveles (como es el caso en su ejemplo)fuente
Compartiré una forma directa que funcionó para mí.
fuente
Para aplanar un MultiIndex dentro de una cadena de otros métodos DataFrame, defina una función como esta:
Luego use el
pipe
método para aplicar esta función en la cadena de métodos DataFrame, despuésgroupby
yagg
pero antes de cualquier otro método en la cadena:fuente
Otra rutina simple.
fuente