Usar datos en marcos de datos de pandas para unir columnas

18

Tengo dos pandasmarcos de datos ay b:

a1   a2   a3   a4   a5   a6   a7
1    3    4    5    3    4    5
0    2    0    3    0    2    1
2    5    6    5    2    1    2

y

b1   b2   b3   b4   b5   b6   b7
3    5    4    5    1    4    3
0    1    2    3    0    0    2
2    2    1    5    2    6    5

Los dos marcos de datos contienen exactamente los mismos datos, pero en un orden diferente y con diferentes nombres de columna. Según los números en los dos marcos de datos, me gustaría poder hacer coincidir el nombre ade cada columna con el nombre de cada columna b.

No es tan fácil como simplemente comparando la primera fila de ala primera fila de bcomo valores duplicados, por ejemplo tanto a4y a7tiene el valor 5por lo que no es posible hacer coincidir inmediatamente a cualquiera b2o b4.

¿Cuál es la mejor manera de hacer esto?

OD1995
fuente

Respuestas:

16

Aquí hay una manera de usar sort_values:

m=df1.T.sort_values(by=[*df1.index]).index
n=df2.T.sort_values(by=[*df2.index]).index
d=dict(zip(m,n))
print(d)

{'a1': 'b5', 'a5': 'b1', 'a2': 'b7', 'a3': 'b6', 'a6': 'b3', 'a7': 'b2', 'a4': 'b4'}
anky
fuente
Gracias por compartir el agradable comando Anky, ¿podrías explicar más en [*df1.index]parte, por favor? Te lo agradeceré, saludos.
RavinderSingh13
1
@ RavinderSingh13 Claro, sort_values(by=..)toma una lista como parámetro, así que estoy desempacando el índice a una lista aquí, también puedes hacerlo en list(df1.index)lugar de [*df1.index]:)
anky
16

Aquí hay una manera de aprovechar numpy broadcasting:

b_cols = b.columns[(a.values == b.T.values[...,None]).all(1).argmax(1)]
dict(zip(a, b_cols))

{'a1': 'b5',
 'a2': 'b7',
 'a3': 'b6',
 'a4': 'b4',
 'a5': 'b1',
 'a6': 'b3',
 'a7': 'b2'}

Otro enfoque similar (por @piR):

a_ = a.to_numpy()
b_ = b.to_numpy()
i, j = np.where((a_[:, None, :] == b_[:, :, None]).all(axis=0))
dict(zip(a.columns[j], b.columns[i]))

{'a1': 'b5',
 'a2': 'b7',
 'a3': 'b6',
 'a4': 'b4',
 'a5': 'b1',
 'a6': 'b3',
 'a7': 'b2'}
yatu
fuente
1
Metí la nariz en tu publicación. Con suerte, no te importa. Por favor cámbialo a tu gusto.
piRSquared
Ah, por el contrario :) enfoque de Niza, y la comprobación de grandes tramas de datos que mejora el rendimiento ligeramente @piRSquared
Yatu
12

Una forma de merge

s=df1.T.reset_index().merge(df2.T.assign(match=lambda x : x.index))
dict(zip(s['index'],s['match']))
{'a1': 'b5', 'a2': 'b7', 'a3': 'b6', 'a4': 'b4', 'a5': 'b1', 'a6': 'b3', 'a7': 'b2'}
YOBEN_S
fuente
Pensé que agregaría otra solución inteligente solo para ver que era la misma que la tuya (-: whoops.
piRSquared
8

comprensión del diccionario

Utilice uno tuplede los valores de columna como la clave hashable en un diccionario

d = {(*t,): c for c, t in df2.items()}
{c: d[(*t,)] for c, t in df1.items()}

{'a1': 'b5',
 'a2': 'b7',
 'a3': 'b6',
 'a4': 'b4',
 'a5': 'b1',
 'a6': 'b3',
 'a7': 'b2'}

En caso de que no tengamos una representación perfecta, solo he producido el diccionario para las columnas donde hay una coincidencia.

d2 = {(*t,): c for c, t in df2.items()}
d1 = {(*t,): c for c, t in df1.items()}

{d1[c]: d2[c] for c in {*d1} & {*d2}}

{'a5': 'b1',
 'a2': 'b7',
 'a7': 'b2',
 'a6': 'b3',
 'a3': 'b6',
 'a1': 'b5',
 'a4': 'b4'}

idxmax

Esto limita con lo absurdo ... En realidad, no hagas esto.

{c: df2.T.eq(df1[c]).sum(1).idxmax() for c in df1}

{'a1': 'b5',
 'a2': 'b7',
 'a3': 'b6',
 'a4': 'b4',
 'a5': 'b1',
 'a6': 'b3',
 'a7': 'b2'}
piRSquared
fuente
1
¿Cómo es que puedo entender cada expresión en estas declaraciones, pero no puedo ver completamente en mi cabeza lo que realmente está pasando aquí? Algo así como el ajedrez, sé cómo mover toda la pieza en el tablero, pero no puedo ver más de 2 movimientos adelante.
Scott Boston
Bien ... He digerido esto ahora y es absolutamente simple todavía, brillante. +1
Scott Boston