Поиск дубликатов в данных на Python: пошаговая инструкция
За полчаса вы научитесь находить и удалять повторяющиеся строки в CSV-файле с помощью pandas: от полных дубликатов до совпадений по одному столбцу.

К концу этой инструкции у вас будет рабочий скрипт, который открывает CSV-файл, находит повторяющиеся строки и сохраняет очищенную версию в новый файл. На небольшом файле (до сотни тысяч строк) всё займёт минут двадцать вместе с установкой библиотек. Мы разберём три случая: полные дубликаты строк, дубликаты по одному столбцу (например, по email) и почти-дубликаты, которые различаются только регистром или пробелами.
Работаем на Python 3.10+ и pandas 2.x. Всё, что нужно уметь заранее, — запускать .py-файл или ячейку в Jupyter и открывать терминал.
Что нужно иметь до начала
- Python 3.10 или новее. Проверьте командой
python --versionв терминале. Если версия ниже, скачайте свежую с python.org. - Библиотека pandas. Установите её, если ещё нет:
pip install pandas. Версию проверите так:python -c "import pandas; print(pandas.__version__)"— должно вывести что-то вроде2.2.2. - Файл с данными. В примере это
data.csvс колонкамиname,email,city. Положите его в ту же папку, где будет скрипт.
Если данные лежат в Excel, а не в CSV, замените в коде pd.read_csv на pd.read_excel — для этого дополнительно нужен пакет openpyxl (pip install openpyxl).
Шаг 1. Загрузите данные и посмотрите на них
Сначала откроем файл и убедимся, что pandas правильно разобрал колонки.
import pandas as pd
# Читаем CSV. Если разделитель не запятая, а точка с запятой (частый случай
# в русскоязычных выгрузках), добавьте sep=";"
df = pd.read_csv("data.csv")
# Сколько строк и столбцов
print("Размер:", df.shape)
# Первые пять строк
print(df.head())Проверка: в консоли появится строка вида Размер: (1000, 3) и таблица с вашими колонками. Если вместо колонок вы видите одну длинную строку — значит, неверный разделитель, добавьте sep=";" в read_csv.
Шаг 2. Найдите полные дубликаты строк
Полный дубликат — это строка, у которой все значения совпадают с другой строкой. Метод duplicated() возвращает булеву маску: True напротив тех строк, которые уже встречались выше.
# Маска дубликатов: True у повторных вхождений
mask = df.duplicated()
# Сколько всего дубликатов
print("Дубликатов:", mask.sum())
# Показать сами дублирующиеся строки
print(df[mask])Проверка: строка Дубликатов: 12 (число зависит от ваших данных). Если вывелось Дубликатов: 0 — полных повторов нет, но по отдельному столбцу они ещё могут быть, это Шаг 4.
Важный нюанс: keep
По умолчанию duplicated() помечает как дубликат каждое повторное вхождение, а первое оставляет. Параметр keep меняет поведение:
keep="first"(по умолчанию) — первое вхождение не считается дубликатом;keep="last"— дубликатом считается всё, кроме последнего вхождения;keep=False— помечаются все вхождения, включая первое. Полезно, когда нужно увидеть все повторы целиком.
Шаг 3. Удалите полные дубликаты
# Удаляем дубликаты, оставляя первое вхождение каждой строки
df_clean = df.drop_duplicates()
print("Было строк:", len(df))
print("Стало строк:", len(df_clean))Проверка: «стало» меньше «было» ровно на число дубликатов из Шага 2. Если числа равны, а дубликаты были — вы, скорее всего, забыли присвоить результат в новую переменную: drop_duplicates() не меняет исходный DataFrame, а возвращает новый.
Шаг 4. Найдите дубликаты по одному столбцу
Часто «дубликат» — это не полное совпадение строки, а повтор ключевого поля. Например, два клиента с одинаковым email, но разными городами. Передайте имя столбца в subset.
# Все строки, где email встречается больше одного раза
email_dups = df[df.duplicated(subset="email", keep=False)]
# Сортируем по email, чтобы дубли шли рядом и их было видно глазами
print(email_dups.sort_values("email"))Проверка: в выводе одинаковые email идут парами или группами подряд. Чтобы оставить по одной записи на email, используйте df.drop_duplicates(subset="email").
Можно задать несколько столбцов сразу — дубликатом будет считаться совпадение по всем перечисленным: subset=["name", "city"].
Шаг 5. Ловите почти-дубликаты: регистр и пробелы
Самая частая ловушка: ivan@mail.ru и Ivan@Mail.ru (с пробелом в конце) pandas считает разными значениями. Перед поиском данные нужно привести к единому виду.
# Приводим email к нижнему регистру и убираем пробелы по краям.
# .str работает только со строковыми колонками.
df["email"] = df["email"].str.strip().str.lower()
# Теперь ищем дубликаты уже по нормализованному столбцу
normalized_dups = df[df.duplicated(subset="email", keep=False)]
print("Дубликатов после нормализации:", len(normalized_dups))Проверка: число дубликатов после нормализации обычно больше, чем до неё, — потому что раскрылись совпадения, спрятанные за регистром и пробелами. Если в столбце есть пустые ячейки (NaN), .str-методы вернут по ним NaN, и это нормально.
Больше половины «ненайденных» дубликатов в реальных выгрузках — это не разные люди, а один и тот же email с разным регистром и лишним пробелом. Нормализация до сравнения важнее, чем сам метод поиска.
Шаг 6. Сохраните результат
# Финальная очистка: нормализовали, убрали дубли по email, сохранили
df_final = df.drop_duplicates(subset="email")
# index=False — чтобы pandas не дописывал колонку с номерами строк
df_final.to_csv("data_clean.csv", index=False)
print("Сохранено строк:", len(df_final))Проверка: рядом со скриптом появится файл data_clean.csv. Откройте его — повторяющихся email там быть не должно.
Если не получилось
FileNotFoundError: data.csv— Python ищет файл в текущей рабочей папке, а не рядом со скриптом. Укажите полный путь или запускайте скрипт из той же папки, где лежит CSV.UnicodeDecodeErrorпри чтении — файл в другой кодировке. Для русских выгрузок из 1С или Excel часто помогаетpd.read_csv("data.csv", encoding="cp1251").KeyError: 'email'— такой колонки нет. Проверьте точное написание:print(df.columns.tolist()). Часто в имени затесался лишний пробел, например'email '.AttributeError: Can only use .str accessor with string values— вы применяете.str.lower()к числовой колонке. Строковые операции работают только с текстом.- Дубликаты остались после
drop_duplicates— либо забыли присвоить результат в переменную, либо строки различаются невидимыми символами (табуляция, неразрывный пробел). Нормализуйте столбец, как в Шаге 5.
Что делать дальше
Скрипт находит и удаляет дубликаты, но не решает, какую из двух записей оставить. Если у повторов различаются другие поля (одна запись полнее, другая свежее), нужна логика слияния, а не простое удаление — смотрите в сторону groupby с агрегацией. Отдельная большая тема — нечёткое сравнение, когда «Иван Петров» и «Петров Иван» это один человек: для неё существуют библиотеки вроде rapidfuzz, но это уже другой инструмент. Здесь мы разобрали только точные и нормализованные совпадения.
Антиблок-аналитик 115-ФЗ: Анализ выписок, поиск рисков блокировки счёта, инструкции по безопасным транзакциям.
Спросить за 41 ₽Источники: pandas.DataFrame.drop_duplicates — официальная документация, pandas.DataFrame.duplicated — официальная документация
Частые вопросы
А если у меня pandas 1.x, а не 2.x?
Файл слишком большой, скрипт съедает всю память. Что делать?
Как посчитать, сколько раз встречается каждое значение, а не просто найти дубли?
drop_duplicates удалил не ту строку из пары. Как выбрать, какую оставить?
Как искать дубликаты сразу в двух столбцах, но независимо?
Работает ли это с Excel-файлами?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.