Что такое LEFT ANTI JOIN в PySpark?

Ответ

LEFT ANTI JOIN в PySpark возвращает только те строки из левой таблицы (DataFrame), для которых нет совпадений в правой таблице по условию соединения. Это эффективный способ фильтрации данных, аналогичный операции "NOT IN" или "NOT EXISTS" в SQL.

Синтаксис и пример:

from pyspark.sql import SparkSession
from pyspark.sql.functions import col

# Создание сессии Spark
spark = SparkSession.builder 
    .appName("LeftAntiJoinExample") 
    .getOrCreate()

# Создание тестовых DataFrames
orders = spark.createDataFrame([
    (1, "2024-01-01", 100.0),
    (2, "2024-01-02", 200.0),
    (3, "2024-01-03", 150.0),
    (4, "2024-01-04", 300.0)
], ["order_id", "order_date", "amount"])

cancelled_orders = spark.createDataFrame([
    (2, "client_request"),
    (4, "out_of_stock")
], ["order_id", "cancel_reason"])

# LEFT ANTI JOIN: найти НЕ отмененные заказы
active_orders = orders.join(
    cancelled_orders,
    orders.order_id == cancelled_orders.order_id,
    "left_anti"
)

active_orders.show()

Результат:

+--------+----------+------+
|order_id|order_date|amount|
+--------+----------+------+
|       1|2024-01-01| 100.0|
|       3|2024-01-03| 150.0|
+--------+----------+------+

Ключевые особенности LEFT ANTI JOIN:

  1. Возвращает только левую таблицу — столбцы правой таблицы не включаются в результат
  2. Условие соединения — обязательно указать условие (ON), иначе будет CROSS JOIN
  3. Производительность — оптимизированная реализация в Spark, часто быстрее чем filter(NOT EXISTS...)

Эквивалентные операции:

# Способ 1: LEFT ANTI JOIN (рекомендуется)
df1.join(df2, "key", "left_anti")

# Способ 2: Filter с подзапросом (менее эффективно)
df1.filter(~col("key").isin(df2.select("key").rdd.flatMap(lambda x: x).collect()))

# Способ 3: Использование except (требует одинаковой схемы)
df1.select("key").exceptAll(df2.select("key"))

Практические сценарии использования:

  1. Поиск отсутствующих записей: клиенты без заказов, продукты без продаж
  2. Очистка данных: удаление тестовых или некорректных записей
  3. Валидация: проверка целостности данных между системами
  4. A/B тестирование: выделение контрольной группы

Пример с несколькими условиями:

# ANTI JOIN по нескольким полям
result = df1.join(
    df2,
    (df1.id == df2.id) & (df1.date == df2.date),
    "left_anti"
)

Важное замечание: LEFT ANTI JOIN в Spark игнорирует дубликаты в правой таблице — достаточно хотя бы одного совпадения, чтобы исключить строку из левой таблицы.

Ответ 18+ 🔞

Э, слушай, давай разберём эту штуку — LEFT ANTI JOIN в PySpark. Это ж как будто тебе надо найти всех, кто не пришёл на тусовку, хотя вроде бы всех позвали. Представь: у тебя список гостей (левая таблица), и список тех, кто реально пришёл и отметился у входа (правая таблица). LEFT ANTI JOIN — это как раз операция, которая тебе выдаст тех, кто не пришёл, ёпта. Тех, кого нет в правой таблице. Овердохуища полезная штука, когда надо отфильтровать то, чего нет.

Вот смотри на примере, тут всё понятно станет:

from pyspark.sql import SparkSession
from pyspark.sql.functions import col

# Запускаем наш движок
spark = SparkSession.builder 
    .appName("LeftAntiJoinExample") 
    .getOrCreate()

# Допустим, есть заказы
orders = spark.createDataFrame([
    (1, "2024-01-01", 100.0),
    (2, "2024-01-02", 200.0),
    (3, "2024-01-03", 150.0),
    (4, "2024-01-04", 300.0)
], ["order_id", "order_date", "amount"])

# А есть отменённые заказы — отдельный список
cancelled_orders = spark.createDataFrame([
    (2, "client_request"),
    (4, "out_of_stock")
], ["order_id", "cancel_reason"])

# И вот магия: LEFT ANTI JOIN — найди мне заказы, которых НЕТ в списке отменённых
active_orders = orders.join(
    cancelled_orders,
    orders.order_id == cancelled_orders.order_id,
    "left_anti"  # Вот этот самый волшебный флаг!
)

active_orders.show()

И что мы получим? Ни хуя себе, всего два заказа!

+--------+----------+------+
|order_id|order_date|amount|
+--------+----------+------+
|       1|2024-01-01| 100.0|
|       3|2024-01-03| 150.0|
+--------+----------+------+

Заказы 2 и 4 — накрылись медным тазом, их в результате нет. А 1 и 3 — живые, активные. Всё, бля, элементарно.

На что смотреть, чтобы не облажаться:

  1. Возвращается только левая таблица. Колонки из правой таблицы (cancel_reason) в результат не попадут. Ты просто фильтруешь левую таблицу по правой. Доверия ебать ноль к правой таблице — она только для проверки.
  2. Условие соединения (ON) обязательно. А то получишь декартово произведение, и тогда терпения ноль ебать — будет считать сто лет, а потом упадёт.
  3. Под капотом это оптимизировано. Spark это делает умно, обычно быстрее, чем если бы ты вручную писал filter(NOT IN(...)). Не изобретай велосипед.

Чем ещё можно сделать, но обычно не стоит:

# 1. LEFT ANTI JOIN (нормальный способ, делай так)
df1.join(df2, "key", "left_anti")

# 2. Фильтр с подзапросом (можно, но часто тормозно и некрасиво)
df1.filter(~col("key").isin(df2.select("key").rdd.flatMap(lambda x: x).collect()))

# 3. Except (работает, но нужны абсолютно одинаковые колонки — **хитрая жопа**)
df1.select("key").exceptAll(df2.select("key"))

Где это реально пригождается, чувак?

  • Найти клиентов без заказов. Весь маркетинг на них потратили, а они ни хрена не купили.
  • Очистить данные от мусора. Удалил тестовые записи, которые завёл какой-то распиздяй.
  • Сравнить два списка. Вот у тебя выгрузка из одной системы и из другой. Кого нет во второй? LEFT ANTI JOIN тебе сразу покажет.
  • Контрольная группа в A/B тесте. Выделить тех, кто не видел новую хуйню (фичу).

Важный нюанс, а то охуеешь потом: Spark, когда делает LEFT ANTI JOIN, игнорирует дубликаты в правой таблице. Ему похуй, один там совпадающий ключ или десять. Если есть хоть одно совпадение — строка из левой таблицы вылетает из результата. Всё, точка.

Короче, инструмент — пизда рулю. Запомнил: нужны записи из таблицы A, которых нет в B — left_anti и дело в шляпе.