Что такое Polars и зачем он нужен
Если вы работаете с данными в Python, то скорее всего давно подружились с pandas — де-факто стандартом для табличных данных. Но вот в чём дело: как только объёмы начинают расти, pandas начинает задыхаться. Однопоточная обработка, прожорливость по памяти, медленное чтение больших файлов — знакомо?
Именно для решения этих болячек и появился Polars — высокопроизводительная библиотека DataFrame, написанная на Rust. Да, именно на Rust — и это не просто маркетинговый ход.
Polars использует формат Apache Arrow для хранения данных в памяти, умеет распараллеливать запросы на все доступные ядра и поддерживает ленивые вычисления с автоматической оптимизацией. По бенчмаркам 2026 года Polars обрабатывает данные в 5–30 раз быстрее pandas — и при этом ест меньше оперативки.
Актуальная стабильная версия на момент написания — Polars 1.39 (март 2026). Нужен Python 3.10+. Обновления выходят практически каждую неделю, так что библиотека развивается очень активно.
Установка и первые шаги
Тут всё максимально просто. Ставим через pip:
pip install polars
Если нужна работа с дополнительными форматами и облачными хранилищами, берите расширенную версию:
pip install 'polars[numpy,pandas,pyarrow,fsspec]'
По соглашению импортируем с алиасом pl (аналогично тому, как pandas — это pd):
import polars as pl
# Создание DataFrame из словаря
df = pl.DataFrame({
"имя": ["Алиса", "Борис", "Виктория", "Дмитрий"],
"возраст": [28, 35, 42, 31],
"зарплата": [85000, 120000, 95000, 110000],
"отдел": ["аналитика", "разработка", "аналитика", "разработка"],
})
print(df)
На выходе получаем аккуратную таблицу с типами данных — Polars строго типизирован и выводит типы автоматически при создании:
shape: (4, 4)
┌──────────┬─────────┬──────────┬────────────┐
│ имя ┆ возраст ┆ зарплата ┆ отдел │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ i64 ┆ str │
╞══════════╪═════════╪══════════╪════════════╡
│ Алиса ┆ 28 ┆ 85000 ┆ аналитика │
│ Борис ┆ 35 ┆ 120000 ┆ разработка │
│ Виктория ┆ 42 ┆ 95000 ┆ аналитика │
│ Дмитрий ┆ 31 ┆ 110000 ┆ разработка │
└──────────┴─────────┴──────────┴────────────┘
Кстати, обратите внимание на разделитель ┆ вместо привычной вертикальной черты — к этому быстро привыкаешь.
Ключевые отличия Polars от pandas
Перед тем как нырять в код, давайте разберёмся в принципиальных различиях. Это реально сэкономит время и нервы при переходе.
Нет индекса
В pandas у каждого DataFrame есть индекс — и с ним целый зоопарк проблем: .loc vs .iloc, SettingWithCopyWarning, сбросы индекса после группировки... В Polars индекса просто нет. Строки адресуются по позиции. И знаете что? Жить стало проще.
Apache Arrow вместо NumPy
Pandas хранит данные в массивах NumPy, а Polars — в формате Apache Arrow. Это современный колоночный формат, который обеспечивает лучшую кэш-локальность, zero-copy обмен данными между библиотеками и куда более эффективную работу со строками.
Многопоточность из коробки
Pandas работает в одном потоке. Точка. В Polars параллелизм встроен на уровне архитектуры — операции автоматически раскидываются по всем ядрам без единой строчки дополнительного кода с вашей стороны.
Ленивые вычисления (Lazy Evaluation)
Pandas выполняет каждую операцию сразу (eager mode). Polars поддерживает оба режима. В ленивом режиме библиотека сначала строит план выполнения, оптимизирует его (pushdown предикатов, pushdown проекций, объединение операций) и только потом считает. На сложных цепочках преобразований это даёт кратное ускорение.
Выражения вместо императивного кода
В pandas вы пишете пошаговые инструкции — сделай то, потом это. В Polars вы описываете что хотите получить через систему выражений, а библиотека сама решает как это сделать быстрее всего. Честно говоря, к этому подходу привыкаешь за пару дней — и потом не хочется возвращаться.
Основные операции: сравнение с pandas
Итак, давайте посмотрим на типичные операции бок о бок. Это, пожалуй, самая полезная часть для тех, кто уже знает pandas.
Выборка столбцов (select)
# pandas
df_pd[["имя", "зарплата"]]
# Polars
df.select("имя", "зарплата")
# или с выражениями:
df.select(pl.col("имя"), pl.col("зарплата"))
Фильтрация строк (filter)
# pandas
df_pd[df_pd["возраст"] > 30]
# Polars
df.filter(pl.col("возраст") > 30)
Добавление и изменение столбцов (with_columns)
# pandas
df_pd["бонус"] = df_pd["зарплата"] * 0.1
# Polars — DataFrame неизменяемый, создаётся новый
df = df.with_columns(
(pl.col("зарплата") * 0.1).alias("бонус")
)
Важный момент: в Polars нельзя менять DataFrame на месте через df["col"] = .... Только with_columns, который возвращает новый объект. Поначалу это раздражает, но потом начинаешь ценить — меньше побочных эффектов, меньше багов.
Группировка и агрегация (group_by)
# pandas
df_pd.groupby("отдел").agg({"зарплата": ["mean", "max"], "возраст": "mean"})
# Polars
df.group_by("отдел").agg(
pl.col("зарплата").mean().alias("средняя_зарплата"),
pl.col("зарплата").max().alias("макс_зарплата"),
pl.col("возраст").mean().alias("средний_возраст"),
)
Синтаксис Polars тут чуть длиннее, зато явно видно, что происходит. Никаких словарей со вложенными списками — всё читаемо.
Сортировка (sort)
# pandas
df_pd.sort_values("зарплата", ascending=False)
# Polars
df.sort("зарплата", descending=True)
Соединение таблиц (join)
# Создаём таблицу отделов
отделы = pl.DataFrame({
"отдел": ["аналитика", "разработка"],
"бюджет": [500000, 800000],
})
# pandas
pd.merge(df_pd, отделы_pd, on="отдел", how="left")
# Polars
df.join(отделы, on="отдел", how="left")
Ленивые вычисления: LazyFrame на практике
Вот тут начинается самое интересное. Ленивые вычисления — это, пожалуй, главная киллер-фича Polars. Вместо того чтобы считать каждый шаг сразу, вы описываете весь конвейер, а Polars сам разбирается, как выполнить его оптимально.
Базовый пример LazyFrame
import polars as pl
# Создаём LazyFrame
lazy_df = pl.LazyFrame({
"product": ["A", "B", "A", "C", "B", "A"],
"revenue": [100, 200, 150, 300, 250, 180],
"region": ["Москва", "СПб", "Москва", "СПб", "Москва", "СПб"],
})
# Строим план запроса (ничего не выполняется!)
result = (
lazy_df
.filter(pl.col("revenue") > 120)
.group_by("product")
.agg(
pl.col("revenue").sum().alias("total_revenue"),
pl.col("revenue").count().alias("num_sales"),
)
.sort("total_revenue", descending=True)
)
# А вот теперь выполняем
print(result.collect())
Вызов collect() запускает оптимизатор и прогоняет весь план за один проход. Polars автоматически «проталкивает» фильтры поближе к источнику данных (predicate pushdown) и загружает только нужные столбцы (projection pushdown).
Чтение больших файлов с scan_parquet
Настоящая мощь LazyFrame раскрывается при работе с файлами. Функция scan_parquet не грузит весь файл в память — она создаёт LazyFrame с отложенным чтением:
# Ленивое чтение Parquet-файла
lazy_df = pl.scan_parquet("sales_data_2026.parquet")
# Polars прочитает только нужные столбцы и строки
result = (
lazy_df
.filter(pl.col("date") >= pl.lit("2026-01-01"))
.select("product_id", "revenue", "quantity")
.group_by("product_id")
.agg(
pl.col("revenue").sum().alias("total_revenue"),
pl.col("quantity").sum().alias("total_qty"),
)
.sort("total_revenue", descending=True)
.head(10)
.collect()
)
print(result)
Parquet-файлы хранят метаданные о диапазонах значений в каждом row group. Polars использует это для пропуска целых блоков, не подходящих под фильтр. На практике это может ускорить чтение в десятки раз по сравнению с pd.read_parquet().
Потоковая обработка: данные больше RAM
А что делать, если датасет банально не влезает в память? Для этого есть streaming-режим:
# Данные не помещаются в RAM — используем streaming
(
pl.scan_parquet("huge_dataset_100gb.parquet")
.filter(pl.col("status") == "active")
.group_by("category")
.agg(pl.col("amount").sum())
.collect(engine="streaming")
)
Параметр engine="streaming" заставляет Polars обрабатывать данные блоками, не пытаясь загрузить всё сразу. Я лично гонял 50-гигабайтный датасет на ноутбуке с 16 ГБ RAM — и оно работало. С pandas такой фокус не прошёл бы.
Работа с файлами: CSV, Parquet, JSON
Polars умеет читать и писать все основные форматы. Вот краткий обзор.
CSV
# Чтение CSV (eager)
df = pl.read_csv("data.csv")
# Ленивое чтение CSV
lazy_df = pl.scan_csv("data.csv")
# Запись в CSV
df.write_csv("output.csv")
Polars параллелит чтение CSV по нескольким ядрам, что даёт 2–5-кратное ускорение по сравнению с pd.read_csv(). Если вы регулярно грузите большие CSV-файлы, разницу почувствуете сразу.
Parquet
# Чтение Parquet
df = pl.read_parquet("data.parquet")
# Ленивое чтение — рекомендуемый способ
lazy_df = pl.scan_parquet("data.parquet")
# Запись в Parquet
df.write_parquet("output.parquet")
Parquet — лучший друг Polars. Колоночное хранение, встроенное сжатие, метаданные для оптимизации запросов. Если есть возможность, конвертируйте свои CSV в Parquet — не пожалеете.
JSON
# Чтение JSON
df = pl.read_json("data.json")
# Чтение JSON Lines (по строке на запись)
df = pl.read_ndjson("data.jsonl")
# Запись
df.write_json("output.json")
df.write_ndjson("output.jsonl")
Интеграция с pandas и другими библиотеками
Переход на Polars — это не ультиматум. Совсем не обязательно выбрасывать весь свой pandas-код. Обе библиотеки отлично уживаются вместе благодаря общему фундаменту Apache Arrow.
Конвертация между Polars и pandas
import pandas as pd
import polars as pl
# pandas → Polars
pd_df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
pl_df = pl.from_pandas(pd_df)
# Polars → pandas
pd_df_back = pl_df.to_pandas()
Конвертация через Arrow идёт без копирования данных (zero-copy), если типы совместимы. То есть накладных расходов — почти ноль.
Использование с scikit-learn
Scikit-learn пока ожидает на входе массивы NumPy или pandas DataFrame (хотя, возможно, скоро это изменится). Пока что — просто конвертируем:
from sklearn.linear_model import LinearRegression
import polars as pl
df = pl.DataFrame({
"feature_1": [1.0, 2.0, 3.0, 4.0, 5.0],
"feature_2": [2.1, 3.9, 6.2, 7.8, 10.1],
"target": [3.0, 6.1, 9.0, 12.2, 15.0],
})
X = df.select("feature_1", "feature_2").to_numpy()
y = df["target"].to_numpy()
model = LinearRegression()
model.fit(X, y)
print(f"Коэффициенты: {model.coef_}")
Использование с matplotlib
import matplotlib.pyplot as plt
import polars as pl
df = pl.DataFrame({
"месяц": ["Янв", "Фев", "Мар", "Апр", "Май", "Июн"],
"продажи": [120, 135, 148, 162, 155, 178],
})
plt.figure(figsize=(8, 5))
plt.bar(df["месяц"].to_list(), df["продажи"].to_list())
plt.title("Продажи по месяцам")
plt.ylabel("Продажи (тыс. руб.)")
plt.show()
Да, нужен лишний шаг с .to_list() или .to_numpy(), но это мелочь.
Производительность: реальные бенчмарки 2026 года
Ну а теперь к цифрам — ради чего всё это затевалось. Бенчмарки на датасете 10 млн строк (19 столбцов, Parquet ~255 МБ), машина с 4 ядрами / 8 потоков и 16 ГБ RAM:
| Операция | pandas | Polars | Ускорение |
|---|---|---|---|
| Чтение CSV | 12.4 с | 2.8 с | 4.4× |
| Фильтрация | 9.4 с | 1.9 с | 5.0× |
| Сортировка | 37.0 с | 3.0 с | 12.3× |
| Group by + agg | 8.2 с | 2.6 с | 3.2× |
| Join (left) | 9.5 с | 2.6 с | 3.6× |
Сортировка — вообще разгром: 12× быстрее. А по памяти ситуация такая: pandas съедает примерно 5–10× от размера данных на диске, Polars — около 2–4×. Для нашего датасета в 255 МБ это ~1.5 ГБ у pandas против ~600 МБ у Polars.
Когда выбрать Polars, а когда оставить pandas
Давайте начистоту: Polars — не серебряная пуля. Вот мой чек-лист для выбора.
Берите Polars, когда:
- Большие наборы данных — от 1 ГБ и выше, где pandas начинает тормозить
- ETL-пайплайны — регулярная обработка данных, где каждая секунда на счету
- Мало памяти — streaming позволяет ворочать данные, которые не влезают в RAM
- Новый проект с нуля — нет legacy-кода, можно сразу начать с Polars
- Работа с Parquet — здесь Polars раскрывается на полную
Оставайтесь с pandas, когда:
- Маленькие датасеты — до 1 ГБ разница в скорости почти незаметна
- ML-экосистема — scikit-learn, statsmodels и компания пока дружат только с pandas
- Интерактивный анализ в Jupyter — для быстрого EDA pandas по-прежнему удобнее
- Куча существующего кода — переписывать рабочий пайплайн ради скорости не всегда имеет смысл
- Сложная работа со строками — тут pandas пока быстрее (хотя Polars догоняет)
Мой совет на 2026 год: не выбирайте «или–или». Используйте Polars для тяжёлых вычислений и ETL, а pandas — для интерактивного анализа и интеграции с ML-библиотеками. Конвертация между ними через Arrow занимает миллисекунды, так что никакого оверхеда.
Шпаргалка: pandas → Polars
Эту таблицу стоит держать под рукой, пока привыкаете к новому синтаксису:
| Операция | pandas | Polars |
|---|---|---|
| Импорт | import pandas as pd | import polars as pl |
| Создание DataFrame | pd.DataFrame(data) | pl.DataFrame(data) |
| Чтение CSV | pd.read_csv() | pl.read_csv() / pl.scan_csv() |
| Выборка столбцов | df[["a", "b"]] | df.select("a", "b") |
| Фильтрация | df[df["a"] > 5] | df.filter(pl.col("a") > 5) |
| Новый столбец | df["c"] = df["a"] + 1 | df.with_columns((pl.col("a") + 1).alias("c")) |
| Группировка | df.groupby("a").agg(...) | df.group_by("a").agg(...) |
| Сортировка | df.sort_values("a") | df.sort("a") |
| Join | pd.merge(df1, df2) | df1.join(df2) |
| Размер | df.shape | df.shape |
| Описание | df.describe() | df.describe() |
Часто задаваемые вопросы (FAQ)
Polars полностью заменяет pandas?
Нет, и не стоит так ставить вопрос. Polars — это альтернатива для определённых задач. У pandas огромная экосистема: сотни библиотек ожидают pandas DataFrame на входе. Polars быстрее для тяжёлой обработки, но pandas остаётся лучшим выбором для интерактивного анализа и ML-пайплайнов. Лучшая стратегия — использовать обе библиотеки вместе, каждую для своих задач.
Насколько Polars быстрее pandas на практике?
Зависит от задачи и объёма данных. На датасетах от 1 ГБ — в 5–30 раз быстрее для типичных операций. На маленьких (до 100 МБ) разница может быть незаметной. Самый большой выигрыш — на сортировке (до 12×) и чтении файлов (до 5×).
Сложно ли перейти с pandas на Polars?
Синтаксис другой, но концепции те же самые. Главное — перестроить мышление с императивного стиля на декларативный (выражения). По моему опыту, большинство разработчиков осваивают базу за 1–2 дня. А ещё у Polars есть отличный гайд по миграции с pandas в официальной документации.
Можно ли использовать Polars с Jupyter Notebook?
Конечно. Polars полностью совместим с Jupyter Notebook и JupyterLab — DataFrame рендерятся в красивые HTML-таблицы. Для визуализации подключайте matplotlib, seaborn или plotly, предварительно конвертировав данные через .to_pandas() или .to_numpy().
Поддерживает ли Polars работу с базами данных?
Да. Есть встроенные функции pl.read_database() и pl.scan_database() для PostgreSQL, MySQL и SQLite. Также можно подключиться к любой БД через SQLAlchemy и передать результат в Polars через pandas или Arrow.