concat vs merge в Pandas — когда что использовать

Коротко

concat склеивает DataFrames друг под другом (или рядом). merge соединяет по общему ключу — аналог JOIN в SQL. Разница: concat не требует общего столбца, merge — требует. Правило: если данные одинаковой структуры и нужно объединить в один файл — concat. Если нужно добавить информацию из другой таблицы по ключу — merge. На собеседованиях аналитиков это одна из ключевых тем по pandas.

concat — склейка DataFrames

По вертикали (друг под другом)

import pandas as pd

df_jan = pd.DataFrame({
    'user_id': [1, 2, 3],
    'revenue': [5000, 3200, 8100]
})

df_feb = pd.DataFrame({
    'user_id': [2, 3, 4],
    'revenue': [4100, 7500, 2800]
})

df_all = pd.concat([df_jan, df_feb])
#    user_id  revenue
# 0        1     5000
# 1        2     3200
# 2        3     8100
# 0        2     4100
# 1        3     7500
# 2        4     2800

Индексы дублируются. Добавьте ignore_index=True для сброса:

df_all = pd.concat([df_jan, df_feb], ignore_index=True)
#    user_id  revenue
# 0        1     5000
# 1        2     3200
# 2        3     8100
# 3        2     4100
# 4        3     7500
# 5        4     2800

По горизонтали (рядом)

df_names = pd.DataFrame({
    'user_id': [1, 2, 3],
    'name': ['Иван', 'Анна', 'Пётр']
})

df_scores = pd.DataFrame({
    'score': [85, 92, 78]
})

df = pd.concat([df_names, df_scores], axis=1)
#    user_id   name  score
# 0        1   Иван     85
# 1        2   Анна     92
# 2        3   Пётр     78

axis=0 (по умолчанию) — вертикально. axis=1 — горизонтально. При горизонтальной склейке строки сопоставляются по индексу.

С разными столбцами

df1 = pd.DataFrame({'user_id': [1], 'revenue': [5000]})
df2 = pd.DataFrame({'user_id': [2], 'revenue': [3200], 'city': ['Москва']})

pd.concat([df1, df2], ignore_index=True)
#    user_id  revenue    city
# 0        1     5000     NaN
# 1        2     3200  Москва

Столбцы, отсутствующие в одном из DataFrame, заполняются NaN. Это join='outer' (по умолчанию). Для пересечения столбцов: join='inner'.

merge — соединение по ключу

Базовый merge

orders = pd.DataFrame({
    'order_id': [1, 2, 3],
    'user_id': [101, 102, 101],
    'amount': [5000, 3200, 8100]
})

users = pd.DataFrame({
    'user_id': [101, 102, 103],
    'name': ['Иван', 'Анна', 'Пётр']
})

result = orders.merge(users, on='user_id')
#    order_id  user_id  amount  name
# 0         1      101    5000  Иван
# 1         2      102    3200  Анна
# 2         3      101    8100  Иван

По умолчанию — inner merge: только строки с совпадающим ключом. Пётр (user_id=103) не попал — у него нет заказов.

Типы merge (аналоги SQL JOIN)

# Inner — только совпадения
orders.merge(users, on='user_id', how='inner')

# Left — все из левого + совпадения из правого
orders.merge(users, on='user_id', how='left')

# Right — все из правого + совпадения из левого
orders.merge(users, on='user_id', how='right')

# Outer — все из обоих
orders.merge(users, on='user_id', how='outer')
Pandas merge SQL JOIN Результат
how='inner' INNER JOIN Только совпадения
how='left' LEFT JOIN Все из левого
how='right' RIGHT JOIN Все из правого
how='outer' FULL OUTER JOIN Все из обоих

Подробнее — в гайде по merge.

Разные названия ключей

orders = pd.DataFrame({'order_id': [1], 'customer_id': [101], 'amount': [5000]})
users = pd.DataFrame({'user_id': [101], 'name': ['Иван']})

orders.merge(users, left_on='customer_id', right_on='user_id')

left_on и right_on — если столбцы-ключи называются по-разному.

Суффиксы при совпадении имён

df1 = pd.DataFrame({'id': [1], 'value': [100]})
df2 = pd.DataFrame({'id': [1], 'value': [200]})

