Ecosystem
Arrow как lingua franca
Apache Arrow — это не одна библиотека. Это стандарт — спецификация колоночного формата в памяти, который реализуют десятки проектов. Каждый из них может обмениваться данными с любым другим без сериализации — достаточно передать указатель на Arrow-буферы.
Это создаёт сетевой эффект: чем больше систем понимают Arrow, тем ценнее каждая из них. Python-скрипт → DuckDB → Polars → ML-модель → визуализация — все говорят на Arrow, ни одной конвертации.
pandas
pandas: через .to_arrow() / pa.Table.from_pandas(). С pandas 2.0+ Arrow-backed Series — данные хранятся в Arrow-формате нативно, без копирования.Polars
Polars: нативно построен на Arrow (Rust arrow-rs). DataFrame = набор Arrow-массивов. Нет конвертации — данные уже в Arrow.DuckDB
DuckDB: внутренний формат основан на Arrow (с расширениями). Экспорт в Arrow через .arrow() / .fetch_arrow_table() — zero-copy для большинства типов.DataFusion
DataFusion: Apache Arrow-native SQL движок на Rust. Использует arrow-rs напрямую. Встраиваемый — основа для InfluxDB 3, Comet, Arroyo, Ballista.C Data Interface: zero-copy FFI
Главная техническая инновация Arrow-экосистемы — Arrow C Data Interface. Это набор из двух C-структур (ArrowSchema + ArrowArray), позволяющих передавать Arrow-данные между библиотеками без копирования и без общей зависимости на одну реализацию Arrow.
Проблема до C Data Interface: Python-приложение использует pyarrow (C++), Rust-библиотека — arrow-rs, Java-процесс — Arrow Java. Чтобы передать данные между ними, нужна IPC-сериализация — копирование.
С C Data Interface: любая библиотека заполняет два C-struct и передаёт указатели через FFI. Получатель интерпретирует буферы без копирования.
общей библиотеки, нет сериализации, нет копирования. Только C ABI.
ArrowSchema
ArrowSchema: format string ('+s' для struct, 'l' для int64), name, metadata, children, dictionary. Рекурсивная структура — описывает любой Arrow-тип.ArrowArray
ArrowArray: length, null_count, offset, n_buffers, buffers (указатели), children, dictionary. Буферы принадлежат producer — consumer не освобождает их.ArrowArrayStream
ArrowArrayStream: get_schema() + get_next() + get_last_error() + release(). Потоковая передача: consumer вызывает get_next() пока не получит NULL (конец потока).C Data Interface — это ABI, не API. Два C-struct с фиксированной раскладкой полей. Любой язык с FFI (Python ctypes, Rust extern “C”, Go cgo, Julia ccall) может их заполнять и читать. Никакой линковки с Arrow-библиотекой не нужно.
nanoarrow: Arrow без тяжёлой зависимости
Полная реализация Arrow (C++, Java) — это мегабайты кода. Для встраиваемых систем, CLI-утилит, и IoT это overhead. nanoarrow — легковесная реализация Arrow C Data Interface:
- 2 файла:
nanoarrow.c+nanoarrow.h— можно скопировать в проект - ~100 KB скомпилированного кода
- Нет зависимостей — даже stdlib опционален
- Поддерживает все Arrow-типы, включая nested (List, Struct, Map)
- Bindings: Python (
pip install nanoarrow), R (install.packages("nanoarrow"))
Типичные сценарии:
- Встраивание Arrow в C/C++ приложение без линковки с libarrow (~50 MB)
- Конвертация CSV/JSON → Arrow streams в embedded-системах
- Использование как зависимости в ADBC-драйверах (нативные драйверы PostgreSQL и SQLite ADBC используют nanoarrow)
import nanoarrow as na
# Создание Arrow-массива через nanoarrow
array = na.Array([1, 2, None, 4], na.int32())
print(array) # nanoarrow.Array<int32>[4]
# Совместимость с pyarrow через PyCapsule protocol
import pyarrow as pa
pa_array = pa.array(array) # zero-copy через C Data Interface
Arrow-native движки
Arrow породил новое поколение аналитических движков, которые хранят данные в Arrow-формате нативно, без конвертации:
DataFusion заслуживает отдельного внимания: это не просто движок, а конструктор движков. Проекты берут DataFusion как основу и добавляют свой storage layer:
| Проект | Поверх DataFusion | Назначение |
|---|---|---|
| InfluxDB 3 | Arrow Flight + custom storage | Time-series database |
| Comet | JNI bridge to Spark | Spark query accelerator |
| Arroyo | Custom streaming runtime | Stream processing |
| Ballista | Distributed scheduler | Distributed SQL |
| delta-rs | Delta Lake reader | Lakehouse |
| lance | Lance format reader | Vector database |
Языковые реализации
Arrow — multi-language стандарт. Каждая реализация совместима с другими через IPC или C Data Interface:
| Язык | Библиотека | Особенности |
|---|---|---|
| C++ | Apache Arrow C++ | Каноническая реализация. Compute kernels, IPC, Parquet, CSV, Flight |
| Rust | arrow-rs (arrow crate) | Независимая реализация. Основа DataFusion, Polars, delta-rs |
| Java | Arrow Java | JNI bridge к C++. Используется в Spark, Flink, Trino |
| Python | pyarrow | Bindings к C++ через Cython. pandas/NumPy интеграция |
| Go | apache-arrow-go | Pure Go. ADBC, Flight, Parquet |
| JavaScript | apache-arrow (npm) | TypeScript. Observable, Perspective, deck.gl |
| C# | Apache.Arrow (NuGet) | .NET. Используется в ML.NET, Power BI connectors |
| Ruby | red-arrow (gem) | Bindings к C++ через GObject Introspection |
Rust-реализация (arrow-rs) и C++-реализация — независимые кодовые базы. Они совместимы по формату (IPC, C Data Interface), но не делят код. Это design decision: Rust-экосистема (DataFusion, Polars, delta-rs) развивается быстрее с собственной реализацией.
Интеграция: pandas, NumPy, ML
pandas 2.0+ Arrow backend
С pandas 2.0 появился Arrow-backed dtype — Series хранит данные в Arrow-формате:
import pandas as pd
import pyarrow as pa
# Arrow-backed Series — нет конвертации при создании
s = pd.Series(["Alice", "Bob", None, "Diana"],
dtype="string[pyarrow]")
# Или целый DataFrame
df = pd.DataFrame({
"name": pd.array(["Alice", "Bob"], dtype="string[pyarrow]"),
"score": pd.array([95.5, 87.3], dtype="float64[pyarrow]"),
})
# Конвертация в pyarrow Table — zero-copy
table = pa.Table.from_pandas(df)
NumPy zero-copy
Для примитивных типов (int, float) Arrow → NumPy zero-copy:
import pyarrow as pa
import numpy as np
arr = pa.array([1.0, 2.0, 3.0, 4.0, 5.0])
np_arr = arr.to_numpy(zero_copy_only=True) # без копирования!
# np_arr.base указывает на Arrow-буфер
# Изменения в np_arr не влияют на arr (Arrow immutable)
Zero-copy работает только для non-null примитивных типов. Если массив содержит NULL — Arrow хранит validity bitmap отдельно, а NumPy не поддерживает bitmap nulls → нужна копия с заменой NULL на NaN.
ML frameworks
import pyarrow as pa
import torch
# Arrow → PyTorch через NumPy bridge
arr = pa.array([1.0, 2.0, 3.0, 4.0])
tensor = torch.from_numpy(arr.to_numpy(zero_copy_only=True))
# tensor разделяет память с Arrow-буфером
Arrow PyCapsule Protocol
С Arrow 14.0 (2023) появился PyCapsule protocol — стандарт передачи Arrow-данных между Python-библиотеками без зависимости на pyarrow:
# Любой объект с __arrow_c_array__ / __arrow_c_stream__
# автоматически совместим с pyarrow, polars, DuckDB, nanoarrow
import polars as pl
import pyarrow as pa
# Polars Series → pyarrow Array (через PyCapsule, без pyarrow import в Polars)
series = pl.Series("x", [1, 2, 3])
pa_array = pa.array(series) # uses __arrow_c_stream__ internally
Это позволяет библиотекам обмениваться Arrow-данными без общей зависимости на pyarrow — каждая реализует PyCapsule protocol через C Data Interface.
Ключевые выводы
- Arrow как lingua franca — единый формат в памяти для десятков систем. pandas → DuckDB → Polars → ML — без конвертации
- C Data Interface — два C-struct (ArrowSchema + ArrowArray) для zero-copy FFI. Не нужна общая библиотека, только C ABI
- nanoarrow — lightweight Arrow (~100 KB, 2 файла). Для embedded, CLI, ADBC-драйверов
- Arrow-native движки — DataFusion (ASF top-level, конструктор движков), DuckDB (embedded OLAP), Velox (Meta), Polars (DataFrame)
- Языковые реализации — C++, Rust (arrow-rs, независимая), Java, Python, Go, JS, C#, Ruby. Совместимы через IPC + C Data Interface
- pandas 2.0+ — Arrow-backed dtype:
string[pyarrow],float64[pyarrow]. Zero-copy конвертация в Arrow Table - PyCapsule protocol (Arrow 14.0+) — стандарт обмена Arrow-данными между Python-библиотеками без зависимости на pyarrow