⚫️.merge(indicator=True): как быстро сравнить таблицы
Когда приходит новая выгрузка — ты открываешь Excel, сводные, фильтры, ломаешь глаза. Но в Python есть способ проще, быстрее и надёжнее.
🛠 Что делает .merge(indicator=True)?
— Добавляет колонку _merge, где видно:
• left_only — строки, которые были только в первой таблице
• right_only — только во второй
• both — те, что совпали
💡 Это удобнее, чем писать по 5 .isin():
• Сразу видно, кто исчез и кто появился
• Можно понять, совпали ли ключи
• Отличный способ тестировать join'ы
📦 Где пригодится:
• сравнение выгрузок из CRM и БД
• сверка API-результатов
• проверка “до/после” расчётов
• аудит отчётов и BI
✍️ Пример:df.merge(df2, how='outer', on='user_id', indicator=True)
Вместо 10 строк сравнения — один понятный датафрейм.
🧠 Сравнивать данные — это не скучная рутина, а навык настоящего аналитика.
Ты выгрузил отчёт, получил API-ответ, проверил pipeline — а в данных что-то изменилось. Вопрос: где? почему? а было ли так вчера?
Обычно сравнение таблиц выглядит как боль:added = df_new[~df_new["id"].isin(df_old["id"])]
removed = df_old[~df_old["id"].isin(df_new["id"])]
Или костыль из concat + assign, который потом страшно открывать.
💡 Но можно проще, красивее и безопаснее — через .merge(indicator=True).
При объединении двух таблиц ты получаешь колонку _merge:
• 'left_only' — строки только в старой таблице
• 'right_only' — строки только в новой
• 'both' — всё совпало
Работает с разными источниками (Excel + API, SQL + csv) и не боится разного порядка колонок. Удобно? Очень.
▪️ Пропавшие строки:merged.query("_merge == 'left_only'")
▪️ Новые записи:merged.query("_merge == 'right_only'")
▪️ Проверка на полное совпадение:assert merged["_merge"].eq("both").all()
▪️ Добавить в отчёт статус:merged["status"] = merged["_merge"].map({
"left_only": "удалён",
"right_only": "добавлен",
"both": "остался"
})
▪️ Сравнение значений по ключу:df_old.merge(df_new, on="sku", how="outer",
suffixes=("_old", "_new"), indicator=True)
▫️ Где это реально спасает:
• Сравнение выгрузок (отчёты вчера/сегодня)
• Сверка поставок или данных от партнёров
• Проверка отчётов: auto vs ручной
• Сравнение таблиц между PostgreSQL и BigQuery
• Диагностика: что потерялось в пайплайне
▫️ Почему это инженерный подход .merge(indicator=True) — это:
• читаемо
• безопасно
• тестируемо
• Переименуй _merge → diff_status — читается лучше
• Используй .map() — сразу видно, что добавилось, что исчезло
• Сохраняй diff в Excel — чтобы показать менеджеру/тимлиду
• Используй .query() — фильтрация понятнее, чем через mask
• Добавляй suffixes=("_old", "_new") — чтобы видеть конкретные отличия
▫️ А если всё это собрать в одну функцию — получаешь свой мини-дифф-движок для данных. Уже подготовил как бонус 👇
import pandas as pd from pandas.api.types import CategoricalDtype from typing import List, Optional def diff_tables( df_old: pd.DataFrame, df_new: pd.DataFrame, on: List[str], compare_cols: Optional[List[str]] = None) -> pd.DataFrame: """ Возвращает DataFrame с diff-результатом между двумя таблицами. - compare_cols: если указаны, покажет различия в этих колонках ( по суффиксам _old/_new) """ how = "outer" suffixes = ("_old", "_new") df = df_old.merge( df_new, on=on, how=how, suffixes=suffixes, indicator=True) df["diff_status"] = df["_merge"].map({ "left_only": "удалён", "right_only": "добавлен", "both": "остался" }) if compare_cols: for col in compare_cols: col_old, col_new = f"{col}_old", f"{col}_new" mask = df[col_old] != df[col_new] new_label = f"изменён: {col}" # Добавим категорию, если diff_status категориальный if isinstance(df["diff_status"].dtype, CategoricalDtype): df["diff_status"] = df["diff_status"].astype(str) df.loc[mask & (df["diff_status"] == "остался"), "diff_status"] = new_label return df.drop(columns=["_merge"]) # Пример использования: df_old = pd.DataFrame({"user_id": [1, 2, 3, 4], "value": [10, 20, 30, 40]}) df_new = pd.DataFrame({"user_id": [3, 4, 5, 6], "value": [30, 45, 50, 60]}) diff = diff_tables(df_old, df_new, on=["user_id"], compare_cols=["value"]) print(diff)
Ты видишь, откуда пришла строка, и можешь писать юнит-тесты под diff. А главное — всё прозрачно.