PandasJuniorCoding
Как обрабатывать дублированные строки в DataFrame — duplicated() и drop_duplicates()?
duplicated() возвращает булеву маску дублей. drop_duplicates() удаляет их. Параметр subset задаёт колонки для сравнения, keep=("first"/"last"/False) — какие вхождения оставить. NaN считаются одинаковыми. Всегда делайте reset_index после очистки.
Два метода — разные задачи
duplicated() возвращает булеву маску: True для строк, которые являются дубликатами. drop_duplicates() возвращает DataFrame без дубликатов. Оба принимают одинаковые параметры.
Базовое использование
import pandas as pd
df = pd.DataFrame({
"user_id": [1, 2, 2, 3, 3],
"event": ["click", "click", "click", "view", "purchase"],
"ts": pd.to_datetime(["2024-01-01"] * 3 + ["2024-01-02"] * 2),
})
# Найти полные дубликаты
print(df.duplicated()) # [False, False, True, False, False]
# Удалить полные дубликаты, оставить первое вхождение
df_clean = df.drop_duplicates()
print(df_clean.shape) # (4, 3)
# Дубликаты только по user_id (разные события — не дубликаты)
print(df.duplicated(subset=["user_id"]))
# [False, False, True, False, True]
Параметр keep
# keep="first" (по умолчанию) — оставить первое вхождение
df.drop_duplicates(subset=["user_id"], keep="first")
# keep="last" — оставить последнее вхождение
df.drop_duplicates(subset=["user_id"], keep="last")
# keep=False — удалить ВСЕ строки с дублирующимся ключом
df.drop_duplicates(subset=["user_id"], keep=False)
# Полезно, чтобы найти только уникальные события (без повторений вообще)
Практический паттерн: дедупликация событий
# Оставить последнее событие каждого пользователя
# (вместо drop_duplicates лучше sort + keep="last")
df_latest = (
df.sort_values("ts")
.drop_duplicates(subset=["user_id"], keep="last")
.reset_index(drop=True)
)
print(df_latest)
# Посмотреть, сколько дубликатов по каждому user_id
dup_counts = df[df.duplicated(subset=["user_id"], keep=False)].groupby("user_id").size()
print(dup_counts)
Работа с NaN
# По умолчанию два NaN считаются одинаковыми (дублируют друг друга)
df2 = pd.DataFrame({"a": [1, None, None], "b": ["x", "y", "y"]})
print(df2.duplicated()) # [False, False, True]
Подводные камни
- inplace=True создаёт копию с CoW — с включённым Copy-on-Write (Pandas 2.0+)
df.drop_duplicates(inplace=True)может не менять оригинал, если df — срез; безопаснееdf = df.drop_duplicates(). - NaN == NaN для duplicated — два NaN в одном столбце считаются дублями, что расходится с поведением обычного равенства Python (
float("nan") != float("nan")). - keep=False удаляет все вхождения — легко перепутать с «удалить лишние копии»; если нужно оставить хотя бы одну запись, используйте
keep="first". - reset_index после drop_duplicates — индекс остаётся от исходного DataFrame; при последующих операциях по позиции это приводит к ошибкам.
- subset не проверяет орфографию — если в списке
subsetесть опечатка в имени колонки, Pandas поднимает KeyError только в момент вызова, а не при объявлении. - Производительность на больших данных —
drop_duplicatesстроит хэш-таблицу; на DataFrame с миллионами строк и широким subset стоит профилировать или использоватьpolars/dask. - Порядок строк после keep="last" —
drop_duplicates(keep="last")не сортирует DataFrame; строки остаются в исходном порядке, просто выбирается последнее вхождение по позиции в df.
Common mistakes
- Объяснять
duplicate rowsтолько синтаксисом без shape, dtype, состояния или режима выполнения. - Игнорировать leakage, воспроизводимость, пустые входы и скрытые копии данных.
- Не проверять production-симптомы: latency, память, ретраи, дрейф качества и несовпадение версий.
What the interviewer is testing
- Может ли связать
duplicate rowsс реальным контрактом входов и выходов. - Упоминает ли тесты, метрики, reproducibility и диагностику ошибок.
- Видит ли различие между demo-кодом в ноутбуке и production-пайплайном.