Utilisez des données dans des blocs de données pandas pour faire correspondre les colonnes ensemble

18

J'ai deux pandastrames de données aet 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

et

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

Les deux trames de données contiennent exactement les mêmes données, mais dans un ordre différent et avec des noms de colonne différents. Sur la base des nombres dans les deux trames de données, je voudrais pouvoir faire correspondre chaque nom de colonne dedans aà chaque nom de colonne dedans b.

Ce n'est pas aussi simple que de comparer simplement la première ligne de aavec la première ligne bcar il y a des valeurs dupliquées, par exemple les deux a4et a7ont la valeur 5il n'est donc pas possible de les faire correspondre immédiatement à l'un b2ou à l'autre b4.

Quelle est la meilleure façon de procéder?

OD1995
la source

Réponses:

16

Voici un moyen d'utiliser 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
la source
Merci de partager la belle commande Anky, pourriez-vous s'il vous plaît expliquer plus sur la [*df1.index]partie s'il vous plaît? Je vous en serai reconnaissant.
RavinderSingh13
1
@ RavinderSingh13 Bien sûr, sort_values(by=..)prend une liste en tant que paramètre, donc je décompresse l'index dans une liste ici, vous pouvez également le faire à la list(df1.index)place de [*df1.index]:)
anky
16

Voici une façon d'exploiter 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'}

Une autre approche similaire (par @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
la source
1
J'ai collé mon nez dans ton post. Espérons que cela ne vous dérange pas. Veuillez le modifier à votre guise.
piRSquared
Ah au contraire :) approche Nice, et le contrôle sur les grands dataframes il améliore légèrement les performances @piRSquared
YATU
12

Une façon 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
la source
J'ai pensé ajouter une autre solution intelligente pour voir que c'était la même que la vôtre (-: whoops.
piRSquared
8

compréhension de dictionnaire

Utilisez l'une tupledes valeurs de colonne comme clé de hachage dans un dictionnaire

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'}

Juste au cas où nous n'aurions pas une représentation parfaite, je n'ai produit le dictionnaire que pour les colonnes où il y a une correspondance.

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

Cela frise l'absurde ... Ne faites pas ça.

{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
la source
1
Comment se fait-il que je puisse comprendre chaque expression dans ces déclarations, sans voir pleinement dans ma tête ce qui se passe vraiment ici? Un peu comme les échecs, je sais comment déplacer toutes les pièces du plateau, mais je ne peux pas voir plus de 2 avancer.
Scott Boston
D'accord ... J'ai digéré cela maintenant et c'est encore tout simplement génial. +1
Scott Boston