Pandas Scalanie 101

362
  • Jak wykonać połączenie ( LEFT| RIGHT| FULL) ( INNER| OUTER) z pandami?
  • Jak dodać NaN dla brakujących wierszy po scaleniu?
  • Jak pozbyć się NaN po połączeniu?
  • Czy mogę scalić indeks?
  • Połącz krzyżowo z pandami?
  • Jak scalić wiele ramek danych?
  • merge? join? concat? update? WHO? Co? Dlaczego?!

... i więcej. Widziałem te powtarzające się pytania dotyczące różnych aspektów funkcji scalania pand. Większość informacji dotyczących scalania i jego różnych przypadków użycia jest dziś podzielona na dziesiątki źle sformułowanych, niemożliwych do przeszukania postów. Celem jest tutaj zestawienie niektórych najważniejszych punktów dla potomności.

Ta QnA ma być kolejną odsłoną serii pomocnych przewodników użytkownika na temat popularnych idiomów pand (zobacz ten post na temat przestawiania i ten post na temat konkatenacji , o którym będę się później zajmować).

Pamiętaj, że ten post nie jest zamiennikiem dokumentacji , więc również go przeczytaj! Niektóre przykłady zostały zaczerpnięte z tego miejsca.

cs95
źródło

Odpowiedzi:

517

Ten post ma na celu dać czytelnikom podstawowe informacje na temat łączenia o smaku SQL z pandami, jak z niego korzystać, a kiedy nie.

W szczególności oto, przez co przejdzie ten post:

  • Podstawy - rodzaje złączeń (LEWY, PRAWY, ZEWNĘTRZNY, WEWNĘTRZNY)

    • łączenie z różnymi nazwami kolumn
    • unikanie zduplikowanej kolumny klucza scalania w danych wyjściowych
  • Scalanie ze wskaźnikiem w różnych warunkach
    • skutecznie używając twojego nazwanego indeksu
    • klucz scalania jako indeks jednego i kolumny innego
  • Multiway łączy się w kolumny i indeksy (unikalne i nieunikalne)
  • Znaczące alternatywy dla mergeijoin

Przez co ten post nie przejdzie:

  • Dyskusje i terminy związane z wynikami (na razie). Głównie godne uwagi wzmianki o lepszych alternatywach, tam gdzie to właściwe.
  • Obsługa sufiksów, usuwanie dodatkowych kolumn, zmiana nazw wyników i inne szczególne przypadki użycia. Są inne (czytaj: lepiej) posty, które sobie z tym radzą, więc wymyśl to!

Uwaga:
Większość przykładów domyślnie wykonuje operacje WEJŚCIA WEWNĘTRZNEGO podczas demonstrowania różnych funkcji, chyba że określono inaczej.

Ponadto wszystkie dane DataFrames tutaj można skopiować i powielić, aby można było z nimi grać. Zobacz także ten post na temat odczytywania ramek danych ze schowka.

Na koniec wszystkie wizualne przedstawienia operacji JOIN zostały ręcznie narysowane przy użyciu Rysunków Google. Inspiracja stąd .

Dość gadania, po prostu pokaż mi, jak używać merge!

Ustawiać

np.random.seed(0)
left = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': np.random.randn(4)})    
right = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'value': np.random.randn(4)})

left

  key     value
0   A  1.764052
1   B  0.400157
2   C  0.978738
3   D  2.240893

right

  key     value
0   B  1.867558
1   D -0.977278
2   E  0.950088
3   F -0.151357

Dla uproszczenia kolumna klucza ma tę samą nazwę (na razie).

INNER JOIN jest reprezentowany przez

Uwaga
To, wraz z nadchodzącymi liczbami, wszystkie są zgodne z tą konwencją:

  • niebieski oznacza wiersze obecne w wyniku scalania
  • czerwony oznacza wiersze wykluczone z wyniku (tj. usunięte)
  • zielony oznacza brakujące wartości, które w wyniku są zastępowane NaN

Aby wykonać mergeŁĄCZENIE WEWNĘTRZNE, wywołaj lewą ramkę DataFrame, określając prawą ramkę DataFrame i klucz łączenia (przynajmniej) jako argumenty.

