Перейти к содержанию

Файловые форматы

Зачем это DE?

Выбор формата данных — одно из первых решений при проектировании пайплайна. CSV на 100 млн строк = 10 GB и 5 минут чтения. Parquet на те же данные = 1 GB и 5 секунд чтения нужных столбцов.


Обзор форматов

Формат Тип Сжатие Схема Чтение столбцов Размер (100M строк)
CSV Строковый ❌ (внешнее) ❌ целиком 10 GB
JSON Lines Строковый ❌ целиком 15 GB
Parquet Колоночный ✅ Snappy/ZSTD ✅ встроена ✅ нужные столбцы 1 GB
Avro Строковый ✅ Snappy/Deflate ✅ встроена 3 GB
ORC Колоночный ✅ ZLIB/Snappy 1.2 GB

CSV

Плюсы: человекочитаемый, универсальный, поддерживается всем. Минусы: нет схемы, нет типов, нет сжатия, медленный.

Python
import pandas as pd

# Чтение
df = pd.read_csv("orders.csv", 
                  dtype={"order_id": int, "amount": float},  # типы вручную!
                  parse_dates=["order_date"])

# Запись
df.to_csv("output.csv", index=False)

# Чанками (для больших файлов)
for chunk in pd.read_csv("huge.csv", chunksize=100_000):
    process(chunk)

CSV — только для обмена

Используй CSV для импорта/экспорта между системами. Для хранения и аналитики — Parquet.


Parquet

Колоночный формат. Стандарт для Data Lake и аналитики.

Почему быстрее CSV

Text Only
CSV (строковое):                    Parquet (колоночное):
┌──────┬─────┬────────┬────┐       ┌──────────────────┐
│ id   │ name│ amount │ dt │       │ id: 1,2,3,...    │ ← row group 1
├──────┼─────┼────────┼────┤       │ name: A,B,C,...  │
│ 1    │ A   │ 100    │ .. │       │ amount: 100,200  │ ← только этот столбец
│ 2    │ B   │ 200    │ .. │       │ dt: ...          │
│ ...  │     │        │    │       └──────────────────┘
                                   ┌──────────────────┐
SELECT SUM(amount):                │ id: ...          │ ← row group 2
→ читает ВСЕ 4 столбца            │ amount: 300,400  │ ← и этот
→ 10 GB I/O                        └──────────────────┘
                                   SELECT SUM(amount):
                                   → читает ТОЛЬКО amount
                                   → 1 GB I/O (сжатый)

Чтение и запись

Python
import pandas as pd

# Запись
df.to_parquet("orders.parquet", engine="pyarrow", compression="snappy")

# Чтение
df = pd.read_parquet("orders.parquet")

# Чтение ТОЛЬКО нужных столбцов (быстро!)
df = pd.read_parquet("orders.parquet", columns=["order_id", "amount"])

pyarrow — для больших данных

Python
import pyarrow.parquet as pq

# Метаданные без чтения данных
meta = pq.read_metadata("orders.parquet")
print(meta.num_rows, meta.num_columns, meta.row_group(0).num_rows)

# Чтение с фильтрацией (predicate pushdown)
table = pq.read_table("orders.parquet",
                       columns=["order_id", "amount"],
                       filters=[("amount", ">", 1000)])

# Запись с партиционированием
pq.write_to_dataset(
    table,
    root_path="output/",
    partition_cols=["year", "month"]
)

Сжатие

Алгоритм Степень сжатия Скорость Когда
Snappy Средняя (4-5x) Быстрый По умолчанию, баланс
ZSTD Высокая (6-8x) Средняя Долгое хранение
GZIP Высокая (5-7x) Медленная Совместимость
LZ4 Низкая (3-4x) Очень быстрый Real-time

Avro

Строковый формат со встроенной схемой. Стандарт для Kafka.

Python
import fastavro

# Схема
schema = {
    "type": "record",
    "name": "Order",
    "fields": [
        {"name": "order_id", "type": "int"},
        {"name": "customer", "type": "string"},
        {"name": "amount", "type": "double"}
    ]
}

# Запись
records = [
    {"order_id": 1, "customer": "Alice", "amount": 100.0},
    {"order_id": 2, "customer": "Bob", "amount": 200.0}
]
with open("orders.avro", "wb") as f:
    fastavro.writer(f, schema, records)

# Чтение
with open("orders.avro", "rb") as f:
    reader = fastavro.reader(f)
    for record in reader:
        print(record)

Avro vs Parquet

Критерий Avro Parquet
Запись ✅ Быстрая (append) ❌ Медленнее (сортировка)
Чтение аналитики ❌ Полное сканирование ✅ Только нужные столбцы
Потоковая обработка ✅ Идеален (Kafka) ❌ Не для потоков
Schema evolution ✅ Встроена ✅ Встроена
Сжатие Хорошее Отличное

Правило: Avro для записи/потоков, Parquet для чтения/аналитики.


DuckDB — аналитика на файлах

DuckDB — встраиваемая аналитическая СУБД. SQL прямо по файлам:

Python
import duckdb

# SQL по Parquet
result = duckdb.sql("""
    SELECT customer_id, SUM(amount) AS total
    FROM 'orders/*.parquet'
    WHERE order_date >= '2025-01-01'
    GROUP BY customer_id
    ORDER BY total DESC
    LIMIT 10
""").df()

# SQL по CSV
duckdb.sql("SELECT * FROM 'data.csv' LIMIT 5").show()

# Конвертация CSV → Parquet
duckdb.sql("""
    COPY (SELECT * FROM 'huge.csv')
    TO 'output.parquet' (FORMAT PARQUET, COMPRESSION ZSTD)
""")

Что запомнить

Формат Лучший для Не подходит для
CSV Обмен между системами, экспорт Аналитика, хранение
Parquet Data Lake, аналитика, DWH Потоковая запись
Avro Kafka, потоковая запись Аналитические запросы
JSON Lines Логи, API-ответы Большие объёмы

Проверь себя


Источники