0Pricing
Pandas & NumPy Academy · Урок

Левое и правое объединение

Выполняйте левые и правые объединения, сохраняя все строки одной таблицы и сопоставляя записи из другой.

«Левое и правое объединение» — бесплатный урок Pandas & NumPy Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Pandas & NumPy Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Pandas & NumPy Academy содержит 4 уроков всего.

Асимметричные объединения

Внутреннее и внешнее объединения рассматривают оба DataFrame симметрично. Но часто требуется сохранить все записи из одной таблицы, дополнив их данными из другой. Для этого используются левые объединения и правые объединения. Такие асимметричные объединения очень распространены в аналитике: сохранить все транзакции и добавить названия товаров, если они доступны; сохранить всех пользователей и добавить дату их последней покупки, если она есть.

Левое объединение: сохранение всех строк слева

Левое объединение (how='left') сохраняет каждую строку из левого DataFrame. Для строк, у которых найден совпадающий ключ в правом DataFrame, в правые столбцы записываются совпавшие значения. Для строк без совпадения в правые столбцы записывается NaN. Количество строк в результате всегда равно количеству строк в левом DataFrame (если ключи не дублируются).

import pandas as pd

orders = pd.DataFrame({
    'order_id': [1, 2, 3, 4],
    'customer_id': [101, 102, 101, 999],
    'amount': [250, 80, 320, 150]
})
customers = pd.DataFrame({
    'customer_id': [101, 102, 103],
    'name': ['Alice', 'Bob', 'Carol']
})

result = pd.merge(orders, customers, on='customer_id', how='left')
print(result)
# All 4 orders kept; order with customer_id=999 gets NaN for name

Структура результата левого объединения

После левого объединения строки из левого DataFrame, для которых не найдено совпадение в правом, будут содержать NaN во всех столбцах, пришедших из правой таблицы. С помощью логического фильтра по столбцу правой таблицы можно найти такие строки и подсчитать, сколько левых строк не имели совпадения, — это полезная проверка качества данных.

result = pd.merge(orders, customers, on='customer_id', how='left')
print(result)
#    order_id  customer_id  amount   name
# 0         1          101     250  Alice
# 1         2          102      80    Bob
# 2         3          101     320  Alice
# 3         4          999     150    NaN  <- no matching customer

# Count unmatched
unmatched = result['name'].isna().sum()
print(f'{unmatched} orders have no customer record')

Когда использовать левое объединение

Левые объединения подходят, когда левый DataFrame является основной таблицей, а правая таблица содержит дополнительные данные. Распространённые случаи: дополнение таблицы фактов таблицей измерений (заказы и названия товаров), добавление необязательных метаданных (пользователи и данные профиля) или расчёт показателей для всех записей, даже если поиск соответствия завершился неудачей. В аналитическом коде левые объединения встречаются гораздо чаще правых.

# Realistic pattern: enrich all products with optional category data
products = pd.DataFrame({'sku': ['A1', 'B2', 'C3'], 'price': [10, 20, 15]})
categories = pd.DataFrame({'sku': ['A1', 'C3'], 'category': ['Tools', 'Home']})

# Keep all products; add category where available
enriched = pd.merge(products, categories, on='sku', how='left')
print(enriched)
#    sku  price category
# 0   A1     10    Tools
# 1   B2     20      NaN
# 2   C3     15     Home

Правое объединение: сохранение всех строк справа

Правое объединение (how='right') является зеркальным отражением левого: оно сохраняет каждую строку из правого DataFrame и записывает NaN в столбцы левой таблицы, если совпадение не найдено. Правые объединения используются реже, поскольку того же результата всегда можно достичь, поменяв DataFrames местами и применив левое объединение, что понятнее читателям.

# Right join: keep all customers, attach orders where they exist
result = pd.merge(orders, customers, on='customer_id', how='right')
print(result)
# All 3 customers appear; Carol (103) has NaN for order_id and amount

# Equivalent left join (swap arguments):
result2 = pd.merge(customers, orders, on='customer_id', how='left')
# Same data, just column order differs

Сравнение всех четырёх типов объединения

Четыре типа объединения различаются только тем, какие строки со стороны без совпадений сохраняются. inner: только совпавшие строки. outer: все строки с обеих сторон. left: все строки слева. right: все строки справа. Для несовпадающих записей столбцы отсутствующей стороны заполняются значениями NaN. Правильный выбор крайне важен, чтобы не удалять и не дублировать записи незаметно.

# Summary table
# how='inner'  : rows in BOTH tables
# how='left'   : ALL left rows  + matched right
# how='right'  : matched left   + ALL right rows
# how='outer'  : ALL left rows  + ALL right rows

# Test each
for how in ['inner', 'left', 'right', 'outer']:
    r = pd.merge(orders, customers, on='customer_id', how=how)
    print(f'{how}: {len(r)} rows')

Заполнение NaN в результатах левого объединения

После левого объединения несовпадающие строки содержат NaN в столбцах правой таблицы. Часто их требуется заменить разумными значениями по умолчанию — например, 'Unknown' для отсутствующей категории или 0 для отсутствующего количества. Используйте fillna() для результата или передайте fill_value в арифметические операции, выполняемые после объединения.