left.merge(right, on='key')
# Or, if you want to be explicit
# left.merge(right, on='key', how='inner')

  key   value_x   value_y
0   B  0.400157  1.867558
1   D  2.240893 -0.977278

To zwraca tylko wiersze z lefti rightktóre mają wspólny klucz (w tym przykładzie „B” i „D).

LEWO ZEWNĘTRZNE Dołącz lub LEWO Dołącz jest reprezentowana przez

Można to zrobić, określając how='left'.

left.merge(right, on='key', how='left')

  key   value_x   value_y
0   A  1.764052       NaN
1   B  0.400157  1.867558
2   C  0.978738       NaN
3   D  2.240893 -0.977278

Ostrożnie zanotuj tutaj położenie NaN. Jeśli określisz how='left', leftużywane są tylko klucze od , a brakujące dane rightsą zastępowane przez NaN.

I podobnie, dla PRAWEGO DOŁĄCZENIA ZEWNĘTRZNEGO lub PRAWEGO DOŁĄCZENIA, które jest ...

... określ how='right':

left.merge(right, on='key', how='right')

  key   value_x   value_y
0   B  0.400157  1.867558
1   D  2.240893 -0.977278
2   E       NaN  0.950088
3   F       NaN -0.151357

Tutaj rightużywane są klucze z , a brakujące dane leftsą zastępowane przez NaN.

Wreszcie, w przypadku PEŁNEGO DOŁĄCZENIA ZEWNĘTRZNEGO , podanego przez

określić how='outer'.

left.merge(right, on='key', how='outer')

  key   value_x   value_y
0   A  1.764052       NaN
1   B  0.400157  1.867558
2   C  0.978738       NaN
3   D  2.240893 -0.977278
4   E       NaN  0.950088
5   F       NaN -0.151357

Wykorzystuje klucze z obu ramek, a NaN są wstawiane dla brakujących wierszy w obu.

Dokumentacja ładnie podsumowuje te różne połączenia:

wprowadź opis zdjęcia tutaj

Inne DOŁĄCZENIA - WYŁĄCZENIE W LEWO, WYŁĄCZENIE W PRAWO, PEŁNE WYŁĄCZENIE / ANTY ŁĄCZENIE

Jeśli potrzebujesz POŁĄCZEŃ W LEWO, z wyłączeniem POŁĄCZEŃ i POŁĄCZEŃ W PRAWO, w dwóch krokach.

Dla LEWEGO-Z WYŁĄCZENIEM DOŁĄCZENIA, reprezentowane jako

Zacznij od wykonania LEWEGO POŁĄCZENIA ZEWNĘTRZNEGO, a następnie filtrowania (wyłączając!) Wierszy pochodzących lefttylko z ,

(left.merge(right, on='key', how='left', indicator=True)
     .query('_merge == "left_only"')
     .drop('_merge', 1))

  key   value_x  value_y
0   A  1.764052      NaN
2   C  0.978738      NaN

Gdzie,

left.merge(right, on='key', how='left', indicator=True)

  key   value_x   value_y     _merge
0   A  1.764052       NaN  left_only
1   B  0.400157  1.867558       both
2   C  0.978738       NaN  left_only
3   D  2.240893 -0.977278       both

I podobnie w przypadku DOŁĄCZENIA DO PRAWA,

(left.merge(right, on='key', how='right', indicator=True)
     .query('_merge == "right_only"')
     .drop('_merge', 1))

  key  value_x   value_y
2   E      NaN  0.950088
3   F      NaN -0.151357

Na koniec, jeśli musisz wykonać scalenie, które zachowuje tylko klucze z lewej lub prawej strony, ale nie oba (IOW, wykonanie ANTI-JOIN ),

Możesz to zrobić w podobny sposób—

(left.merge(right, on='key', how='outer', indicator=True)
     .query('_merge != "both"')
     .drop('_merge', 1))

  key   value_x   value_y
0   A  1.764052       NaN
2   C  0.978738       NaN
4   E       NaN  0.950088
5   F       NaN -0.151357

