Cómo aplanar un índice jerárquico en columnas

Resuelto Ross R asked hace 11 años • 20 respuestas

Tengo un data frame con un índice jerárquico en el eje 1 (columnas) (de una groupby.aggoperació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, sin éxito).

Por sugerencia, aquí está el encabezado en forma de dictado.

{('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}}
Ross R avatar Jan 25 '13 01:01 Ross R
Aceptado

Creo que la forma más sencilla de hacerlo sería establecer las columnas en el nivel superior:

df.columns = df.columns.get_level_values(0)

Nota: si el nivel de destino tiene un nombre, también puede acceder a él mediante este, en lugar de 0.

.

Si desea combinar joinsu MultiIndex en un índice (suponiendo que solo tenga entradas de cadena en sus columnas), puede:

df.columns = [' '.join(col).strip() for col in df.columns.values]

Nota: debemos stripdejar el espacio en blanco para cuando no haya un segundo índice.

In [11]: [' '.join(col).strip() for col in df.columns.values]
Out[11]: 
['USAF',
 'WBAN',
 'day',
 'month',
 's_CD sum',
 's_CL sum',
 's_CNT sum',
 's_PC sum',
 'tempf amax',
 'tempf amin',
 'year']
Andy Hayden avatar Jan 24 '2013 18:01 Andy Hayden

Todas las respuestas actuales en este hilo deben haber estado un poco anticuadas. A partir de pandasla versión 0.24.0, .to_flat_index()hace lo que necesita.

De la propia documentación de panda :

MultiIndex.to_flat_index()

Convierta un MultiIndex en un índice de tuplas que contenga los valores de nivel.

Un ejemplo simple de su documentación:

import pandas as pd
print(pd.__version__) # '0.23.4'
index = pd.MultiIndex.from_product(
        [['foo', 'bar'], ['baz', 'qux']],
        names=['a', 'b'])

print(index)
# MultiIndex(levels=[['bar', 'foo'], ['baz', 'qux']],
#           codes=[[1, 1, 0, 0], [0, 1, 0, 1]],
#           names=['a', 'b'])

Aplicando to_flat_index():

index.to_flat_index()
# Index([('foo', 'baz'), ('foo', 'qux'), ('bar', 'baz'), ('bar', 'qux')], dtype='object')

Utilizándolo para reemplazar pandasla columna existente

Un ejemplo de cómo lo usarías dat, que es un DataFrame con una MultiIndexcolumna:

dat = df.loc[:,['name','workshop_period','class_size']].groupby(['name','workshop_period']).describe()
print(dat.columns)
# MultiIndex(levels=[['class_size'], ['count', 'mean', 'std', 'min', '25%', '50%', '75%', 'max']],
#            codes=[[0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 2, 3, 4, 5, 6, 7]])

dat.columns = dat.columns.to_flat_index()
print(dat.columns)
# Index([('class_size', 'count'),  ('class_size', 'mean'),
#     ('class_size', 'std'),   ('class_size', 'min'),
#     ('class_size', '25%'),   ('class_size', '50%'),
#     ('class_size', '75%'),   ('class_size', 'max')],
#  dtype='object')

Aplanamiento y cambio de nombre in situ

Puede que valga la pena señalar cómo puedes combinar eso con una comprensión de lista simple (gracias @Skippy y @mmann1123) para unir los elementos de modo que los nombres de las columnas resultantes sean cadenas simples separadas por, por ejemplo, guiones bajos:

dat.columns = ["_".join(a) for a in dat.columns.to_flat_index()]
onlyphantom avatar Apr 19 '2019 05:04 onlyphantom