concat, join по индексу и сравнение с SQL
Кроме merge есть concat для простой склейки таблиц и join для соединения по индексу — у каждого своя ниша.
concat складывает таблицы стопкой (по строкам) или бок о бок (по столбцам). join соединяет по индексу, как merge по ключу.
concat: склейка по строкам
Когда у вас несколько таблиц одинаковой структуры (выгрузки за разные месяцы, части одного датасета), их нужно просто поставить друг под друга. Это concat с axis=0 (по умолчанию):
import pandas as pd
all_data = pd.concat([jan, feb, mar]) # стопкой, по строкам
all_data = pd.concat([jan, feb, mar], ignore_index=True) # перенумеровать индекс
all_data = pd.concat({"янв": jan, "фев": feb}, names=["месяц"]) # пометить источник
ignore_index=True важен: без него склеенная таблица сохранит исходные индексы, и они начнут повторяться (три раза по 0, 1, 2…). keys= (или словарь) добавляет верхний уровень индекса, помечающий, из какой таблицы пришла строка.
Идея склейки по строкам элементарна — это конкатенация списков записей:
jan = [{"товар": "мышь", "шт": 3}, {"товар": "кабель", "шт": 10}]
feb = [{"товар": "монитор", "шт": 1}]
all_rows = jan + feb # concat по строкам = склейка
for i, r in enumerate(all_rows): # ignore_index: новая нумерация 0..n
print(i, r["товар"], r["шт"])
Вывод:
0 мышь 3 1 кабель 10 2 монитор 1
В pandas pd.concat([jan, feb], ignore_index=True) делает ровно это: ставит строки одну под другую и присваивает сквозной индекс 0..n.
concat по столбцам
С axis=1 таблицы ставятся бок о бок, выравниваясь по индексу строк (вспомните главную идею pandas). Это удобно, когда у вас два набора столбцов с общим индексом:
pd.concat([prices, stock], axis=1) # столбцы рядом, выравнивание по индексу
Если индексы не совпадают, в местах рассогласования появятся NaN — это не баг, а выравнивание. По смыслу concat(axis=1) близок к outer join по индексу.
Когда брать concat(axis=1), а когда merge? concat по столбцам уместен, когда таблицы уже выровнены по индексу и вы просто хотите поставить их рядом — например, посчитали несколько Series по одному и тому же индексу и собираете их в один DataFrame. Если же соединять надо по значению столбца (id клиента), а индексы произвольные — это работа для merge, а не concat. Грубо: concat — «склей по позиции/метке индекса как есть», merge — «найди соответствие по ключу и сопоставь». Путать их — частая причина неожиданных NaN.
verify_integrity: защита от дублей при склейке
У concat есть полезный параметр verify_integrity=True: он проверяет, что в итоговом индексе нет повторов, и падает с ошибкой, если они появились. Это аналог validate у merge — дешёвая страховка, когда вы ожидаете уникальный индекс после склейки:
pd.concat([a, b], verify_integrity=True) # упадёт, если индексы пересеклись
join: соединение по индексу
join — это merge, но соединяющий по индексу, а не по столбцу-ключу (по умолчанию). Удобен, когда естественный ключ уже стоит индексом обеих таблиц:
clients = clients.set_index("client_id")
orders.join(clients, on="client_id", how="left") # left.column ↔ right.index
По сути orders.join(clients) ≈ orders.merge(clients, left_on=..., right_index=True). Если у обеих таблиц ключ в индексе — join короче и читаемее.
Когда что использовать
| Задача | Инструмент |
| Поставить таблицы одной структуры стопкой | concat(axis=0) |
| Поставить столбцы рядом по общему индексу | concat(axis=1) |
| Соединить по столбцу-ключу | merge |
| Соединить по индексу | join |
Прямое соответствие SQL
Операции pandas — это реляционная алгебра с другим синтаксисом. Сравним на живой SQLite-песочнице: тот же left join.
CREATE TABLE orders (id INTEGER, client_id INTEGER);
INSERT INTO orders VALUES (1, 10), (2, 20), (3, 99);
CREATE TABLE clients (client_id INTEGER, name TEXT);
INSERT INTO clients VALUES (10, 'Аня'), (20, 'Боря'), (30, 'Вера');
SELECT o.id, o.client_id, c.name
FROM orders o
LEFT JOIN clients c ON o.client_id = c.client_id
ORDER BY o.id;
Вывод:
1|10|Аня 2|20|Боря 3|99|
Это в точности результат orders.merge(clients, on="client_id", how="left") из прошлого урока: заказ 3 остался, но имя пустое (в SQL — NULL, в pandas — NaN). Таблица соответствий:
| SQL | pandas |
JOIN ... ON | merge(on=...) |
LEFT JOIN | merge(how="left") |
UNION ALL | concat(axis=0) |
GROUP BY | groupby |
WHERE | df[маска] / query |
Подводные камни
- Повторяющийся индекс после concat. Без
ignore_index=Trueметки начнут дублироваться, что ломает дальнейшийlocи слияния. - concat(axis=1) и рассогласованный индекс. Несовпадение меток строк даёт NaN; убедитесь, что индексы сопоставимы.
- concat не проверяет ключи. В отличие от merge с
validate, concat просто склеивает — следите за структурой сами. - Разные столбцы при concat по строкам. Если у таблиц разный набор столбцов, недостающие заполнятся NaN — иногда это сюрприз.
Лучшие практики
- Склеиваете однотипные таблицы —
concat(axis=0, ignore_index=True). - Соединяете по ключу-столбцу —
merge; по индексу —join. - Помечайте источник строк через
keys=, если важно знать, откуда они. - Думаете в терминах SQL — переносите интуицию JOIN/UNION напрямую на merge/concat.
Итог
concat(axis=0)— стопка таблиц (как UNION ALL); используйтеignore_index.concat(axis=1)— столбцы рядом с выравниванием по индексу.joinсоединяет по индексу,merge— по столбцу-ключу.- Операции pandas прямо соответствуют SQL JOIN/UNION/GROUP BY/WHERE.