Różne nazwy dla kluczowych kolumn

Jeśli klucz kolumny są nazywane różnie, na przykład, leftma keyLeft, i rightma keyRightzamiast key-Więc trzeba będzie określić left_oni right_onjako argumenty zamiast on:

left2 = left.rename({'key':'keyLeft'}, axis=1)
right2 = right.rename({'key':'keyRight'}, axis=1)

left2

  keyLeft     value
0       A  1.764052
1       B  0.400157
2       C  0.978738
3       D  2.240893

right2

  keyRight     value
0        B  1.867558
1        D -0.977278
2        E  0.950088
3        F -0.151357

left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')

  keyLeft   value_x keyRight   value_y
0       B  0.400157        B  1.867558
1       D  2.240893        D -0.977278

Unikanie zduplikowanej kolumny klucza w danych wyjściowych

Podczas łączenia na keyLeftod lefti keyRightod right, jeśli tylko chcą albo z keyLeftalbo keyRight(ale nie oba) na wyjściu, można uruchomić poprzez ustawienie wskaźnika jako wstępny krok.

left3 = left2.set_index('keyLeft')
left3.merge(right2, left_index=True, right_on='keyRight')

    value_x keyRight   value_y
0  0.400157        B  1.867558
1  2.240893        D -0.977278

