Файловые форматы¶
Зачем это 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-ответы | Большие объёмы |