df1.merge(df2, on='id', suffixes=('_left', '_right'))
#    id  value_left  value_right
# 0   1         100          200

Сравнение: concat vs merge

Критерий concat merge
Что делает Склеивает DataFrames Соединяет по ключу
Нужен общий ключ Нет Да
Аналог в SQL UNION ALL JOIN
Направление Вертикально или горизонтально Горизонтально
Дубликаты Не убирает Может размножить строки
Типичный кейс Объединить файлы за разные месяцы Добавить данные из справочника

Когда concat

  • Одинаковая структура, разные данные: январь + февраль + март
  • Объединить результаты нескольких запросов
  • Добавить вычисленные столбцы рядом (axis=1)

Когда merge

  • Добавить имена пользователей к заказам
  • Присоединить справочник (города, категории)
  • Любая задача, где в SQL написали бы JOIN

Практические примеры

Объединить CSV за несколько месяцев

import glob

files = glob.glob('data/orders_*.csv')
dfs = [pd.read_csv(f) for f in files]
df = pd.concat(dfs, ignore_index=True)

Обогатить заказы данными о пользователях

orders = pd.read_csv('orders.csv')
users = pd.read_csv('users.csv')

enriched = orders.merge(users, on='user_id', how='left')

how='left' — все заказы сохраняются, даже если пользователь не найден (NaN в столбцах из users).

Найти пользователей без заказов

merged = users.merge(orders, on='user_id', how='left', indicator=True)
no_orders = merged[merged['_merge'] == 'left_only']

indicator=True добавляет столбец _merge со значениями: both, left_only, right_only. Аналог антиджойна в SQL.

Типичные ошибки

concat вместо merge. Склейка по вертикали не соединяет по ключу — просто кладёт строки друг под друга. Если нужен JOIN — используйте merge.

Дубликаты после merge. Если ключ не уникален в обоих DataFrame, merge создаст декартово произведение для совпадающих ключей. Проверяйте: df.duplicated(subset=['key']).sum().

Потеря строк при inner merge. По умолчанию merge делает inner join — строки без совпадения отбрасываются. Для сохранения всех строк из основного DataFrame — how='left'.

Горизонтальный concat без совпадающих индексов. pd.concat([df1, df2], axis=1) сопоставляет по индексу. Если индексы разные — получите NaN. Используйте merge вместо concat для соединения по ключу.

Вопросы с собеседований

-- Чем отличается concat от merge? -- concat склеивает DataFrames (вертикально или горизонтально) без логики соединения по ключу. merge соединяет по общему столбцу — аналог SQL JOIN. concat ≈ UNION, merge ≈ JOIN.

-- Как объединить два DataFrame по ключу? -- df1.merge(df2, on='key') для inner join. how='left' / 'right' / 'outer' для других типов. Если ключи называются по-разному: left_on и right_on.

-- Как найти строки, которые есть в одном DataFrame, но нет в другом? -- df1.merge(df2, on='key', how='left', indicator=True). Фильтрация по _merge == 'left_only'. Альтернатива: df1[~df1['key'].isin(df2['key'])].

-- Когда merge размножает строки? -- Когда ключ не уникален в обоих DataFrame. Например, у пользователя 3 заказа, а в справочнике 2 строки с его city — merge создаст 6 строк. Это аналог CROSS JOIN для совпадающих ключей.


Потренируйтесь решать задачи — откройте тренажёр с 1500+ вопросами для подготовки к собеседованиям аналитиков.

FAQ

concat vs append — в чём разница?

append удалён в pandas 2.0. Используйте pd.concat([df1, df2]) вместо df1.append(df2). Функционально — одно и то же.

merge vs join — в чём разница?

merge — функция/метод для соединения по столбцам. join — метод для соединения по индексу. На практике merge универсальнее и используется чаще.

Как ускорить merge на больших данных?

Убедитесь, что ключевой столбец имеет правильный тип (int, не object). Отсортируйте оба DataFrame по ключу перед merge. Для очень больших данных — рассмотрите SQL или Dask.

Как тренироваться

concat и merge — must-have для аналитика pandas. Задачи на соединение данных — в тренажёре Карьерник. Больше вопросов — в разделе с примерами.