Как объединить несколько CSV файлов в один: 4 способа с дедупликацией
У вас пять файлов с телефонными номерами из разных источников: выгрузка из CRM, экспорт из рекламного кабинета, база с прошлой рассылки, данные из партнёрской программы и список с конференции. Общий размер — 350 тысяч контактов. Задача: объединить всё в один файл, удалив дубликаты.
Объединение CSV-файлов — частая операция при работе с данными. В этой статье разберём четыре способа: от примитивного копирования до специализированных инструментов с автоматической дедупликацией. Каждый метод протестирован на реальных данных с замером времени и оценкой качества результата.
Когда нужно объединять CSV-файлы
Типичные сценарии из практики:
- Сбор данных из нескольких источников — вы работаете с разными рекламными платформами, и каждая выгружает свои лиды в отдельный файл
- Объединение разбитых баз — ранее вы разделили большой файл на части для обработки, теперь нужно собрать обратно
- Консолидация отчётов — у вас есть ежедневные или еженедельные выгрузки, которые нужно свести в один файл
- Создание единой базы клиентов — данные хранятся в разных отделах, нужно создать общий справочник
- Подготовка к импорту — сервис принимает только один файл, а у вас данные в нескольких
Главная проблема при объединении — дубликаты. Если просто склеить файлы, один и тот же номер может встретиться 3–5 раз. Результат: переплата за рассылку, повторные звонки клиентам, потеря репутации. Разберём, как этого избежать.
Ручное копирование
Самый простой, но наименее эффективный подход — открыть все файлы и скопировать содержимое в один.
Как это делается
- Создайте новый файл
merged.csv - Откройте первый файл в текстовом редакторе или Excel
- Скопируйте всё содержимое (Ctrl+A, Ctrl+C)
- Вставьте в
merged.csv - Повторите для остальных файлов
- Сохраните результат
Проблемы этого подхода
| Проблема | Последствия |
|---|---|
| Дубликаты остаются | Переплата за рассылку, повторные обращения к клиентам |
| Заголовки CSV повторяются | Строка с названиями колонок попадает в данные |
| Долго и скучно | На 10 файлов уходит 15–20 минут |
| Высокий риск ошибки | Можно пропустить файл или скопировать дважды |
Пример: вы объединили пять файлов по 50 тысяч номеров. В итоговом файле оказалось 250 тысяч строк. Но реальных уникальных номеров может быть всего 180 тысяч — остальное дубли. Если вы загрузите эту базу в сервис рассылок, заплатите за 70 тысяч лишних контактов.
Вердикт: подходит только для 2–3 маленьких файлов, когда вы уверены, что дублей нет. Для остальных случаев — неэффективно и опасно.
Командная строка
В Unix-системах (Linux, macOS) объединение файлов делается одной командой. В Windows есть аналогичные инструменты.
Команда cat (Linux/macOS)
# Простое объединение без дедупликации
cat файл1.csv файл2.csv файл3.csv > merged.csv
# Или все CSV файлы в папке
cat *.csv > merged.csv
# Убрать заголовки из всех файлов кроме первого
cat файл1.csv > merged.csv
tail -n +2 файл2.csv >> merged.csv
tail -n +2 файл3.csv >> merged.csv
Параметры:
cat— утилита для вывода и объединения файлов>— перенаправление вывода в файл (перезаписывает)>>— дописывает в конец файлаtail -n +2— пропускает первую строку (заголовок)
PowerShell и CMD (Windows)
# PowerShell: объединение файлов
Get-Content файл1.csv, файл2.csv, файл3.csv | Set-Content merged.csv
# Или все CSV в папке
Get-Content *.csv | Set-Content merged.csv
# CMD: более примитивный способ
copy файл1.csv + файл2.csv + файл3.csv merged.csv
Дедупликация через командную строку
После объединения можно удалить дубликаты с помощью sort и uniq:
# Unix: объединить и удалить дубли
cat файл1.csv файл2.csv файл3.csv | sort -u > merged_unique.csv
# Или в два этапа
cat *.csv > merged.csv
sort -u merged.csv > merged_unique.csv
Важно: sort -u сортирует строки, поэтому порядок данных изменится. Если порядок важен, используйте другие инструменты.
Производительность
Тест на пяти файлах по 100 тысяч строк (суммарно 500 тысяч, после дедупликации — 380 тысяч):
| Метод | Время | Дубли удалены |
|---|---|---|
cat (без дедупликации) |
1,2 секунды | Нет |
cat + sort -u |
8,5 секунды | Да |
| PowerShell Get-Content | 15 секунд | Нет |
Проблема: эти методы работают на уровне строк, не понимая содержимое. Если номера в разных файлах записаны в разных форматах (+79001234567 и 89001234567), они не будут считаться дубликатами.
Python с библиотекой pandas
Для более интеллектуальной обработки можно использовать Python. Библиотека pandas предоставляет удобные инструменты для работы с CSV.
Простое объединение с дедупликацией
import pandas as pd
# Список файлов для объединения
files = ['файл1.csv', 'файл2.csv', 'файл3.csv']
# Читаем все файлы
dfs = [pd.read_csv(f) for f in files]
# Объединяем
merged = pd.concat(dfs, ignore_index=True)
# Удаляем дубликаты
merged_unique = merged.drop_duplicates()
# Сохраняем результат
merged_unique.to_csv('merged.csv', index=False)
# Статистика
total = len(merged)
unique = len(merged_unique)
duplicates = total - unique
print(f'Всего строк: {total}')
print(f'Уникальных: {unique}')
print(f'Дублей: {duplicates}')
Дедупликация с нормализацией номеров
Если номера записаны в разных форматах, перед дедупликацией нужно привести их к единому виду:
import pandas as pd
import re
def normalize_phone(phone):
"""Приводит номер к формату 79XXXXXXXXX"""
if pd.isna(phone):
return None
# Убираем все символы кроме цифр
phone = re.sub(r'[^\d]', '', str(phone))
# Приводим к формату 79XXXXXXXXX
if phone.startswith('89') and len(phone) == 11:
phone = '7' + phone[1:]
elif phone.startswith('9') and len(phone) == 10:
phone = '7' + phone
elif phone.startswith('79') and len(phone) == 11:
pass # уже в нужном формате
else:
return None # невалидный номер
return phone if len(phone) == 11 else None
# Читаем файлы
files = ['файл1.csv', 'файл2.csv', 'файл3.csv']
dfs = [pd.read_csv(f, header=None, names=['phone']) for f in files]
merged = pd.concat(dfs, ignore_index=True)
# Нормализуем номера
merged['phone_normalized'] = merged['phone'].apply(normalize_phone)
# Удаляем невалидные и дублирующиеся номера
merged_clean = merged.dropna(subset=['phone_normalized'])
merged_unique = merged_clean.drop_duplicates(subset=['phone_normalized'])
# Сохраняем только нормализованные номера
merged_unique[['phone_normalized']].to_csv('merged.csv', index=False, header=False)
print(f'Исходных номеров: {len(merged)}')
print(f'Валидных после нормализации: {len(merged_clean)}')
print(f'Уникальных: {len(merged_unique)}')
print(f'Удалено дублей: {len(merged_clean) - len(merged_unique)}')
Производительность pandas
Тест на тех же пяти файлах (500 тысяч строк, с разными форматами номеров):
| Операция | Время | Использование памяти |
|---|---|---|
| Чтение файлов | 2,1 секунды | ~150 МБ |
| Объединение | 0,3 секунды | +50 МБ |
| Нормализация номеров | 4,5 секунды | +80 МБ |
| Дедупликация | 1,2 секунды | +30 МБ |
| Запись результата | 0,8 секунды | — |
| Итого | 8,9 секунды | ~310 МБ |
Когда использовать Python
Подходит, если:
- Нужна сложная логика обработки (нормализация, валидация, фильтрация)
- Файлы в разных форматах или с разной структурой колонок
- Требуется детальная статистика или логирование
- Задача повторяется регулярно — можно написать скрипт один раз
Не подходит, если:
- Файлы очень большие (больше 2 ГБ) — pandas загружает всё в память
- Задача разовая и простая — писать скрипт дольше, чем выполнить руками
- Нет опыта с Python — порог входа выше, чем у GUI-инструментов
Базальт — специализированный инструмент
Если вы регулярно работаете с телефонными базами, удобнее использовать инструмент, заточенный под эту задачу. Базальт — десктопное приложение для работы с CSV без программирования.
Как объединить файлы в Базальте
- Откройте раздел «Объединить файлы»
- Добавьте файлы для объединения (2 или больше)
- Укажите название выходного файла
- Нажмите «Объединить файлы»
- Получите CSV с уникальными номерами и статистикой
Что делает Базальт автоматически
- Парсит номера — извлекает телефоны из любого формата, игнорируя текстовый мусор
- Нормализует — приводит к единому виду (79XXXXXXXXX), независимо от исходного формата
- Дедуплицирует — использует Set для O(1) проверки уникальности
- Показывает статистику — всего номеров, уникальных, количество дублей
- Работает офлайн — данные не уходят на сторонние серверы
Почему автоматическая дедупликация важна
Базальт использует структуру данных Set для хранения номеров. Это даёт два критичных преимущества:
- O(1) сложность проверки — проверка на дубликат происходит мгновенно, даже на базах в миллионы номеров
- Гарантия уникальности — Set не допускает дубликатов по определению
Сравнение подходов:
| Метод | Сложность | Время на 1 млн номеров |
|---|---|---|
| Ручная проверка (массив + перебор) | O(n²) | ~10 минут |
| Сортировка + сравнение соседей | O(n log n) | ~8 секунд |
| Set-based (Базальт, Python) | O(n) | ~3 секунды |
Производительность Базальта
Тест на реальной задаче — пять файлов, суммарно 800 тысяч номеров, разные форматы (+7, 8, без префикса):
| Метод | Время | Клики | Дубли удалены | Форматы нормализованы |
|---|---|---|---|---|
| Ручное (copy-paste) | ~30 минут | ~40 | Нет | Нет |
cat + sort -u |
12 секунд | 0 | Частично* | Нет |
| Python + pandas | 15 секунд | 0 | Да | Да (код нужен) |
| Базальт | 8 секунд | 4 | Да | Да |
*sort -u удаляет дубли только на уровне строк. Номера +79001234567 и 89001234567 останутся как разные записи.
Преимущества Базальта
- Нет порога входа — не нужно знать командную строку или программирование
- Автоматическая нормализация — разные форматы номеров приводятся к единому
- Статистика в реальном времени — видите, сколько дублей найдено
- Офлайн-обработка — конфиденциальные данные остаются на вашем компьютере
- История операций — можно вернуться к предыдущим файлам
Работа с файлами разной структуры
Частая проблема — файлы с разным количеством колонок или разным порядком данных.
Пример проблемы
Файл 1:
Имя,Телефон,Email
Иван,+79001234567,ivan@example.com
Файл 2:
Телефон,Город
89002345678,Москва
Если просто склеить эти файлы, структура сломается:
Имя,Телефон,Email
Иван,+79001234567,ivan@example.com
Телефон,Город
89002345678,Москва
Решения
Вариант 1: Извлекайте только нужную колонку перед объединением
import pandas as pd
# Читаем только колонку с телефонами
df1 = pd.read_csv('файл1.csv', usecols=['Телефон'])
df2 = pd.read_csv('файл2.csv', usecols=['Телефон'])
# Объединяем
merged = pd.concat([df1, df2], ignore_index=True)
Вариант 2: Используйте инструменты, которые парсят номера из любой структуры (например, Базальт) — они игнорируют заголовки и другие колонки, извлекая только номера.
Практические советы
- Всегда проверяйте количество строк до и после объединения. Формула:
сумма строк всех файлов минус количество дублей = итоговое количество. - Сохраняйте исходные файлы — не удаляйте их сразу после объединения, могут понадобиться для проверки.
- Используйте информативные названия —
merged_2024-02-06_5files.csvпонятнее, чемmerged.csv. - Документируйте источники — записывайте, какие файлы объединили, в отдельный текстовый файл.
- Проверяйте формат номеров — если в итоговом файле есть номера с разными префиксами (+7, 8, 7), возможно, дедупликация сработала неправильно.
- Учитывайте размер файлов — если суммарный размер больше 2 ГБ, Python и Excel не подойдут, используйте командную строку или специализированные инструменты.
Что выбрать для разных задач
Разовая задача, 2–3 маленьких файла без дублей — ручное копирование. Быстрее, чем разбираться с инструментами.
Работаете в Linux/macOS, файлы большие и чистые — используйте cat и sort -u. Это самый быстрый способ для стандартных CSV.
Нужна сложная логика или специфическая обработка — Python с pandas. Полный контроль над процессом, возможность добавить валидацию и трансформации.
Регулярная работа с телефонными базами, разные форматы номеров — используйте Базальт. Автоматическая нормализация, дедупликация через Set, удобный интерфейс, работает офлайн.
Ключевое отличие специализированных инструментов типа Базальта — они не просто склеивают строки, а понимают содержимое. Номера +79001234567, 8 (900) 123-45-67 и 79001234567 будут распознаны как один и тот же контакт. Это экономит деньги на рассылках и сохраняет репутацию — клиенты не получат по три одинаковых письма.