Learning Platform
Глоссарий Troubleshooting
Урок 08.04 · 25 мин
Продвинутый
Apache ArrowFeatherIPCParquetpyarrowColumnar FormatCompression

Feather Format

История: зачем Feather

В 2016 году Wes McKinney (создатель pandas) и Hadley Wickham (автор tidyverse в R) столкнулись с одной проблемой: передача DataFrame между Python и R требовала CSV-сериализации — медленной, с потерей типов, и неуправляемой для больших данных. Они создали Feather — бинарный формат для быстрого обмена табличными данными между языками.

Feather v1 использовал собственную FlatBuffers-схему для метаданных, но хранил данные в Arrow-колоночном формате. Это был proof of concept — быстро, но ограниченно.

NOTE

Feather — это не конкурент Parquet. Feather оптимизирован для скорости чтения/записи (временное хранение, кэш, обмен между процессами). Parquet — для компактного долговременного хранения с rich metadata и predicate pushdown.

Feather v1 vs v2

Эволюция Feather: v1 → v2

Feather v1 (2016)

Feather v1 (2016): собственная FlatBuffers-схема, ограниченный набор типов, без компрессии. Максимум 2 ГБ на колонку из-за int32 offsets.
• Собственная FlatBuffers-схема
• Ограниченные типы
• Без компрессии
• Лимит 2 ГБ / колонка
• Python + R только

Feather v2 (Arrow 0.17+)

Feather v2 (Arrow 0.17, 2020): идентичен Arrow IPC File Format. Поддерживает все Arrow-типы, LZ4/ZSTD компрессию, произвольный chunk size. Лимит на колонку снят.
• = Arrow IPC File Format
• Все Arrow-типы
• LZ4 / ZSTD компрессия
• Нет лимита на размер
• Любой Arrow-клиент
ИтогС версии 0.17.0 Feather v2 — это просто Arrow IPC File Format с удобным API. Название 'Feather' сохранено для обратной совместимости.

Ключевое решение Arrow-команды: вместо развития собственного формата Feather v1, они сделали v2 идентичным Arrow IPC File Format. Это означает:

  • Файл .feather (v2) — это валидный .arrow файл
  • Любой Arrow-клиент (C++, Java, Rust, Go) может читать Feather v2 без специальной библиотеки
  • Обратное тоже верно: файл, записанный через pyarrow.ipc.new_file(), читается через feather.read_feather()

Структура файла на диске

Feather v2 = Arrow IPC File Format
ARROW1 (magic, 6 bytes)Magic bytes 'ARROW1' (6 байт) — маркер начала файла. Позволяет быстро определить формат без чтения метаданных.

Padding (2 bytes)

Padding до 8 байт после magic bytes. Обеспечивает выравнивание FlatBuffers Schema message.
Schema MessageFlatBuffers-encoded Arrow Schema: список полей с типами, custom metadata. Определяет структуру всех RecordBatch в файле.
DictionaryBatch × NСловари для Dictionary-encoded колонок. Передаются перед RecordBatch, на которые ссылаются. Каждый DictionaryBatch содержит id и данные словаря.
RecordBatch × MОсновные данные. Каждый batch — chunk данных с validity bitmaps и буферами. Размер chunk по умолчанию 64K строк (configurable через chunksize).
FooterFooter содержит копию Schema и массив Block descriptors — offset/metadataLength/bodyLength для каждого RecordBatch. Это обеспечивает O(1) random access к любому batch.
ARROW1 (magic, 6 bytes)Magic bytes 'ARROW1' (6 байт) в конце файла. Ридер проверяет совпадение с начальным magic при открытии.

Благодаря Footer с Block descriptors, Feather v2 поддерживает random access: можно прочитать batch #42 без сканирования предыдущих 41 — seek по offset из footer.

Компрессия

Feather v2 поддерживает покадровую (per-buffer) компрессию:

КодекХарактеристикаКогда использовать
LZ4 (по умолчанию)Быстрая декомпрессия, умеренное сжатиеКэш, промежуточные файлы, IPC
ZSTDЛучшее сжатие, медленнее LZ4Архивация, сетевая передача
uncompressedМаксимальная скорость чтенияmmap, zero-copy сценарии
WARNING

Компрессия в Feather/IPC — это trade-off: сжатые буферы нельзя использовать zero-copy, потому что данные нужно декомпрессировать в новый буфер. Если latency чтения критична и диск быстрый — пишите без компрессии.

pyarrow.feather API

Запись:

import pyarrow as pa
import pyarrow.feather as feather

