Baza wiedzy / Python
pandas.merge: klucze, typ połączenia i kontrola wyniku
Łączę małe dane syntetyczne i wyjaśniam każdy wiersz wyniku. Poprawiam przykład klucza złożonego oraz pokazuję, kiedy sufiksy rzeczywiście zmieniają nazwy kolumn.

Przed połączeniem tabel ja zadaję sobie pytanie, co dokładnie ma oznaczać jeden wiersz wyniku. Jeśli do zamówienia dopisuję segment klienta, chcę zachować zamówienie i znaleźć właściwego klienta. Sam brak błędu przy wykonaniu operacji nie potwierdza, że tak się stało. Źle wybrany klucz może usunąć część danych albo zwielokrotnić obserwacje, które później zsumuję w raporcie.
pandas.merge służy do łączenia obiektów po kolumnach lub indeksach. Tutaj skupiam się na dokładnym dopasowaniu kolumn w pandas 3.x. Przykłady są syntetyczne, a ich wyniki wyprowadzam z opisanych reguł, bez uruchamiania kodu. Pełną sygnaturę zawiera dokumentacja merge. Poszczególne fragmenty poniżej korzystają z bazowych tabel, chyba że wyraźnie definiuję nowy zestaw.
Dane i jawny wybór klucza
Mamy trzy zamówienia oraz słownik segmentów klientów:
import pandas as pd
orders = pd.DataFrame({
'order_id': [101, 102, 103],
'customer_id': [1, 2, 3],
'units': [10, 20, 30],
})
customers = pd.DataFrame({
'customer_id': [1, 2, 4],
'segment': ['A', 'B', 'C'],
})
Klucz customer_id występuje w obu tabelach. Klient trzy nie ma segmentu w słowniku, a klient cztery nie ma zamówienia. Te dwa przypadki celowo zostawiam w danych. Dzięki nim można ocenić, co wybrany typ połączenia robi z niedopasowaniem. Na idealnie pasujących tabelach łatwo przeoczyć różnicę między zachowaniem left a inner.
Podaję on jawnie. Bez niego, jeśli nie wybierasz łączenia po indeksach, pandas może użyć wspólnych nazw kolumn. Dodanie kolejnej jednakowo nazwanej kolumny zmieniłoby wtedy warunek. Własność „ten kod nadal się wykonuje” nie wystarcza do utrzymania sensu raportu. Wolę zapisać, które pola naprawdę stanowią klucz.
Left merge z kontrolą pochodzenia wierszy
result = pd.merge(
orders,
customers,
on='customer_id',
how='left',
validate='many_to_one',
indicator=True,
)
| order_id | customer_id | units | segment | _merge |
|---|---|---|---|---|
| 101 | 1 | 10 | A | both |
| 102 | 2 | 20 | B | both |
| 103 | 3 | 30 | brak | left_only |
Pierwszy wiersz dopasowuje klienta jeden do segmentu A, drugi klienta dwa do B. Trzeci pozostaje, ponieważ wybraliśmy left, ale nie dostaje segmentu. W tabeli słowo „brak” oznacza brakującą wartość, bez uzależniania opisu od jej wydruku. Klient cztery nie pojawia się, bo bazą są zamówienia. To nie jest jeszcze lista wszystkich klientów.
Kolumna _merge pozwala odróżnić dopasowanie od wiersza pochodzącego tylko z lewej strony. Przy outer może pojawić się również right_only. Ja najpierw oglądam te grupy i ustalam, czy niedopasowania są oczekiwane. Dopiero później decyduję, czy brak segmentu ma specjalne oznaczenie w raporcie. Automatyczne wypełnienie go literą A zatarłoby problem danych.
Co zmieni inner, right i outer
Dla tych samych danych how='inner' zachowa zamówienia 101 i 102. how='right' zachowa klucze klientów jeden, dwa i cztery, więc dla cztery zabraknie danych zamówienia. how='outer' uwzględni również zamówienie klienta trzy. To porównanie zestawów dopasowań, a nie obietnica identycznego porządku wydruku dla wszystkich wariantów.
Nie wybieram inner tylko dlatego, że daje tabelę bez braków. W tym przykładzie brak znika razem z zamówieniem 103. Suma jednostek wyglądałaby wtedy inaczej, bo analizowałbym inny zbiór. Najpierw określam populację raportu, potem wybieram typ połączenia. Jeśli zadanie ma wykazać brakujące dopasowania, ich usunięcie przed kontrolą utrudni odpowiedź na pytanie.
Duplikaty i validate
Przewodnik łączenia danych pandas opisuje zwielokrotnienie wynikające z powtarzających się kluczy. Dwa wiersze z tym samym kluczem po lewej i trzy po prawej tworzą sześć par. To może być poprawna relacja wiele do wielu, ale przy dopisywaniu pojedynczego segmentu klienta zwykle oczekuję innej reguły.
customers_bad = pd.DataFrame({
'customer_id': [1, 1],
'segment': ['A', 'B'],
})
pd.merge(
orders,
customers_bad,
on='customer_id',
how='left',
validate='many_to_one',
)
Ten przykład ma zakończyć się MergeError, ponieważ prawa strona zawiera powtórzony klucz. many_to_one dopuszcza powtórzenia po lewej, a wymaga unikalności po prawej. one_to_one wymaga unikalności po obu stronach, a one_to_many po lewej. many_to_many dopuszcza powtórzenia i nie stanowi kontroli unikalności.
Walidacja relacji nie sprawdza poprawności biznesowej klucza ani kompletności dopasowań. Słownik może mieć unikalne identyfikatory i przypisany niewłaściwy segment. Dlatego łączę kontrolę relacji z przeglądem _merge i znaczenia danych. Gdy walidacja zgłasza problem, wyjaśniam duplikat przed usuwaniem wierszy. Dwa segmenty mogą oznaczać dane z różnych okresów, które potrzebują dodatkowego warunku.

