Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Файлы данных в pandas: CSV, Parquet и выбор формата

CSV, Parquet, JSONL: выбор формата файлов данных для pandas

Как хранить и читать данные в pandas-пайплайнах: проблемы CSV (типы, кодировки, разделители), преимущества Parquet, JSONL для потоков, когда нужен DuckDB — практические правила.

Прямой ответ

Правило 2026 года: CSV — для обмена с людьми и внешними системами, Parquet — для всего остального (промежуточные данные пайплайна, датасеты, кеш выгрузок). Parquet хранит типы (даты остаются датами, числа — числами), сжимает данные в разы, читается быстрее CSV (особенно выборочно — по колонкам и строковым группам) и не страдает от «1 234,56», «01/02/2026» и «Excel открыл и пересохранил». CSV остаётся там, где его ждут: выгрузки для коллег, импорты из чужих систем, «открыть глазами». Внутренний формат пайплайна — parquet; поток записей/логи — JSONL; запросы к файлам без загрузки — DuckDB (отдельная статья — вот).

CSV: почему он продолжает ломаться

CSV — не формат, а соглашение, и каждое расхождение — потенциальный баг. Типовой набор проблем: типы не хранятся (даты, ведущие нули, «да/нет» — при каждом чтении парсятся заново, и 00123 становится 123); разделители (запятая/точка с запятой/таб — sep= угадывается не всегда); десятичные разделители (европейская «запятая» — decimal=","); кодировки (cp1251 из Excel против UTF-8 — UnicodeDecodeError или кракозябры; системный разбор — в статье про Unicode и кодировки); кавычки и переводы строк внутри полей (наивные парсеры ломаются, pandas справляется при корректном quoting). Практика чтения чужого CSV: сначала read_csv(..., nrows=100, dtype=str) — посмотреть на данные «как есть», затем явные dtype=, parse_dates=, decimal=, encoding= — и зафиксировать параметры в коде пайплайна, а не подбирать каждый раз.

Parquet: почему он дефолт

Parquet — колоночный формат со схемой: типы вшиты в файл (чтение не «угадывает»), сжатие (snappy/zstd — типично 3–10× компактнее CSV), колоночная организация (чтение двух колонок из ста не трогает остальные — columns=[...]), предикат-пушдаун через статистику строковых групп (фильтр по дате пропускает ненужные блоки файла). Из pandas: df.to_parquet("data.parquet", compression="zstd") / pd.read_parquet(...) (движок pyarrow). Нюансы: схема требует консистентных типов (смешанные колонки — привести до записи; очистка — по паттернам); «файл глазами не открыть» (нужен инструмент — duckdb-CLI/Python читают parquet без pandas); партиционирование по директориям (dataset с partition_cols) — для датасетов с естественным ключом (дата/регион) даёт пропуск целых директорий при фильтрации.

JSONL и остальные форматы

JSONL (JSON Lines, строка на запись) — ниша потоков и логов: дописывается атомарно построчно, читается без загрузки файла (chunksize/построчно), поля могут различаться между записями; цена — объем (текст, повтор ключей) и скорость. pd.read_json(..., lines=True). Excel (xlsx) — только обмен с людьми: медленный, типонебезопасный (даты/числа живут по своим правилам), лимиты строк; для чтения чужого — read_excel с явным dtype=. Feather/Arrow IPC — максимальная скорость чтения/записи при внутреннем обмене между процессами (без сжатия тяжелее parquet). SQLite — когда нужен не файл, а хранилище с запросами и транзакциями (когда достаточно встроенной БД — отдельный разбор в статье про SQLite).

Практические сценарии выбора