table = pa.table({
 "user_id": pa.array([1, 2, 3, 4, 5]),
 "name": pa.array(["Alice", "Bob", "Charlie", "Diana", "Eve"]),
 "score": pa.array([95.5, 87.3, None, 91.0, 78.8]),
})

# LZ4 по умолчанию
feather.write_feather(table, "users.feather")

# ZSTD с уровнем 3
feather.write_feather(table, "users_zstd.feather",
 compression="zstd",
 compression_level=3)

# Без компрессии — для mmap
feather.write_feather(table, "users_raw.feather",
 compression="uncompressed")

# Feather v1 (legacy, для совместимости со старыми ридерами)
feather.write_feather(table, "users_v1.feather", version=1)

Чтение:

# Как pandas DataFrame
df = feather.read_feather("users.feather")

# Как pyarrow Table (без конвертации в pandas)
table = feather.read_table("users.feather")

# Выборочное чтение колонок
df_names = feather.read_feather("users.feather",
 columns=["user_id", "name"])

# Memory-mapped чтение (zero-copy если без компрессии)
table_mm = feather.read_table("users_raw.feather",
 memory_map=True)
TIP

read_table() возвращает pyarrow.Table — Arrow-нативный объект. read_feather()pandas.DataFrame. Если дальше данные идут в Arrow-совместимый движок (DuckDB, Polars), используйте read_table() чтобы избежать лишней конвертации.

Feather vs Parquet: когда что

Feather vs Parquet: сценарии применения

Feather

Feather оптимален когда важна скорость I/O. Данные хранятся в Arrow-формате — минимум трансформаций при чтении/записи.
Да Кэш между этапами пайплайна
Да Обмен Python ↔ R ↔ Julia
Да Промежуточные файлы в ETL
Да Локальный анализ (ноутбук)
Нет Нет predicate pushdown
Нет Слабое сжатие vs Parquet

Parquet

Parquet оптимален для долговременного хранения. Мощные кодировки (RLE, DICT, DELTA), rich metadata, row group pruning, Bloom-фильтры.
Да Data lake (S3, HDFS, GCS)
Да Predicate pushdown
Да Лучшее сжатие (encoding + codec)
Да Schema evolution
Нет Медленнее запись (encoding)
Нет Decode overhead при чтении

Правило: Feather — для скорости, Parquet — для хранения.

Числа (приблизительные, зависят от данных и железа):

  • Запись: Feather 2-5× быстрее Parquet (нет encoding stage)
  • Чтение: Feather 1.5-3× быстрее Parquet (нет decode stage)
  • Размер: Parquet 2-10× компактнее (RLE + dictionary + delta encoding перед компрессией)
import pyarrow as pa
import pyarrow.feather as feather
import pyarrow.parquet as pq

table = pa.table({"x": range(1_000_000), "y": ["cat", "dog"] * 500_000})

# Feather: быстро, но больше
feather.write_feather(table, "data.feather") # ~12 MB, 50ms

# Parquet: медленнее, но компактнее
pq.write_table(table, "data.parquet") # ~2 MB, 200ms

DuckDB и Polars с Feather

Feather v2 — first-class citizen в Arrow-нативных движках:

import duckdb

# DuckDB читает Feather напрямую через Arrow
result = duckdb.sql("""
 SELECT name, AVG(score) as avg_score
 FROM read_arrow('users.feather')
 GROUP BY name
""").fetchdf()
import polars as pl

# Polars: scan_ipc для lazy evaluation
lf = pl.scan_ipc("users.feather")
result = lf.filter(pl.col("score") > 80).collect()
NOTE

Polars использует scan_ipc() / read_ipc() — потому что Feather v2 = IPC File. Название функции отражает реальный формат, а не историческое имя.

Ключевые выводы

  1. Feather v1 (2016) — proof of concept от McKinney и Wickham: собственная схема, ограниченные типы, без компрессии, лимит 2 ГБ/колонка
  2. Feather v2 (Arrow 0.17, 2020) — идентичен Arrow IPC File Format. Все Arrow-типы, LZ4/ZSTD компрессия, нет лимитов
  3. Структура — ARROW1 magic + Schema + DictionaryBatch + RecordBatch + Footer + ARROW1. Random access через Block descriptors
  4. Компрессия — LZ4 (по умолчанию), ZSTD, uncompressed. Компрессия убивает zero-copy — trade-off скорость/размер
  5. Feather vs Parquet — Feather для скорости (кэш, IPC, обмен), Parquet для хранения (encoding, pushdown, компактность)
  6. APIfeather.write_feather() / feather.read_table(). DuckDB и Polars читают Feather нативно как Arrow IPC

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Feather v2 файл и Arrow IPC File — в чём разница на уровне бинарного формата?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7