result = pd.merge(products, categories, on='sku', how='left')
result['category'] = result['category'].fillna('Uncategorised')
print(result)
#    sku  price       category
# 0   A1     10          Tools
# 1   B2     20  Uncategorised
# 2   C3     15           Home

Антиобъединение слева: строки без совпадений

Полезный шаблон, который напрямую не поддерживается параметром how, — это антиобъединение: сохраняются только левые строки, для которых нет совпадения в правой таблице. Реализуйте его с помощью левого объединения и indicator=True, а затем отфильтруйте строки по условию _merge == 'left_only'. Это идеально подходит для поиска потерянных записей, новых элементов, отсутствующих в справочном списке, или транзакций, отсутствующих в журнале.

# Anti-join: orders with no matching customer record
result = pd.merge(orders, customers, on='customer_id',
                  how='left', indicator=True)
unmatched_orders = result[result['_merge'] == 'left_only'].drop(columns='_merge')
print(unmatched_orders)
#    order_id  customer_id  amount  name
# 3         4          999     150   NaN

Сохранение количества строк при левом объединении

Если в правом DataFrame ключи уникальны, левое объединение гарантированно сохраняет точное количество строк в левой таблице. Однако если в правой таблице есть дублирующиеся значения ключа, левое объединение умножает строки так же, как внутреннее. Всегда проверяйте, что количество строк в результате совпадает с количеством строк в левой таблице, если ожидается связь «один ко многим».

# Right table has duplicate keys -> row multiplication
orders_small = pd.DataFrame({'id': [1, 2], 'amount': [100, 200]})
multi_customer = pd.DataFrame({
    'id': [1, 1],  # duplicate!
    'contact': ['Alice', 'Alice2']
})

result = pd.merge(orders_small, multi_customer, on='id', how='left')
print(len(result))  # 3 rows! order 1 appears twice
print(result)

Практический шаблон левого объединения

Вот полный рабочий процесс из практики: начните с журнала транзакций, выполните левое объединение с каталогом товаров, чтобы получить названия, а затем левое объединение с таблицей клиентов, чтобы получить имена. Замените отсутствующие результаты поиска значениями по умолчанию. Цепочка левых объединений гарантирует сохранение каждой строки транзакций, даже если запись о товаре или клиенте отсутствует в справочных таблицах.

transactions = pd.DataFrame({
    'txn_id': [1, 2, 3],
    'cust_id': [10, 11, 99],
    'prod_id': [20, 21, 20],
    'total': [50, 30, 70]
})

custs = pd.DataFrame({'cust_id': [10, 11], 'cust_name': ['Alice', 'Bob']})
prods = pd.DataFrame({'prod_id': [20, 21], 'prod_name': ['Widget', 'Gadget']})

result = (
    transactions
    .merge(custs, on='cust_id', how='left')
    .merge(prods, on='prod_id', how='left')
)
print(result)

Выбор между левым и внутренним объединением

Выбор между левым и внутренним объединением — это решение, связанное с бизнес-логикой: следует ли сохранять записи без найденного соответствия или незаметно удалять их? Используйте левое объединение, если отсутствие соответствия допустимо и требуется сохранить все основные записи. Используйте внутреннее объединение, если отсутствие соответствия означает, что запись недействительна и её следует исключить из анализа. Всегда документируйте свой выбор и его причину.

# Left join: keep all transactions (some may have no product name)
result_left = pd.merge(transactions, prods, on='prod_id', how='left')
print('Left join rows:', len(result_left))   # same as transactions

# Inner join: only transactions with known product
result_inner = pd.merge(transactions, prods, on='prod_id', how='inner')
print('Inner join rows:', len(result_inner))  # may be fewer

Быстрая проверка

Проверьте, насколько хорошо Вы поняли левые и правые объединения из этого урока.

Итоги урока

В этом уроке Вы узнали, что левое объединение сохраняет все строки из левого DataFrame, заполняя правые столбцы значением NaN для несовпадающих строк; правое объединение является его зеркальным отражением; шаблон антиобъединения с использованием indicator=True находит левые строки без совпадения справа; а выбор между левым и внутренним объединением является решением, связанным с бизнес-логикой. Далее Вы изучите объединение DataFrames по индексу, а не по столбцу.

Часто задаваемые вопросы

Урок «Левое и правое объединение» бесплатный?

Да — полный текст урока «Левое и правое объединение» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Pandas & NumPy Academy, подпишись на CoddyKit PRO. Курс Pandas & NumPy Academy содержит 4 уроков всего.

Чему я научусь в уроке «Левое и правое объединение»?

Выполняйте левые и правые объединения, сохраняя все строки одной таблицы и сопоставляя записи из другой. Ты практикуешь Pandas & NumPy Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Pandas & NumPy Academy?

Предыдущий опыт не требуется. Pandas & NumPy Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Левое и правое объединение»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Pandas & NumPy Academy?

Да. Каждый урок Pandas & NumPy Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. pd.concat для объединения DataFrames
  2. pd.merge: внутреннее и внешнее объединение
  3. Левое и правое объединение
  4. Объединение по индексу
← Назад к Pandas & NumPy Academy