Выгрузка из БД для анализа: сразу в parquet (или CSV, если анализ одноразовый и файл уйдёт коллеге). Промежуточный результат пайплайна (очищенные данные): parquet + имя с версией/датой — повторный запуск не перечитывает источник. Кеш медленного API: JSONL-дописывание (потоковость) или parquet-партиции по дням. Датасет > памяти: parquet-партиции + чтение фильтрованных колонок в pandas, либо DuckDB поверх файлов без загрузки. Данные для фронтенда/другого языка: JSON/JSONL (parquet тоже кросс-языковой, но JSON универсальнее). Правило большого пальца: если файл читается чаще, чем пишется, и живёт дольше дня — parquet.

Гигиена файловых данных

Независимо от формата: схема задокументирована (колонки, типы, единицы — README рядом или схема в коде); версионирование (дата выгрузки в имени, «latest» — симлинк/копия, не перезапись); целостность (для критичных выгрузок — контрольная сумма и число строк в метаданных; «файл доехал не весь» — классика ночных джоб); чувствительные данные — не в общих папках и не в git (parquet бинарен и «незаметен» при ревью диффов; подходы к секретам — в статье про env и секреты). И последнее: «сырой» слой (raw, как приехало) стоит хранить отдельно от «очищенного» — перепарсить сырьё при изменении логики очистки можно, восстановить затёртый оригинал — нет.

Практический сценарий: конвейер «выгрузка → отчёт»

Соберём типовой пайплайн, в котором каждый формат на своём месте. Ночью джоба выгружает данные из рабочей БД: сырой слой сохраняется как есть — CSV или JSONL с датой в имени (это «фотография источника», её не пересохраняют в другой формат задним числом). Утренний шаг очистки читает сырьё с явными параметрами (dtype=, parse_dates=, decimal=, кодировка — всё зафиксировано в коде), приводит типы, убирает дубликаты — и кладёт результат в clean_YYYY-MM-DD.parquet со сжатием zstd: из трёхсот мегабайт CSV получается сорок мегабайт parquet, в котором даты уже даты, а числа — числа. Дальше отчётный ноутбук читает только parquet: pd.read_parquet(..., columns=[...]) подтягивает пять нужных колонок из сорока за доли секунды, тяжёлые агрегации при желании уходят в DuckDB-запрос к тому же файлу. Итоговая табличка для людей экспортируется в xlsx с форматированием — и на этом месте форматы заканчиваются. Когда через месяц выясняется, что правило очистки было неверным, пайплайн перечитывает сырой слой и пересобирает clean-паркет за минуты — сырьё не потеряно, история версий выгрузок цела. Попытка проделать то же самое на одном CSV «от начала до конца» означает повторный парсинг с угадыванием типов на каждом шаге и потерю воспроизводимости.

FAQ

Почему мой parquet больше/меньше ожидаемого? Сжатие зависит от данных: много повторяющихся значений (категории, отсортированные ключи) — сжатие отличное; случайные float — слабое. Сортировка по ключевым колонкам перед записью обычно заметно уменьшает файл (лучше работают RLE/дельта-кодировки).

Чем parquet отличается от feather? Оба на Arrow: parquet — «формат хранения» (сжатие, статистики, экосистема Big Data), feather/IPC — «формат обмена» (минимальная накладная стоимость чтения). Для долгосрочного хранения — parquet.

Можно ли читать только часть parquet-файла? Да: columns=[...] — колоночный срез; фильтры через filters= (движок пропускает строковые группы по статистикам). Это главное операционное преимущество перед CSV — читается нужное, а не всё.

Excel или CSV для отправки отчёта коллеге? CSV, если коллега откроет его программно; xlsx — если нужны листы/форматирование. И в том и в другом случае — явная кодировка (UTF-8 с BOM для Excel-совместимости кириллицы) и даты в ISO-формате.

Источники: pandas IO tools, документация Apache Arrow/Parquet — ссылки в шапке.

FAQ

Можно ли хранить пароли и токены доступа в parquet? Нет — как и в любом файловом формате данных: parquet не шифрует содержимое, а бинарность лишь скрывает его от случайного взгляда. Секреты живут в переменных окружения и специализированных хранилищах; датасеты с чувствительными полями — в защищённых директориях с контролем доступа и ограниченным сроком хранения.