Урок курса
merge и concat: соединение таблиц с контролем кардинальности
NumPy и pandas: практический тренажёрВ прошлом уроке 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.
Происхождение NaN в результатах left, right и outer join
Когда merge не находит пару для строки, он не выбрасывает её и не ставит заглушку — он заполняет недостающие поля NaN. Это не баг и не случайность: это явный сигнал о том, что данных с другой стороны не было.
Разберём на тех же таблицах:
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': ['Аня', 'Боря', 'Вера']})
При left join левый DataFrame — orders — сохраняется полностью. order_id=3 есть в orders, но в clients такой записи нет. pandas добавляет строку с order_id=3 и amount=300, а в столбец client пишет NaN — потому что значение из правой таблицы не пришло:
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
NaN появляется именно в столбцах, пришедших из правого DataFrame (client). Столбцы левого (order_id, amount) остаются нетронутыми.
При right join логика зеркальная: все строки правого DataFrame (clients) сохраняются. order_id=4 есть в clients, но не в orders — значит, amount получит NaN:
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 Вера
Здесь стоит обратить внимание на dtype: amount был целочисленным, но после right join pandas переводит его в float64, чтобы вместить NaN — обычный int в pandas не умеет хранить пропуски.
При outer join строки без пары сохраняются с обеих сторон одновременно. order_id=3 даёт NaN в client, order_id=4 даёт NaN в amount:
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 Вера
Четыре строки: две совпавших без пропусков, одна с NaN в client, одна с NaN в amount.
inner join — единственный режим, который не добавляет NaN из-за несовпадения ключей: в результат попадают только строки, нашедшие пару в обоих DataFrame. Однако если в исходных данных уже были пропуски, inner join их сохранит:
orders_with_nan = pd.DataFrame({'order_id': [1, 2], 'amount': [100, None]})
inner = pd.merge(orders_with_nan, clients, on='order_id', how='inner')
print(inner)
# order_id amount client
# 0 1 100.0 Аня
# 1 2 NaN Боря
НaN в amount здесь пришёл из исходной таблицы — не от несовпадения ключей. Это важно понимать: inner join гарантирует только то, что все ключи совпали, но не то, что все поля заполнены.
Практическое правило: сразу после merge с how='left' или 'outer' стоит проверить, сколько NaN появилось в ключевых столбцах:
print(left['client'].isna().sum()) # 1 — один заказ без клиента
print(outer['amount'].isna().sum()) # 1 — один клиент без заказа
Эти числа говорят о несовпадениях в данных — и следующий шаг зависит от задачи: либо заполнить пропуски, либо разобраться, почему строки не нашли пару.
suffixes, indicator=True и validate: контроль качества соединения
После того как разобрались с тем, какие строки попадают в результат и откуда берутся NaN, следующий вопрос — как убедиться, что merge сделал именно то, что ожидалось. Для этого в pd.merge есть три параметра: suffixes, indicator и validate.
Конфликт имён столбцов и suffixes
Если оба DataFrame содержат столбец с одинаковым именем, и этот столбец не является ключом соединения, pandas не выбросит ошибку — он переименует оба, добавив суффиксы _x и _y:
import pandas as pd
orders = pd.DataFrame({
'order_id': [1, 2, 3],
'amount': [100, 200, 300],
'status': ['new', 'paid', 'new']
})
reviews = pd.DataFrame({
'order_id': [1, 2],
'status': ['ok', 'ok'],
'score': [4.5, 3.8]
})
result = pd.merge(orders, reviews, on='order_id', how='left')
print(result.columns.tolist())
# ['order_id', 'amount', 'status_x', 'status_y', 'score']
Столбец status оказался в обеих таблицах, и pandas создал status_x (из orders) и status_y (из reviews). Это работает, но читать такой код через неделю неудобно. Параметр suffixes позволяет задать понятные имена явно:
result = pd.merge(
orders, reviews,
on='order_id',
how='left',
suffixes=('_order', '_review')
)
print(result.columns.tolist())
# ['order_id', 'amount', 'status_order', 'status_review', 'score']
Теперь сразу понятно, откуда пришёл каждый status.
indicator=True: аудит происхождения строк
Параметр indicator=True добавляет в результат столбец _merge, который показывает, из какого источника пришла каждая строка. Возможные значения: 'both' — строка нашла пару в обоих DataFrame, 'left_only' — строка есть только в левом, 'right_only' — только в правом.
orders = pd.DataFrame({'order_id': [1, 2, 3], 'amount': [100, 200, 300]})
clients = pd.DataFrame({'order_id': [1, 2, 4], 'client': ['Аня', 'Боря', 'Вера']})
outer = pd.merge(orders, clients, on='order_id', how='outer', indicator=True)
print(outer)
# order_id amount client _merge
# 0 1 100.0 Аня both
# 1 2 200.0 Боря both
# 2 3 300.0 NaN left_only
# 3 4 NaN Вера right_only
Столбец _merge имеет категориальный dtype. Чаще всего его используют сразу после merge, чтобы посмотреть, сколько строк осталось без пары:
print(outer['_merge'].value_counts())
# both 2
# left_only 1
# right_only 1
Это удобнее, чем считать NaN вручную: indicator=True явно разделяет случай «строка без пары слева» и «строка без пары справа», а не просто фиксирует факт пропуска.
validate: проверка кардинальности до использования результата
Параметр validate позволяет задекларировать ожидаемую структуру соединения. Если реальные данные её нарушают, pandas выбрасывает MergeError — сразу, до того как результат будет где-то использован.
pandas принимает четыре значения: 'one_to_one', 'one_to_many', 'many_to_one' и 'many_to_many'. В этом уроке используем три проверяющих контракта, которые реально ограничивают уникальность ключей:
'one_to_one'— ключ уникален в обоих DataFrame'one_to_many'— ключ уникален в левом, может повторяться в правом'many_to_one'— ключ может повторяться в левом, уникален в правом
'many_to_many' тоже допустимо, но не проверяет уникальность ни в одной из сторон — оно лишь документирует намерение, не защищая от неожиданного декартова произведения.
Посмотрим, как работает проверка, когда ожидание не совпадает с реальностью:
orders_dup = pd.DataFrame({
'order_id': [1, 1, 2], # order_id=1 дублируется
'amount': [100, 150, 200]
})
clients = pd.DataFrame({'order_id': [1, 2, 4], 'client': ['Аня', 'Боря', 'Вера']})
try:
result = pd.merge(
orders_dup, clients,
on='order_id',
how='inner',
validate='one_to_one'
)
except pd.errors.MergeError as e:
print(e)
# Merge keys are not unique in left dataset; not a one-to-one merge
Если бы не было validate, merge молча выполнился бы и вернул три строки вместо двух — order_id=1 объединился бы с Аней дважды. Это декартово произведение по ключу, которое легко принять за корректный результат.
Когда дубликаты по ключу ожидаемы (например, один клиент — много заказов), используют 'many_to_one':
result = pd.merge(
orders_dup, clients,
on='order_id',
how='inner',
validate='many_to_one'
)
print(result.shape) # (3, 3) — корректно, validate не возражает
В практике validate особенно важен при соединении со справочниками: если справочник должен быть уникален по ключу, validate='many_to_one' это гарантирует и сразу сообщает о проблеме, если в справочнике завелись дубликаты.
pd.concat с ignore_index=True: добавление строк без соединения по ключу
pd.merge работает по ключу — он ищет совпадения между двумя таблицами. pd.concat делает принципиально другое: он просто складывает строки одна под другой, без какого-либо поиска пар.
Типичный сценарий — дозагрузка данных. Есть таблица заказов за прошлый месяц, появилась новая партия за текущий. Нужно объединить их в один DataFrame.
import pandas as pd
orders_old = pd.DataFrame({
'order_id': [1, 2, 3],
'amount': [100, 200, 300]
})
orders_new = pd.DataFrame({
'order_id': [4, 5],
'amount': [400, 500]
})
all_orders = pd.concat([orders_old, orders_new])
print(all_orders)
# order_id amount
# 0 1 100
# 1 2 200
# 2 3 300
# 0 4 400
# 1 5 500
Результат правильный по данным, но индекс сломан: значения 0 и 1 повторяются. Это потому что pd.concat по умолчанию сохраняет исходные индексы обоих DataFrame. Если исходные индексы не несут смысловой нагрузки, нужно передать ignore_index=True:
all_orders = pd.concat([orders_old, orders_new], ignore_index=True)
print(all_orders)
# order_id amount
# 0 1 100
# 1 2 200
# 2 3 300
# 3 4 400
# 4 5 500
Теперь индекс — сплошной RangeIndex от 0, как у свежесозданного DataFrame.
Несовпадение схем столбцов
Если у двух DataFrame разные наборы столбцов, pd.concat не выбросит ошибку. Он создаст объединённую схему, а в строках, где столбца не было, поставит NaN:
orders_with_note = pd.DataFrame({
'order_id': [6],
'amount': [600],
'note': ['срочно']
})
all_orders_with_note = pd.concat([orders_old, orders_with_note], ignore_index=True)
print(all_orders_with_note)
# order_id amount note
# 0 1 100 NaN
# 1 2 200 NaN
# 2 3 300 NaN
# 3 6 600 срочно
Столбец note появился из второго DataFrame. Три строки из orders_old получили NaN в этом столбце — механика та же, что при left/outer join, но причина другая: не несовпадение ключей, а отсутствие столбца в одном из источников.
Это важно проверять после concat: если схемы таблиц расходятся неожиданно, NaN-столбцы сразу это покажут:
print(all_orders_with_note.isna().sum())
# order_id 0
# amount 0
# note 3
Три пропуска в note — три из четырёх строк пришли из таблицы без этого столбца.
Чем pd.concat отличается от merge
pd.concat не знает ничего о содержимом строк — он не проверяет ключи, не ищет совпадений и не контролирует кардинальность. Если передать два одинаковых DataFrame, он просто удвоит строки без предупреждения. Поэтому после concat стоит проверить, что итоговое число строк соответствует ожидаемому:
# all_orders = pd.concat([orders_old, orders_new], ignore_index=True)
expected = len(orders_old) + len(orders_new)
assert len(all_orders) == expected, f"Ожидалось {expected} строк, получено {len(all_orders)}"
Если нужно проверить уникальность ключа после объединения, используйте duplicated:
dupes = all_orders[all_orders.duplicated(subset=['order_id'])]
if not dupes.empty:
print(f"Дубликаты order_id: {dupes['order_id'].tolist()}")
Что делать с дубликатами — зависит от спецификации задачи: нужно знать не только по какому столбцу проверять уникальность (subset), но и какую запись считать правильной (keep='first', keep='last' или иной способ разрешения). Без явного правила удалять строки не стоит: одинаковые записи могут быть корректными наблюдениями, а не ошибками.
Когда нужно добавить строки — pd.concat. Когда нужно обогатить таблицу данными из другой по общему ключу — pd.merge.
Закрепите тему на задачах
Перед merge проверьте уникальность ключей с обеих сторон. Рост числа строк может быть закономерным many-to-many соединением, а не просто дублями исходной таблицы.
pandas merge(how='left'): что будет без совпадения ключа
pandas merge: validate и suffixes при соединении таблиц
pandas merge indicator: right и outer join без потери строк
Откройте условие и решите задачу перед чтением следующей темы. В задачах на код проверяется программа, в тестовых заданиях — выбранный ответ. Для последовательного обучения вернитесь в программу бесплатного курса.
Задача 1
DataFrame orders (столбцы: order_id, amount) и DataFrame clients (столбцы: order_id, name) соединяются:
result = pd.merge(orders, clients, on='order_id', how='left')
В orders есть order_id=5, которого нет в clients. Что будет в result для строки с order_id=5 в столбце 'name'?
Задача 2
Выполнен код:
import pandas as pd
from pandas.errors import MergeError
df1 = pd.DataFrame({'id': [1, 1, 2], 'val': [10, 20, 30]})
df2 = pd.DataFrame({'id': [1, 2], 'info': ['a', 'b']})
try:
result = pd.merge(df1, df2, on='id', how='inner', validate='many_to_one')
except MergeError as e:
print('error')
else:
print(result.shape[0])
Что будет выведено?
Задача 3
Выполнен код:
import pandas as pd
a = pd.DataFrame({'k': [1, 2], 'v': [10, 20]})
b = pd.DataFrame({'k': [3, 4], 'v': [30, 40]})
result = pd.concat([a, b], ignore_index=True)
Чему равен result['k'].tolist()?
Задача 4
Склад и поставки: три типа join, indicator и concat за один проход
Что уже дано
Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет — вы читаете всё сами.
Что нужно сделать
Напишите программу, которая:
- Читает из
stdinцелое числоp— количество строк таблицы товаров. - Читает следующие
pстрок; каждая содержит два токена через пробел:item_id(целое число) иstock(целое число). Создаётpd.DataFrameс именемitemsи столбцами'item_id','stock'. - Читает целое число
s— количество строк таблицы поставок. - Читает следующие
sстрок; каждая содержит два токена через пробел:item_id(целое число) иsupply(целое число). Создаётpd.DataFrameс именемsuppliesи столбцами'item_id','supply'. - Шаг A. inner join. Выполняет
pd.merge(items, supplies, on='item_id', how='inner'). Выводит одну строку:inner shape=<shape>. - Шаг B. left join. Выполняет
pd.merge(items, supplies, on='item_id', how='left'). Выводит одну строку:left nan_supply=<k>, где<k>— число NaN в столбце'supply'. - Шаг C. outer join с indicator. Выполняет
pd.merge(items, supplies, on='item_id', how='outer', indicator=True). Выводит одну строку:outer left_only=<a> right_only=<b> both=<c>, где<a>,<b>,<c>— число строк с соответствующим значением'_merge'. - Шаг D. concat. Читает целое число
e— количество строк дополнительных товаров. Читает следующиеeстрок; каждая содержит два токена:item_id(целое число) иstock(целое число). Создаётpd.DataFrameextraс теми же столбцами. Объединяетitemsиextraчерезpd.concat([items, extra], ignore_index=True). Выводит одну строку:concat shape=<shape>.
Ввод и вывод
Программа читает stdin и пишет в stdout.
Ограничения: p >= 1, s >= 1.
Формат ввода
p item_id stock ... s item_id supply ... e item_id stock ...
Формат вывода
Ровно четыре строки в порядке шагов A–D:
inner shape=(<r>, <c>)
left nan_supply=<k>
outer left_only=<a> right_only=<b> both=<c>
concat shape=(<r>, <c>)
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задача 5
Суффиксы и validate: merge двух таблиц с общим столбцом-не-ключом
Что уже дано
Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет — вы читаете всё сами.
Что нужно сделать
Напишите программу, которая работает с двумя таблицами: складом (warehouse) и поставщиком (supplier). Обе таблицы содержат столбец rating (помимо ключа), поэтому при merge появятся суффиксы.
- Читает из
stdinцелое числоw— количество строк таблицы склада. - Читает следующие
wстрок; каждая содержит три токена через пробел:item_id(целое число),stock(целое число),rating(вещественное число). Создаётpd.DataFrameс именемwarehouseи столбцами'item_id','stock','rating'. - Читает целое число
s— количество строк таблицы поставщика. - Читает следующие
sстрок; каждая содержит три токена через пробел:item_id(целое число),supply(целое число),rating(вещественное число). Создаётpd.DataFrameс именемsupplierи столбцами'item_id','supply','rating'. - Шаг A. outer join с явными суффиксами. Выполняет:
merged = pd.merge(warehouse, supplier, on='item_id', how='outer',
suffixes=('_wh', '_sup'))
Выводит одну строку: cols=<список имён столбцов> — list(merged.columns) как строку Python.
Затем выводит: nan_supply=<k> — число NaN в столбце 'supply'.
Затем выводит: nan_rating_sup=<k> — число NaN в столбце 'rating_sup'.
6. Шаг B. validate='one_to_one' — ожидаемая ошибка. Выполняет merge с теми же параметрами плюс validate='one_to_one' внутри try/except и при поимке pd.errors.MergeError выводит строку: validate=error.
7. Шаг C. validate='one_to_many' — успешное соединение. Выполняет inner join тех же таблиц с validate='one_to_many' и suffixes=('_wh', '_sup'). Выводит одну строку: validate=ok shape=<shape>.
Ввод и вывод
Программа читает stdin и пишет в stdout.
Ограничения: w >= 1, s >= 1.
Формат ввода
w item_id stock rating ... s item_id supply rating ...
Гарантируется:
- ключ
item_idуникален в таблицеwarehouse(one side); - в таблице
supplierхотя бы одинitem_idвстречается более одного раза (many side), поэтомуvalidate='one_to_one'обязан выброситьMergeError; - хотя бы один
item_idизwarehouseотсутствует вsupplier(порождает NaN вsupplyиrating_supпри outer join); - хотя бы один
item_idизsupplierотсутствует вwarehouse(порождает NaN вstockиrating_wh).
Формат вывода
Ровно пять строк:
cols=<list>
nan_supply=<k>
nan_rating_sup=<k>
validate=error
validate=ok shape=(<r>, <c>)
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задача 6
Найди и исправь: три ошибки в pd.merge (how, indicator, pd.concat)
Что уже дано
Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет.
В редакторе уже есть сломанный код. В нём ровно три ошибки в трёх независимых строках:
- Шаг A:
pd.merge(base, updates, on='sku', how='inner')— используетсяinner, хотя нужно сохранить все строки левого DataFrame и добавить данные из правого там, где есть совпадение (how='left'). - Шаг B:
pd.merge(base, updates, on='sku', how='outer')вызывается безindicator=True, хотя нужно добавить столбец'_merge'для диагностики происхождения строк. - Шаг C:
pd.concat([base, extra])вызывается безignore_index=True, хотя нужно получить сброшенный RangeIndex.
Каждая ошибка находится в отдельной строке кода. Исправьте все три.
Что нужно сделать
Исправьте три ошибки так, чтобы программа:
- Читала из
stdinцелое числоb— количество строк базовой таблицы склада. - Читала следующие
bстрок; каждая содержит два токена через пробел:sku(целое число) иqty(целое число). Создавалаpd.DataFrameс именемbaseи столбцами'sku','qty'. - Читала целое число
u— количество строк таблицы обновлений. - Читала следующие
uстрок; каждая содержит два токена через пробел:sku(целое число) иnew_qty(целое число). Создавалаpd.DataFrameс именемupdatesи столбцами'sku','new_qty'. - Шаг A. left join. Выполняла
pd.merge(base, updates, on='sku', how='left'). Выводила одну строку:left shape=<shape>иleft nan=<k>, где<k>— число NaN в столбце'new_qty'. Формат:left shape=(r, c) nan=k. - Шаг B. outer join с indicator. Выполняла
pd.merge(base, updates, on='sku', how='outer', indicator=True). Выводила одну строку:both=<a> left_only=<b> right_only=<c>— число строк с соответствующим значением'_merge'. - Читала целое число
e— количество строк дополнительных записей. - Читала следующие
eстрок; каждая содержит два токена:sku(целое число) иqty(целое число). Создавалаpd.DataFrameextraс теми же столбцами'sku','qty'. - Шаг C. concat с ignore_index. Выполняла
pd.concat([base, extra], ignore_index=True). Выводила одну строку:concat shape=<shape> index_start=<first> index_end=<last>, где<first>и<last>— первый и последний элементы индекса результата.
Ввод и вывод
Программа читает stdin и пишет в stdout.
Ограничения: b >= 1, u >= 1.
Формат ввода
b sku qty ... u sku new_qty ... e sku qty ...
Формат вывода
Ровно три строки:
left shape=(r, c) nan=k
both=a left_only=b right_only=c
concat shape=(r, c) index_start=0 index_end=last
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задача 7
Right join и outer с indicator: диагностика несовпавших строк
Что уже дано
Проверяющая система запускает вашу программу и подаёт данные через stdin. Никаких заранее созданных переменных нет — вы читаете всё сами.
Что нужно сделать
Напишите программу, которая работает с двумя таблицами — курсов и записей студентов.
- Читает из
stdinцелое числоc— количество строк таблицы курсов. - Читает следующие
cстрок; каждая содержит два токена через пробел:course_id(целое число) иtitle(строка без пробелов). Создаётpd.DataFrameс именемcoursesи столбцами'course_id','title'. - Читает целое число
r— количество строк таблицы записей. - Читает следующие
rстрок; каждая содержит два токена через пробел:course_id(целое число) иstudent(строка без пробелов). Создаётpd.DataFrameс именемregistrationsи столбцами'course_id','student'. - Шаг A. right join. Выполняет
pd.merge(courses, registrations, on='course_id', how='right'). Выводит одну строку:right shape=<shape>, затем одну строку:right nan_title=<k>— число NaN в столбце'title'результата. - Шаг B. outer join с indicator. Выполняет
pd.merge(courses, registrations, on='course_id', how='outer', indicator=True). Выводит одну строку:outer both=<a> left_only=<b> right_only=<c>— число строк с каждым значением'_merge'. - Шаг C. NaN по оси right. В результате outer join из шага B подсчитывает число NaN в столбце
'student'и выводит одну строку:outer nan_student=<k>.
Ввод и вывод
Программа читает stdin и пишет в stdout.
Ограничения: c >= 1, r >= 1.
Формат ввода
c course_id title ... r course_id student ...
Формат вывода
Ровно четыре строки в порядке шагов A–C:
right shape=(<rows>, <cols>)
right nan_title=<k>
outer both=<a> left_only=<b> right_only=<c>
outer nan_student=<k>
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задачи к уроку
- pandas merge(how='left'): что будет без совпадения ключа
Что появится в столбце правой таблицы после left join, если ключ не найден? Проверьте понимание pd.merge на короткой задаче.
- pandas merge: validate, suffixes и MergeError — задача
Зачем нужны validate и suffixes в pandas merge? Пример проверки ключей и задача на соединение таблиц с бесплатной проверкой кода.
- pandas merge indicator: outer join и несовпавшие ключи
Как найти строки без пары через merge indicator=True? Пример outer join, диагностика несовпавших ключей и задача pandas с проверкой кода.
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
