Merge в Pandas: шпаргалка для собеседования
Зачем аналитику уверенность в merge
Merge в pandas — прямой аналог JOIN в SQL, но с нюансами, которые ловят на собеседовании. Чаще всего — дублирование строк, неожиданный NaN и путаница между merge / join / concat.
Базовый синтаксис
import pandas as pd
result = df1.merge(df2, on='key', how='inner')on— общий столбец-ключ.how— тип объединения: 'inner' (по умолчанию), 'left', 'right', 'outer', 'cross'.
Типы how
inner (по умолчанию)
Только совпадения:
users = pd.DataFrame({'user_id': [1, 2, 3], 'name': ['a', 'b', 'c']})
orders = pd.DataFrame({'user_id': [1, 2, 2, 5], 'amount': [100, 200, 50, 300]})
users.merge(orders, on='user_id', how='inner')
# user 1: 1 row, user 2: 2 rows, user 3 и 5: дропнутыleft
Все строки левой + совпадения справа:
users.merge(orders, on='user_id', how='left')
# user 3 будет с amount=NaNouter
Все строки обеих + NaN где нет совпадений:
users.merge(orders, on='user_id', how='outer')
# user 3: amount=NaN, user 5: name=NaNcross
Декартово произведение (pandas 1.2+):
df1.merge(df2, how='cross')on vs left_on/right_on
# Если столбцы называются одинаково
df1.merge(df2, on='user_id')
# Если по-разному
df1.merge(df2, left_on='user_id', right_on='id')
# По нескольким ключам
df1.merge(df2, on=['user_id', 'date'])Тренироваться на таких вопросах можно в Telegram-боте Карьерник — там 1500+ задач с реальных собесов с разборами.
indicator=True
Добавляет колонку _merge со значениями left_only, right_only, both:
result = df1.merge(df2, on='key', how='outer', indicator=True)
# Посмотреть расхождения
print(result['_merge'].value_counts())Полезно для сверки данных между источниками — видим, что есть только в левой, только в правой, в обеих.
validate — защита от неожиданных дублей
df1.merge(df2, on='user_id', validate='one_to_one') # 1:1
df1.merge(df2, on='user_id', validate='one_to_many') # 1:n
df1.merge(df2, on='user_id', validate='many_to_one') # n:1
df1.merge(df2, on='user_id', validate='many_to_many') # n:mБросает ошибку, если данные не соответствуют ожиданию. Золотая защита от бага: если по бизнес-логике у user должен быть один заказ, но кто-то залил дубли — validate это поймает.
Суффиксы при одинаковых именах
df1.merge(df2, on='user_id', suffixes=('_left', '_right'))
# Если у обеих был столбец name — станут name_left и name_rightmerge vs join vs concat
| Функция | Для чего |
|---|---|
merge |
JOIN по ключу (SQL-way) |
join |
Как merge, но по индексу |
concat |
Стэкование таблиц (vertical/horizontal append) |
join vs merge
# merge — по столбцу
df1.merge(df2, on='user_id')
# join — по индексу
df1.set_index('user_id').join(df2.set_index('user_id'))concat — append / stack
# Склеить вертикально (append rows)
pd.concat([df1, df2], axis=0)
# Склеить горизонтально (side-by-side)
pd.concat([df1, df2], axis=1)concat НЕ объединяет по ключу — просто стэкует. Если нужно JOIN по ключу — merge.
merge_asof — самая мощная функция
«Ближайший ключ» merge, идеален для time-series:
trades = pd.DataFrame({
'time': pd.to_datetime(['10:00:00', '10:00:05', '10:00:10']),
'price': [100, 101, 102]
})
quotes = pd.DataFrame({
'time': pd.to_datetime(['10:00:00', '10:00:03', '10:00:08']),
'bid': [99, 100, 101]
})
pd.merge_asof(trades, quotes, on='time', direction='backward')
# Для каждой строки trades найдёт ближайшую quote <= timeПрактика: сопоставить события с последней известной ценой/курсом.
К слову, набить руку на таких кейсах удобно через тренажёр в Telegram — разбирайте по 10 вопросов в день, через 2 недели тема становится рефлексом.
Частые ловушки
1. Дублирование строк
# Если в df2 несколько строк с одним user_id
# результат растянется
df1.merge(df2, on='user_id') # каждая строка df1 продублируетсяРешение — агрегировать df2 до merge или использовать validate='one_to_one'.
2. Неочевидный NaN при left join
users.merge(orders, on='user_id', how='left')
# Для пользователей без заказов — NaN в amount
# SUM проигнорирует их, AVG сдвинетсяРешение: fillna(0) где уместно.
3. Разные типы ключа
# user_id в одной таблице int, в другой str — merge не сматчит
df1['user_id'] = df1['user_id'].astype(int)
df2['user_id'] = df2['user_id'].astype(int)Самая тупая ошибка, которую все совершали. Проверяйте dtypes.
10 задач с собесов
1. Обогатить заказы именем пользователя
orders.merge(users[['user_id', 'name']], on='user_id', how='left')2. Найти пользователей без заказов
result = users.merge(orders, on='user_id', how='left', indicator=True)
result[result['_merge'] == 'left_only']3. Заказы с суммой из items
order_totals = order_items.groupby('order_id')['price'].sum().reset_index()
orders.merge(order_totals, on='order_id', how='left')4. Сверить две таблицы и найти расхождения
merged = source_a.merge(source_b, on='id', how='outer',
indicator=True, suffixes=('_a', '_b'))
diffs = merged[merged['_merge'] != 'both']5. JOIN по двум ключам
df1.merge(df2, on=['user_id', 'date'])6. SELF JOIN: найти пары из одной таблицы
df1 = df.copy()
df1['join_key'] = 1
df2 = df.copy()
df2['join_key'] = 1
pairs = df1.merge(df2, on='join_key').query('user_id_x != user_id_y')Или через merge(on=, how='cross') в pandas 1.2+.
7. Обогатить продажу курсом валюты за тот же день (merge_asof)
pd.merge_asof(sales.sort_values('date'),
rates.sort_values('date'),
on='date', direction='backward')8. Объединить несколько источников
from functools import reduce
dfs = [users, orders_agg, profile, activity]
result = reduce(lambda l, r: l.merge(r, on='user_id', how='left'), dfs)9. Антиjoin: клиенты без премиума
all_users.merge(premium_users, on='user_id', how='left', indicator=True)
.query('_merge == "left_only"')10. Проверить 1:1 связь
df1.merge(df2, on='id', validate='one_to_one')
# Бросит ошибку, если не 1:1Как тренироваться
Merge учится через реальные датасеты с тремя-четырьмя таблицами. Синтаксис простой, ловушки — нет. Нужно чувствовать, когда использовать left, когда inner, когда валидировать.
Тренажёр Карьерник содержит блок задач на merge: типы how, validate, merge_asof, антиjoin. С разборами всех ловушек.
Совет: всегда ставьте validate при merge критичных данных. Это автоматический assert, который ловит баги данных до того, как они доедут до дашборда.
Читайте также
FAQ
Merge или join в pandas?
merge — универсальнее, по любому столбцу. join — по индексу. На практике merge используется в 90% случаев. join имеет смысл, когда у вас настроены индексы и хочется короче.
Когда concat вместо merge?
Concat — когда нужно stack таблицы: объединить логи за разные дни, склеить train и test. Merge — когда обогащаете данные из одной таблицы другой.
Как убедиться, что merge не продублирует строки?
Используйте validate='one_to_one' или 'many_to_one'. Или до merge проверьте, что правая таблица уникальна: df2['key'].is_unique.
Что делать, если после merge появились NaN?
Это либо ожидаемо (left join без совпадения), либо баг. Проверьте _merge через indicator=True. Если NaN только в new columns — скорее всего left_only, что значит — в правой нет этих ключей.