Klucz złożony z dwóch istniejących kolumn
W dawnym EN pojawiał się klucz ['ID', 'Name'], mimo że druga tabela nie miała Name. Nowy, osobny przykład zawiera oba pola po obu stronach:
stock = pd.DataFrame({
'shop': ['north', 'south'],
'product': ['pen', 'pen'],
'units': [2, 3],
})
prices = pd.DataFrame({
'shop': ['north', 'south'],
'product': ['pen', 'pen'],
'price': [4, 5],
})
matched = pd.merge(
stock, prices,
on=['shop', 'product'],
how='left',
validate='one_to_one',
)
Sklep north i produkt pen dostają cenę cztery, a south i pen cenę pięć. Sam produkt nie identyfikuje tu ceny w konkretnym sklepie. Obie kolumny tworzą warunek dopasowania, więc muszą istnieć w obu tabelach. Jeśli nazwy kluczy są różne, służą do tego left_on i right_on, a nie dopisywanie nieistniejącej nazwy do on.
Sufiksy dla rzeczywiście wspólnych nazw
W bazowych tabelach poza kluczem nie było wspólnej nazwy, więc same sufiksy niczego nie pokazywały. Dodaję kolumnę status o innym znaczeniu po każdej stronie:
orders_status = orders.assign(status='new')
customers_status = customers.assign(status='active')
with_status = pd.merge(
orders_status,
customers_status,
on='customer_id',
how='left',
suffixes=('_order', '_customer'),
validate='many_to_one',
)
Powstaną status_order i status_customer. Dla zamówienia 101 wartości wyniosą new oraz active. Dla zamówienia 103 status klienta pozostanie brakujący, bo samo ustawienie statusu po prawej nie tworzy dopasowania. Sufiksy rozróżniają nazwy, bez decydowania, którą informację uznać za ważniejszą.
Brakujący klucz i różnica względem SQL
Pandas dopasowuje brakujące wartości klucza po obu stronach. Ostrzeżenie znajduje się w referencji merge. Nie przenoszę tu automatycznie oczekiwania ze zwykłego porównania równości NULL w SQL. W raporcie dwa nieznane identyfikatory nie muszą oznaczać tego samego klienta, nawet jeśli biblioteka potrafi je połączyć.
Przed takim połączeniem ustalam, czy brakujące klucze odrzucić z dopasowania, wydzielić do kontroli czy obsłużyć inaczej. Nie zastępuję ich wspólnym fikcyjnym identyfikatorem bez decyzji o znaczeniu. Sprawdzam także typ i zapis kluczy, bo liczba oraz tekst ją przedstawiający wymagają świadomego przygotowania danych. Operacja łączenia nie naprawia ich tożsamości za mnie.
Ćwiczenie przed kolejnym raportem
W bazowym przykładzie dopisz drugie zamówienie klienta jeden i przewidź liczbę wierszy przy poprawnym słowniku. Potem rozważ dodanie drugiego segmentu temu klientowi. Która walidacja zatrzyma pracę i dlaczego? Odpowiedź wynika z liczby dopasowań, bez potrzeby dużych danych. Następnie opisz, jak pokażesz odbiorcy zamówienie bez segmentu.
Jeśli klucz jest już indeksem prawej tabeli, przejdź do DataFrame.join. Gdy dopisujesz kolejne fragmenty danych zamiast wyszukiwać dopasowania po kluczu, sprawdź pandas.concat. Najpierw wybierz sens operacji, potem zapis w bibliotece.
Najważniejsze w pigułce
Dlaczego merge zwiększył liczbę wierszy?
Powtarzające się klucze mogą tworzyć wiele par dopasowania. Dwa wiersze z danym kluczem po lewej i trzy po prawej dają sześć par. Sprawdź oczekiwaną relację i użyj validate.
Czy brakujące klucze łączą się jak NULL w SQL?
Nie zakładaj tego. Pandas dopasowuje brakujące klucze po obu stronach, inaczej niż zwykłe porównanie równości NULL w SQL. Ustal zasady obsługi takich wierszy przed połączeniem.
Kiedy używać klucza złożonego?
Gdy dopiero kilka pól razem identyfikuje właściwe dopasowanie. Wszystkie pola wskazane w on muszą istnieć po obu stronach; przy różnych nazwach użyj left_on i right_on.
Przy pracy nad artykułem użyto w zautomatyzowany sposób sztucznej inteligencji. Jeśli też chcesz tak umieć, sprawdź ten projekt.
