Ответ
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:
- Возвращает только левую таблицу — столбцы правой таблицы не включаются в результат
- Условие соединения — обязательно указать условие (ON), иначе будет CROSS JOIN
- Производительность — оптимизированная реализация в 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"))
Практические сценарии использования:
- Поиск отсутствующих записей: клиенты без заказов, продукты без продаж
- Очистка данных: удаление тестовых или некорректных записей
- Валидация: проверка целостности данных между системами
- 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 — живые, активные. Всё, бля, элементарно.
На что смотреть, чтобы не облажаться:
- Возвращается только левая таблица. Колонки из правой таблицы (
cancel_reason) в результат не попадут. Ты просто фильтруешь левую таблицу по правой. Доверия ебать ноль к правой таблице — она только для проверки. - Условие соединения (ON) обязательно. А то получишь декартово произведение, и тогда терпения ноль ебать — будет считать сто лет, а потом упадёт.
- Под капотом это оптимизировано. 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 и дело в шляпе.