Porównaj to z wyjściem polecenia tuż przed (to jest wyjście z left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')), zauważysz, że keyLeftbrakuje. Możesz dowiedzieć się, którą kolumnę zachować, na podstawie tego, który indeks ramki jest ustawiony jako klucz. Może to mieć znaczenie, gdy powiedzmy, wykonując operację JOIN ZEWNĘTRZNY.

Scalanie tylko jednej kolumny z jednej z DataFrames

Rozważmy na przykład

right3 = right.assign(newcol=np.arange(len(right)))
right3
  key     value  newcol
0   B  1.867558       0
1   D -0.977278       1
2   E  0.950088       2
3   F -0.151357       3

Jeśli musisz scalić tylko „new_val” (bez żadnej innej kolumny), zwykle możesz po prostu podgrupować kolumny przed scaleniem:

left.merge(right3[['key', 'newcol']], on='key')

  key     value  newcol
0   B  0.400157       0
1   D  2.240893       1

Jeśli wykonujesz lewe połączenie zewnętrzne, bardziej wydajne rozwiązanie wymagałoby map:

# left['newcol'] = left['key'].map(right3.set_index('key')['newcol']))
left.assign(newcol=left['key'].map(right3.set_index('key')['newcol']))

  key     value  newcol
0   A  1.764052     NaN
1   B  0.400157     0.0
2   C  0.978738     NaN
3   D  2.240893     1.0

Jak wspomniano, jest to podobne, ale szybsze niż

left.merge(right3[['key', 'newcol']], on='key', how='left')

  key     value  newcol
0   A  1.764052     NaN
1   B  0.400157     0.0
2   C  0.978738     NaN
3   D  2.240893     1.0

Scalanie w wielu kolumnach

Aby przyłączyć się do więcej niż jednej kolumnie, należy określić listę dla on(lub left_oni right_on, w razie potrzeby).

left.merge(right, on=['key1', 'key2'] ...)

Lub, jeśli nazwy są różne,

left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2'])

Inne przydatne merge*operacje i funkcje

Ta sekcja obejmuje tylko podstawowe informacje i ma na celu tylko zaostrzenie apetytu. Więcej przykładów i przypadków, zobacz dokumentację merge, joiniconcat jak również linki do specyfikacji funkcyjnych.


Na podstawie indeksu * -JOIN (+ kolumna indeksu merge)

Ustawiać

np.random.seed([3, 14])
left = pd.DataFrame({'value': np.random.randn(4)}, index=['A', 'B', 'C', 'D'])    
right = pd.DataFrame({'value': np.random.randn(4)}, index=['B', 'D', 'E', 'F'])
left.index.name = right.index.name = 'idxkey'

left
           value
idxkey          
A      -0.602923
B      -0.402655
C       0.302329
D      -0.524349

right

           value
idxkey          
B       0.543843
D       0.013135
E      -0.326498
F       1.385076

Zazwyczaj scalanie indeksu wyglądałoby następująco:

left.merge(right, left_index=True, right_index=True)


         value_x   value_y
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Obsługa nazw indeksów

Jeśli Twój indeks ma nazwę, użytkownicy wersji v0.23 mogą również określić nazwę poziomu na on(lub left_oni right_onw razie potrzeby).

left.merge(right, on='idxkey')

         value_x   value_y
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Scalanie na indeksie jednej kolumny (kolumn) innej

Możliwe jest (i dość proste) użycie indeksu jednego i kolumny drugiego, aby wykonać scalenie. Na przykład,

left.merge(right, left_on='key1', right_index=True)

Lub odwrotnie ( right_on=...i left_index=True).

right2 = right.reset_index().rename({'idxkey' : 'colkey'}, axis=1)
right2

  colkey     value
0      B  0.543843
1      D  0.013135
2      E -0.326498
3      F  1.385076

left.merge(right2, left_index=True, right_on='colkey')

    value_x colkey   value_y
0 -0.402655      B  0.543843
1 -0.524349      D  0.013135

W tym szczególnym przypadku leftnazwa indeksu jest nazywana, więc możesz także użyć nazwy indeksu z left_on:

left.merge(right2, left_on='idxkey', right_on='colkey')

    value_x colkey   value_y
0 -0.402655      B  0.543843
1 -0.524349      D  0.013135

DataFrame.join
Oprócz nich istnieje inna zwięzła opcja. Możesz użyć DataFrame.joinwartości domyślnych, które są dołączane do indeksu. DataFrame.joindomyślnie wykonuje POŁĄCZENIE ZEWNĘTRZNE, więc how='inner'jest to konieczne tutaj.

left.join(right, how='inner', lsuffix='_x', rsuffix='_y')

         value_x   value_y
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Zauważ, że musiałem podać argumenty lsuffixi, rsuffixponieważ w joinprzeciwnym razie wystąpiłby błąd:

left.join(right)
ValueError: columns overlap but no suffix specified: Index(['value'], dtype='object')

Ponieważ nazwy kolumn są takie same. Nie byłoby problemu, gdyby były inaczej nazwane.

left.rename(columns={'value':'leftvalue'}).join(right, how='inner')

        leftvalue     value
idxkey                     
B       -0.402655  0.543843
D       -0.524349  0.013135

pd.concat
Wreszcie, jako alternatywa dla sprzężeń opartych na indeksie, możesz użyć pd.concat:

pd.concat([left, right], axis=1, sort=False, join='inner')

           value     value
idxkey                    
B      -0.402655  0.543843
D      -0.524349  0.013135

Pomiń, join='inner'jeśli potrzebujesz PEŁNEGO DOŁĄCZENIA ZEWNĘTRZNEGO (ustawienie domyślne):

pd.concat([left, right], axis=1, sort=False)

      value     value
A -0.602923       NaN
B -0.402655  0.543843
C  0.302329       NaN
D -0.524349  0.013135
E       NaN -0.326498
F       NaN  1.385076

Aby uzyskać więcej informacji, zobacz ten kanoniczny post na pd.concat@piRSquared .


Uogólnianie: mergeing wielu DataFrames

Często zdarza się, że wiele DataFrames ma zostać połączonych ze sobą. Naiwnie można to zrobić, łącząc mergepołączenia:

df1.merge(df2, ...).merge(df3, ...)

Jednak to szybko wymyka się spod kontroli wielu DataFrames. Ponadto może być konieczne uogólnienie dla nieznanej liczby ramek danych.

W tym miejscu przedstawiam połączenia pd.concatwielodrożne na unikalnych kluczach oraz połączenia DataFrame.joinwielodrożne na nieunikalnych kluczach. Najpierw konfiguracja.

# Setup.
np.random.seed(0)
A = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'valueA': np.random.randn(4)})    
B = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'valueB': np.random.randn(4)})
C = pd.DataFrame({'key': ['D', 'E', 'J', 'C'], 'valueC': np.ones(4)})
dfs = [A, B, C] 

