Użyj danych w ramkach danych pand, aby dopasować kolumny razem


18

Mam dwie pandasramki danych ai 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

i

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

Dwie ramki danych zawierają dokładnie te same dane, ale w innej kolejności i przy różnych nazwach kolumn. Na podstawie liczb w dwóch ramkach danych chciałbym móc dopasować nazwę akażdej kolumny do nazwy każdej kolumny w b.

Nie jest to tak proste, jak proste porównanie pierwszego wiersza az pierwszym wierszem, bponieważ istnieją zduplikowane wartości, na przykład oba a4i a7mają tę wartość, 5więc nie jest możliwe natychmiastowe dopasowanie ich do jednego b2lub drugiego b4.

Jak najlepiej to zrobić?

Odpowiedzi:


16

Oto sposób użycia 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'}

Dziękujemy za udostępnienie miłego polecenia Anky, czy mógłbyś proszę wyjaśnić więcej po [*df1.index]części, proszę? Będę ci wdzięczny, na zdrowie.
RavinderSingh13

1
@ RavinderSingh13 Jasne, sort_values(by=..)bierze listę jako parametr, więc rozpakowuję tutaj indeks do listy, możesz też zrobić list(df1.index)zamiast [*df1.index]:)
anky

16

Oto jeden ze sposobów wykorzystania 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'}

Inne podobne podejście (autorstwa @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'}

1
Wsadziłem nos w twój post. Mam nadzieję, że nie masz nic przeciwko. Zmień to według własnych upodobań.
piRSquared

Ach wręcz przeciwnie :) Ładne podejście, a sprawdzanie dużych ramek danych nieznacznie poprawia wydajność @piRSquared
yatu

12

Jednym ze sposobów 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'}

Pomyślałem, że dodam inne sprytne rozwiązanie, aby zobaczyć, że jest takie samo jak twoje (-:
ups

8

słownictwo

Użyj a tuplez wartości kolumny jako klucza skrótu w słowniku

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

Na wypadek, gdybyśmy nie mieli idealnej reprezentacji, stworzyłem słownik tylko dla kolumn, w których istnieje dopasowanie.

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

To graniczy z absurdem ... Nie rób tego.

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

1
Jak to możliwe, że rozumiem każde wyrażenie w tych stwierdzeniach, ale nie do końca rozumiem, co się tutaj naprawdę dzieje? Trochę jak w szachy, wiem, jak przenieść cały pionek na planszy, ale nie widzę więcej niż 2 ruchy do przodu.
Scott Boston

Okej ... Przetrawiłem to teraz i jest to absolutnie po prostu genialne. +1
Scott Boston
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.