fusionar dos marcos de datos y agregar nivel de columna con nombres

9

Hola, he estado investigando los métodos de concat, join y fusion para pandas y parece que no puedo encontrar lo que quiero.

Supongamos que tengo dos marcos de datos

A = pd.DataFrame("A",index=[0,1,2,3,4],columns=['Col 1','Col 2','Col 3'])
B = pd.DataFrame("B",index=[0,1,2,3,4],columns=['Col 1','Col 2','Col 3'])
>>> A
  Col 1 Col 2 Col 3
0     A     A     A
1     A     A     A
2     A     A     A
3     A     A     A
4     A     A     A
>>> B
  Col 1 Col 2 Col 3
0     B     B     B
1     B     B     B
2     B     B     B
3     B     B     B
4     B     B     B

Ahora quiero hacer un nuevo marco de datos con las columnas fusionadas, creo que es más fácil explicar si hago un índice múltiple de cómo quiero que las columnas

index = pd.MultiIndex.from_product([A.columns.values,['A','B']])
>>> index
MultiIndex(levels=[['Col 1', 'Col 2', 'Col 3'], ['A', 'B']],
           labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]])

Ahora, si hago un marco de datos vacío con este índice múltiple para las columnas

empty_df = pd.DataFrame('-',index=A.index,columns=index)
>>> empty_df
  Col 1    Col 2    Col 3
      A  B     A  B     A  B
0     -  -     -  -     -  -
1     -  -     -  -     -  -
2     -  -     -  -     -  -
3     -  -     -  -     -  -
4     -  -     -  -     -  -

Mi pregunta es, ¿qué combinación, concat o join utilizo para obtener eso? He intentado varias cosas para concat ... interior, exterior, etc. Parece que no puedo encontrar lo que quiero. Lo único que se me ocurre es hacer el marco de datos vacío y luego volver a llenarlo.

Editar: Después de probar la respuesta de Jezrael, está cerca pero no exactamente. ¿Lo que quiero es como columnas anidadas de tipo? Por ejemplo

empty_df['Col 1']
>>> empty_df['Col 1']
   A  B
0  -  -
1  -  -
2  -  -
3  -  -
4  -  -

O

>>> empty_df['Col 1']['A']
0    -
1    -
2    -
3    -
4    -
Name: A, dtype: object

Así que esta es una solución que se me ocurrió pero es iterar sobre las columnas.

row_idx = A.index.union(B.index)
col_idx = pd.MultiIndex.from_product([A.columns.values,['A','B']])
new_df = pd.DataFrame('-',index=row_idx,columns=col_idx)
for column in A.columns:
   new_df.loc[:,(column,'A')] = A[column]
   new_df.loc[:,(column,'B')] = B[column]
>>> new_df
  Col 1    Col 2    Col 3
      A  B     A  B     A  B
0     A  B     A  B     A  B
1     A  B     A  B     A  B
2     A  B     A  B     A  B
3     A  B     A  B     A  B
4     A  B     A  B     A  B
>>> new_df['Col 1']
   A  B
0  A  B
1  A  B
2  A  B
3  A  B
4  A  B
>>> new_df['Col 1']['A']
0    A
1    A
2    A
3    A
4    A
Name: A, dtype: object
Melendowski
fuente

Respuestas:

8

Creo que se necesita concatcon keysparámetros y axis=1, por último cambio de orden de los niveles de DataFrame.swaplevely clasificación de primer nivel por DataFrame.sort_index:

df1 = (pd.concat([A, B], axis=1, keys=('A','B'))
         .swaplevel(0,1, axis=1)
         .sort_index(axis=1, level=0))
print (df1)
  Col 1    Col 2    Col 3   
      A  B     A  B     A  B
0     A  B     A  B     A  B
1     A  B     A  B     A  B
2     A  B     A  B     A  B
3     A  B     A  B     A  B
4     A  B     A  B     A  B

Para trabajar con MultiIndexes posible utilizar DataFrame.xs:

print (df1.xs('Col 1', axis=1, level=0))
   A  B
0  A  B
1  A  B
2  A  B
3  A  B
4  A  B

Si desea seleccionar MultiIndex columnuso tuple:

print (df1[('Col 1', 'A')])
0    A
1    A
2    A
3    A
4    A
Name: (Col 1, A), dtype: object

Si lo desea, seleccione por índice y por columna use loc:

print (df1.loc[4, ('Col 1', 'A')])
A
jezrael
fuente
2
Eso lo hizo! ¡Muchas gracias!
Melendowski