# Note, the "key" column values are unique, so the index is unique.
A2 = A.set_index('key')
B2 = B.set_index('key')
C2 = C.set_index('key')

dfs2 = [A2, B2, C2]

Scalanie wielostronne na unikatowych kluczach (lub indeksach)

Jeśli twoje klucze (tutaj klucz może być kolumną lub indeksem) są unikalne, możesz użyć pd.concat. Pamiętaj, że pd.concatdołącza DataFrames do indeksu .

# merge on `key` column, you'll need to set the index before concatenating
pd.concat([
    df.set_index('key') for df in dfs], axis=1, join='inner'
).reset_index()

  key    valueA    valueB  valueC
0   D  2.240893 -0.977278     1.0

# merge on `key` index
pd.concat(dfs2, axis=1, sort=False, join='inner')

       valueA    valueB  valueC
key                            
D    2.240893 -0.977278     1.0

Pomiń join='inner'PEŁNE ZŁĄCZE ZEWNĘTRZNE. Zauważ, że nie możesz określić LEWEGO ani PRAWEGO połączenia zewnętrznego (jeśli potrzebujesz, użyj joinopisanych poniżej).

Scalanie wielostronne na kluczach z duplikatami

concatjest szybki, ale ma swoje wady. Nie może obsługiwać duplikatów.

A3 = pd.DataFrame({'key': ['A', 'B', 'C', 'D', 'D'], 'valueA': np.random.randn(5)})

pd.concat([df.set_index('key') for df in [A3, B, C]], axis=1, join='inner')
ValueError: Shape of passed values is (3, 4), indices imply (3, 2)

W tej sytuacji możemy użyć, joinponieważ może obsłużyć nieunikalne klucze (pamiętaj, że joindołącza DataFrames do ich indeksu; wywołuje mergepod maską i wykonuje POŁĄCZENIE ZEWNĘTRZNE, chyba że określono inaczej).

# join on `key` column, set as the index first
# For inner join. For left join, omit the "how" argument.
A.set_index('key').join(
    [df.set_index('key') for df in (B, C)], how='inner').reset_index()

  key    valueA    valueB  valueC
0   D  2.240893 -0.977278     1.0

# join on `key` index
A3.set_index('key').join([B2, C2], how='inner')

       valueA    valueB  valueC
key                            
D    1.454274 -0.977278     1.0
D    0.761038 -0.977278     1.0
cs95
źródło
49

Dodatkowy widok wizualny pd.concat([df0, df1], kwargs). Zauważ, że znaczenie kwarg axis=0lub axis=1nie jest tak intuicyjne jak df.mean()lubdf.apply(func)


na pd.concat ([df0, df1])

eliu
źródło
9
To fajny schemat. Czy mogę zapytać, jak to wyprodukowałeś?
cs95,
6
wbudowany w google doc „insert ==> rysunek ... ==> nowy” (od 2019 do maja). Ale, dla jasności: jedynym powodem, dla którego użyłem google doc do tego zdjęcia, jest to, że moje notatki są przechowywane w google doc, i chciałbym, aby zdjęcie można było szybko zmodyfikować w samym google doc. Właściwie teraz o tym wspomniałeś, narzędzie do rysowania w google doc jest całkiem fajne.
eliu
Wow, to jest świetne. Pochodzące ze świata SQL połączenie pionowe nie jest złączeniem w mojej głowie, ponieważ struktura tabeli jest zawsze stała. Teraz nawet myśl, że pandy powinny się skonsolidować, concata mergeparametr kierunku to horizontallub vertical.
Ufos
2
@Ufos Czy to nie jest dokładnie to, co jest axis=1i axis=0jest?
cs95,
2
Tak, są tam teraz mergea concati oś i cokolwiek. Jednak, jak pokazuje @eliu, to wszystko to ta sama koncepcja łączenia z „lewym” i „prawym” oraz „poziomym” lub „pionowym”. Ja osobiście muszę zaglądać do dokumentacji za każdym razem, gdy muszę pamiętać, która „oś” jest, 0a która jest 1.
Ufos,