Apache Arrow та PyArrow 18 у Python: повний посібник на 2026
Практичний посібник з Apache Arrow і PyArrow 18 у Python: zero-copy конверсія з pandas та Polars, робота з Parquet, Compute API, Arrow Flight RPC і ADBC.
Apache Arrow — це крос-мовний колонковий формат даних у пам'яті, який дозволяє різним інструментам (pandas, Polars, DuckDB, Spark) обмінюватися таблицями без серіалізації та копіювання. PyArrow 18 (квітень 2026), це офіційна Python-прив'язка до Arrow C++ із zero-copy конверсією в/з pandas та Polars, читанням Parquet/Feather, потоковою передачею через Flight RPC і векторизованим Compute API. У цьому посібнику ви навчитеся використовувати PyArrow як основу для аналітичних пайплайнів у 2026 році. Офіційну документацію проєкту можна знайти на сайті arrow.apache.org/docs/python.
Apache Arrow, це специфікація колонкового формату у пам'яті, що стандартизує обмін табличними даними між мовами та фреймворками.
PyArrow 18.0 (квітень 2026) повністю інтегрований з pandas 3.0 як стандартний backend і використовується Polars, DuckDB та scikit-learn 1.8.
Zero-copy конверсія pandas ↔ PyArrow ↔ Polars економить пам'ять і час, без жодних серіалізаційних накладних витрат.
Формат Parquet через pyarrow.parquet залишається стандартом для аналітичних файлів. Підтримує partition pruning, predicate pushdown і dictionary encoding.
Arrow Flight RPC у 2026 році витісняє JDBC/ODBC для високопродуктивної передачі даних між сервісами зі швидкістю до 6 ГБ/с на з'єднання.
ADBC (Arrow Database Connectivity) дає драйвери для PostgreSQL, Snowflake, BigQuery з нативною підтримкою Arrow.
Що таке Apache Arrow і навіщо він потрібен
Чесно кажучи, за останні роки я багато разів бачив один і той самий патерн: команда читає Parquet у Spark, конвертує в pandas, передає в scikit-learn, потім назад у Parquet. У результаті 70% часу йде на серіалізацію та копіювання. Apache Arrow з'явився саме для розв'язання цієї проблеми. Це специфікація колонкового формату у пам'яті, незалежна від мови програмування: один і той самий буфер може читатися з C++, Python, Rust, Java, R, Julia і Go без перетворень.
Колонкова організація даних дає три вирішальні переваги для аналітики. По-перше, дані одного типу зберігаються поруч, що дозволяє SIMD-векторизацію на сучасних CPU. По-друге, dictionary encoding і run-length encoding скорочують пам'ять у 5–20 разів для категоріальних колонок. По-третє, аналітичний запит зазвичай зачіпає лише кілька колонок, і Arrow дозволяє читати їх без завантаження решти.
PyArrow 18.0 вийшов у квітні 2026 року і вимагає Python 3.10+. Установлення стандартне, через pip або conda:
# Базове встановлення
pip install "pyarrow>=18.0"
# З підтримкою Flight RPC, ADBC та GPU (CUDA)
pip install "pyarrow[flight,adbc,cuda]>=18.0"
# Через conda (рекомендовано для Apple Silicon)
conda install -c conda-forge pyarrow=18
Перевіримо встановлення і подивимось, яка версія Arrow C++ під капотом:
Arrow Tables, Arrays та RecordBatch, три основні структури
PyArrow оперує трьома структурами даних: Array (одна типізована колонка), RecordBatch (набір колонок однакової довжини, як фрагмент таблиці) і Table (логічна таблиця, що складається з одного або більше RecordBatch). Зрозуміти їх взаємодію критично важливо, бо саме на цьому рівні відбувається zero-copy обмін.
import pyarrow as pa
# 1. Array, одна колонка з типом
prices = pa.array([19.99, 24.50, 7.20, 99.00], type=pa.float64())
categories = pa.array(["book", "book", "pen", "laptop"]).dictionary_encode()
# 2. RecordBatch, фрагмент таблиці
batch = pa.RecordBatch.from_arrays(
[prices, categories],
names=["price", "category"]
)
print(batch.num_rows, batch.schema)
# 3. Table, повна логічна таблиця
table = pa.Table.from_batches([batch, batch]) # 8 рядків з 2 батчів
print(table.num_rows) # 8
print(table.nbytes) # ~200 байт завдяки dictionary encoding
Зверніть увагу на dictionary_encode(): для категоріальних даних Arrow зберігає лише унікальні значення один раз, а в колонці тримає int-індекси. Для типового датасету з повторюваними рядками це економить 90%+ пам'яті порівняно з наївним зберіганням рядків.
Типи даних Arrow, яких немає у NumPy
Arrow підтримує типи, відсутні в NumPy 2.x: string з нульовим overhead'ом (на відміну від object dtype), list<T> для вкладених масивів, struct для іменованих полів, decimal128/256 для фінансів, timestamp[ns, tz] з часовими зонами та map<K,V>. Це дозволяє PyArrow точно представляти Parquet- та JSON-схеми, які pandas традиційно ламав.
Zero-copy між PyArrow, pandas і Polars
Найпоширеніша операція в реальному пайплайні, це конверсія між форматами. До Arrow вона вимагала серіалізації через pickle або копіювання через NumPy. У 2026 році це zero-copy: ви передаєте лише вказівник на буфер пам'яті, а не дані.
Arrow визначає фіксований layout у пам'яті: для кожної колонки є три буфери (validity bitmap, offsets, values). pandas 3.0 і Polars обидва читають з цих буферів напряму через спільний C-FFI протокол. Жодних memcpy, лише атомарне інкрементування лічильника посилань.
Робота з Parquet через PyArrow
Apache Parquet, це колонковий файловий формат, тісно інтегрований з Arrow. PyArrow надає найповнішу реалізацію читача Parquet у Python, включно з partition pruning, predicate pushdown і паралельним декодуванням. Якщо хочете глибше зрозуміти сам формат, рекомендую почати з офіційної документації Parquet.
import pyarrow as pa
import pyarrow.parquet as pq
# Запис Parquet із Snappy-стисненням і dictionary encoding
table = pa.table({"id": range(1_000_000), "value": [0.5] * 1_000_000})
pq.write_table(
table,
"data.parquet",
compression="zstd", # zstd кращий за snappy у 2026
compression_level=3,
use_dictionary=True,
write_statistics=True, # потрібно для predicate pushdown
)
# Читання лише потрібних колонок (column pruning)
subset = pq.read_table("data.parquet", columns=["id"])
print(subset.num_rows)
# Читання з фільтром (predicate pushdown, фільтр виконується на рівні файлу)
filtered = pq.read_table(
"data.parquet",
filters=[("id", ">", 500_000)],
)
print(filtered.num_rows) # ~499_999
Партиціонований датасет
Для великих даних використовуйте партиціонування за датою або категорією. Це дозволяє читати лише потрібні файли:
PyArrow має власний модуль pyarrow.compute з понад 250 векторизованими функціями: агрегації, рядкові операції, дати, math, статистика. У багатьох випадках він швидший за pandas і не потребує матеріалізації проміжних DataFrame'ів.
import pyarrow as pa
import pyarrow.compute as pc
table = pa.table({
"name": ["Олена", "Іван", "Марія", "Андрій"],
"salary": [85000, 92000, 78000, 110000],
"department": ["DS", "ENG", "DS", "ENG"],
})
# Фільтрація
mask = pc.greater(table["salary"], 80000)
filtered = table.filter(mask)
# Агрегація по групах
grouped = table.group_by("department").aggregate([
("salary", "mean"),
("salary", "max"),
("name", "count"),
])
print(grouped.to_pandas())
# Рядкові операції (швидші за str.* у pandas)
upper_names = pc.utf8_upper(table["name"])
contains_a = pc.match_substring(table["name"], "а")
За моїм досвідом, pyarrow.compute у 2–4 рази швидший за відповідні операції pandas 2.x на CPU-bound задачах. На pandas 3.0 з Arrow backend різниця нівелюється, бо pandas використовує саме ці функції під капотом.
Arrow Dataset для роботи з мільярдами рядків
Модуль pyarrow.dataset дозволяє працювати з даними, що не вміщаються у пам'ять. Він підтримує лінивий API: ви описуєте трансформації, а PyArrow стримить дані з диска (або S3, GCS, Azure Blob) батчами.
import pyarrow.dataset as ds
import pyarrow.compute as pc
# Підключаємось до 500 ГБ Parquet-датасету в S3
dataset = ds.dataset(
"s3://my-bucket/events/",
format="parquet",
partitioning="hive",
)
# Lazy scan: фільтр + проекція + агрегація без завантаження
scanner = dataset.scanner(
columns=["user_id", "amount", "country"],
filter=(ds.field("year") == 2026) & (ds.field("amount") > 100),
batch_size=100_000,
)
# Стрімінг по батчах
total = 0.0
for batch in scanner.to_batches():
total += pc.sum(batch["amount"]).as_py()
print(f"Total: {total:,.2f}")
Інтеграція з DuckDB
Один з найпотужніших патернів 2026 року, це використати DuckDB як SQL-двигун поверх Arrow Dataset. DuckDB читає Arrow без копіювання і дає SQL-інтерфейс:
import duckdb
import pyarrow.dataset as ds
dataset = ds.dataset("events/", format="parquet")
con = duckdb.connect()
query = (
"SELECT country, COUNT(*) AS events, SUM(amount) AS revenue "
"FROM dataset WHERE year = 2026 "
"GROUP BY country ORDER BY revenue DESC"
)
result = con.execute(query).arrow()
Arrow Flight RPC і ADBC: дані між сервісами
Arrow Flight — це high-performance RPC-протокол на основі gRPC для передачі Arrow-даних між сервісами. У бенчмарках 2026 року Flight досягає 6 ГБ/с на одному з'єднанні, що у 10–20 разів швидше за JDBC/ODBC. Сервери Dremio, InfluxDB IOx і Snowflake вже підтримують Flight як основний транспорт.
import pyarrow.flight as flight
class MyFlightServer(flight.FlightServerBase):
def do_get(self, context, ticket):
# Повертаємо великий датасет напряму як Arrow stream
table = pa.table({"id": range(10_000_000)})
return flight.RecordBatchStream(table)
# Клієнт
client = flight.FlightClient("grpc://localhost:8815")
reader = client.do_get(flight.Ticket(b"my_query"))
table = reader.read_all() # zero-copy Arrow Table
ADBC: заміна для JDBC/ODBC
Arrow Database Connectivity (ADBC), це стандарт драйверів для БД, що повертає результати запитів напряму в Arrow. Доступні драйвери для PostgreSQL, SQLite, Snowflake, BigQuery і DuckDB. Повний список і документація живуть на arrow.apache.org/adbc:
import adbc_driver_postgresql.dbapi as adbc
with adbc.connect("postgresql://user:pass@localhost/db") as conn:
with conn.cursor() as cur:
cur.execute("SELECT * FROM events WHERE date >= '2026-01-01'")
table = cur.fetch_arrow_table() # Arrow напряму, без list-of-tuples
Порівняно з psycopg2, ADBC дає 5–8x прискорення на великих результатах саме тому, що оминає Python-об'єкти. У мене на проєкті з аналітикою CRM-подій перехід з psycopg2 на ADBC скоротив нічний ETL з 47 хвилин до приблизно 9.
Типові помилки та як їх уникнути
За останні два роки я бачив, як команди наступають на ті самі граблі. Ось топ-5 помилок при роботі з PyArrow:
Конверсія в pandas без ArrowDtype, ви втрачаєте zero-copy і потрапляєте в object dtype для рядків. Завжди передавайте types_mapper=pd.ArrowDtype.
Запис Parquet без статистики, без write_statistics=True predicate pushdown не працює, і фільтр сканує весь файл. У PyArrow 18 це стандартна поведінка, але краще перевірити.
Використання Table.from_pylist для мільйонів рядків, це повільно. Збирайте дані батчами через RecordBatchBuilder або одразу з NumPy-масивів.
Ігнорування timezone у timestamps. Arrow timestamp[ns] без tz і з tz='UTC' дають різні типи. При читанні з Parquet pandas може мовчки переключити dtype.
Завантаження великих датасетів через read_table замість dataset.scanner(), швидко з'їдає RAM. Для всього понад 5 ГБ використовуйте Dataset API.
Поширені запитання
У чому різниця між Apache Arrow і Parquet?
Arrow, це формат у пам'яті (RAM), оптимізований для швидкого доступу та обчислень. Parquet, це формат на диску, оптимізований для зберігання і стиснення. Вони компліментарні: Parquet-файли читаються в Arrow-таблиці, а Arrow-таблиці можуть записуватися в Parquet через pyarrow.parquet.
Чи потрібен PyArrow, якщо я вже використовую Polars?
Так. Polars побудований на Arrow і автоматично встановлює його як залежність, але pyarrow потрібен для специфічних операцій: запис Parquet із кастомним кодуванням, Flight RPC, ADBC-драйвери, обмін даними з pandas через ArrowDtype та робота з pyarrow.dataset на S3/GCS.
Чи швидший PyArrow за pandas для аналітики?
Для pandas 2.x, так, у 2–10 разів на типових операціях завдяки SIMD і колонковому layout. Для pandas 3.0 з Arrow backend різниця мінімальна, бо pandas 3.0 використовує PyArrow під капотом. Однак PyArrow виграє, коли ви оминаєте pandas API і працюєте напряму через pyarrow.compute.
Як читати CSV у PyArrow з кастомним розділювачем?
Використайте pyarrow.csv.read_csv з параметром parse_options=csv.ParseOptions(delimiter=';'). PyArrow CSV-парсер у 5–15 разів швидший за pandas.read_csv завдяки паралельному декодуванню. Для дуже великих файлів використайте pyarrow.csv.open_csv зі стрімінгом.
Що таке Arrow Flight і коли він кращий за REST API?
Arrow Flight, це gRPC-протокол для передачі Arrow-даних між сервісами зі швидкістю до 6 ГБ/с. Він кращий за REST/JSON для будь-яких аналітичних навантажень: ETL-пайплайнів, ML-сервісів, що повертають великі результати, дашбордів, що тягнуть мільйони рядків. Для типових CRUD-операцій REST залишається простішим.
Практичний посібник із vLLM для продакшн-сервінгу LLM у Python: установка, OpenAI-сумісний сервер, tensor parallelism, FP8/AWQ квантизація, моніторинг і типові граблі з польового досвіду.
Повний практичний посібник з DuckDB 1.5 у Python: встановлення, zero-copy інтеграція з pandas і Polars, запити над Parquet/CSV/JSON, AsOf joins і бенчмарки 2026 року.
Повний практичний посібник з інженерії ознак у Python: числові трансформації, кодування категорій, обробка пропусків, автоматизація з feature-engine та Featuretools, побудова пайплайнів scikit-learn 1.8.