pandas: группировки, соединения и упорядоченные показатели

merge и concat: соединение таблиц с контролем кардинальности

Содержание курса

В прошлом уроке transform помогал добавлять к каждой строке групповой показатель — сумму или среднее — не выходя за пределы одного DataFrame. Но если нужные данные хранятся в двух разных таблицах, transform не поможет: нужна операция явного соединения. Именно её разберём сейчас.

pd.merge: параметры on и how — механика четырёх типов соединения

pd.merge — основная функция pandas для соединения двух DataFrame по общему ключевому столбцу. Минимальный вызов выглядит так:

import pandas as pd

orders = pd.DataFrame({'order_id': [1, 2, 3], 'amount': [100, 200, 300]})
clients = pd.DataFrame({'order_id': [1, 2, 4], 'client': ['Аня', 'Боря', 'Вера']})

result = pd.merge(orders, clients, on='order_id', how='inner')
print(result)
#    order_id  amount client
# 0         1     100    Аня
# 1         2     200   Боря

Параметр on указывает имя столбца, по которому идёт сравнение. Он должен присутствовать в обоих DataFrame. Параметр how задаёт правило отбора строк в результат — и именно здесь поведение четырёх режимов принципиально расходится.

inner — строгое пересечение. В результат попадают только те строки, у которых значение ключа есть в обеих таблицах. В примере выше order_id=3 нет в clients, order_id=4 нет в orders — обе строки отброшены.

left — все строки левого DataFrame сохраняются. Если для какого-то order_id из orders нет совпадения в clients, строка всё равно попадает в результат.

left = pd.merge(orders, clients, on='order_id', how='left')
print(left)
#    order_id  amount client
# 0         1     100    Аня
# 1         2     200   Боря
# 2         3     300    NaN
print(left.shape)  # (3, 3)

order_id=3 присутствует, но client для него — NaN, потому что в правой таблице такой записи нет.

right — симметрично left: все строки правого DataFrame (clients) сохраняются, строки левого (orders) добавляются там, где есть совпадение.

right = pd.merge(orders, clients, on='order_id', how='right')
print(right)
#    order_id  amount client
# 0         1   100.0    Аня
# 1         2   200.0   Боря
# 2         4     NaN   Вера
print(right.shape)  # (3, 3)

order_id=4 есть только в clients, поэтому amount для него — NaN.

outer — объединение всех строк из обоих DataFrame. Строки без пары получают NaN в столбцах противоположной таблицы.

outer = pd.merge(orders, clients, on='order_id', how='outer')
print(outer)
#    order_id  amount client
# 0         1   100.0    Аня
# 1         2   200.0   Боря
# 2         3   300.0    NaN
# 3         4     NaN   Вера
print(outer.shape)  # (4, 3)

Четыре строки: две совпавших, одна только из orders, одна только из clients.

Параметр on принимает не только строку, но и список столбцов, если ключ составной: on=['user_id', 'date']. Главное требование — все перечисленные столбцы должны существовать в обоих DataFrame с одинаковыми именами.

Выбор how — это не технический, а смысловой вопрос. Он зависит от того, какие строки нельзя потерять: если приоритет у левой таблицы (основная сущность) — left; если нужно полное покрытие обеих — outer; если важны только подтверждённые совпадения — inner.