Привет, Хабр! Это Леша Жиряков, техлид backend-команды витрины онлайн-кинотеатра KION. В прошлом посте я рассказывал про альтернативы Pandas, а сегодня будем сравнивать две библиотеки — Polars и Pandas. Обсудим, какие преимущества есть у Polars и за счет чего она выигрывает в производительности. В посте — мой взгляд, но мнения по этому поводу, конечно, разные. Пишите, что думаете, в комментариях — будем обсуждать!

Что за библиотеки такие
Сначала вспомним историю появления и предназначение двух этих инструментов.
Pandas

Pandas создана в 2008 году Уэсом МакКинни в компании AQR Capital Management. Основная специализация библиотеки — обработка и анализ табличных данных, включая их преобразование, фильтрацию, агрегацию и визуализацию. Написана она на Python, с критическими компонентами, оптимизированными на Cython.
Pandas — один из самых популярных инструментов для работы с данными, о чем свидетельствуют 40+ тысяч звезд на GitHub.
Что по характеристикам:
-
Возможности: позволяет разработчикам выбирать между различными типами массивов для представления данных датафрейма. Традиционно большинство датафреймов поддерживается массивами NumPy. Начиная с версии 2.0 можно использовать PyArrow в качестве формата хранения данных.
-
Количество звезд на GitHub: 44,1 тыс.
-
Открытых Issues: ~3,6 тысяч — актуально на декабрь 2024 года.
-
Версия: 2.2.3.
-
Merge Requests (MRs): обрабатываются индивидуальными разработчиками и широким комьюнити.
Подробнее тут.
Начиная с версии 2.0 в Pandas была введена экспериментальная функция Copy-on-Write (CoW), изменяющая подход к копированию: появились улучшения этого режима. При ее включении копирование данных откладывается до момента их изменения, а это позволяет эффективнее использовать память. DuckDB/Dask всегда используют out-of-core.
Ключевые технологии библиотеки:
-
DataFrame и Series. Основные структуры данных Pandas, обеспечивающие удобную работу с табличными (DataFrame) и одномерными данными (Series). Позволяют легко обрабатывать, фильтровать и манипулировать информацией.
-
NumPy. Pandas построена на базе NumPy — это обеспечивает высокую производительность и эффективные операции с массивами данных.
-
Чтение и запись данных. Поддержка множества форматов — CSV, Excel, SQL, JSON — делает Pandas универсальным инструментом для загрузки и экспорта информации.
-
Гибкая индексация. Возможности работы с метками и числовыми индексами и поддержка многомерной индексации (MultiIndex) упрощают работу с данными любой сложности.
Polars

Библиотека для обработки данных Polars появилась сравнительно недавно — в 2020 году. Разработана она с упором на производительность. Создатель — Рейнарс Вегис. Polars специализируется на быстром анализе больших объемов информации и написана на Rust. На GitHub у нее 4,5 тысячи звезд.
Что по характеристикам:
-
Возможности: использует колоночный формат Apache Arrow для хранения данных.
-
Количество звезд на GitHub: 31 тыс.
-
Открытых Issues: 2,1 тысяч — актуально на декабрь 2024 года.
-
Версия: 1.17.1.
-
Merge Requests (MRs): комьюнити.
Подробнее тут.
Polars предоставляет два API: отложенные вычисления (lazy evaluation) и немедленное выполнение (eager evaluation). Она эффективно обрабатывает большие объемы данных и превосходит по скорости многие другие библиотеки при выполнении сложных операций — например, сортировке и группировке. Еще библиотека умеет помещать данные в память и работать в out-of-core-режиме, обращаясь к файлам на диске и обрабатывая их небольшими чанками.
Хотя синтаксис Pandas может быть более привычным, Polars обеспечивает лучшую производительность при работе с крупными датафреймами, а это важный фактор для дата-инженеров. При подготовке данных для анализа и машинного обучения, когда требуется очистка, нормализация и другие преобразования, Polars справляется с этими задачами быстрее, чем даже Pandas версии 2.0.
Сравнение производительности — бенчмарки
Агрегация
Операции агрегации в Polars показали значительное превосходство по скорости выполнения по сравнению с Pandas. Для проведения тестов использовался Covertype из библиотеки scikit-learn, в котором было 581 012 строк и 54 столбца. Во время тестирования выполнялась операция вычисления среднего значения для всех числовых столбцов.
Тестирование
# Pandas start_time = time.time() mean_pandas = df_pandas.drop('target', axis=1).mean() pandas_time = time.time() - start_time # Polars start_time = time.time() mean_polars = df_polars.select(pl.all().exclude('target')).mean() polars_time = time.time() - start_time print(f"Pandas aggregation time: {pandas_time:.4f} seconds") print(f"Polars aggregation time: {polars_time:.4f} seconds") print(f"Polars is {pandas_time/polars_time:.2f}x faster than Pandas")
Результаты показали, что Polars выполнил операцию агрегации за 0,0083 сек., тогда как Pandas потребовалось 0,1863 сек. Значит, Polars оказался больше чем в 22 раза быстрее Pandas.
Такие показатели объясняются использованием Polars языка программирования Rust, который оптимизирует работу с памятью и применяет многопоточность. Это делает библиотеку привлекательной для задач, связанных с обработкой и анализом больших объемов данных.
Слияние, или join
Тот же набор данных использовался и для бенчмарка по операциям фильтрации, ключевым в анализе данных. В рамках тестирования выполнялась операция, где значение в столбце ‘feature_0’ превышало среднее.
Тестирование
# Pandas start_time = time.time() filtered_pandas = df_pandas[df_pandas['feature_0'] > df_pandas['feature_0'].mean()] pandas_time = time.time() - start_time # Polars start_time = time.time() filtered_polars = df_polars.filter(pl.col('feature_0') > pl.col('feature_0').mean()) polars_time = time.time() - start_time print(f"Pandas filtering time: {pandas_time:.4f} seconds") print(f"Polars filtering time: {polars_time:.4f} seconds") print(f"Polars is {pandas_time/polars_time:.2f}x faster than Pandas")
Результаты показали, что Polars выполнил операцию фильтрации за 0,0183 сек., тогда как Pandas потребовалось 0,0741 сек. То есть Polars оказался примерно в четыре раза быстрее Pandas.
Группировка данных, или GroupBy Operation
В рамках тестирования выполнялась операция группировки по столбцу ‘target’ с последующим вычислением среднего значения для каждой группы.
Тестирование
# Pandas start_time = time.time() grouped_pandas = df_pandas.groupby('target').mean() pandas_time = time.time() - start_time # Polars start_time = time.time() grouped_polars = df_polars.group_by('target').agg(pl.all().exclude('target').mean()) polars_time = time.time() - start_time print(f"Pandas groupby time: {pandas_time:.4f} seconds") print(f"Polars groupby time: {polars_time:.4f} seconds") print(f"Polars is {pandas_time/polars_time:.2f}x faster than Pandas")
Результаты показали, что Polars выполнил операцию группировки за 0,201 сек., тогда как Pandas потребовалось 1,100 сек. Polars оказался примерно в 5,5 раз быстрее Pandas.
Сортировка, или sorting
В рамках тестирования выполнялась операция сортировки по столбцу ‘Elevation’.
Тестирование
# Pandas start_time = time.time() sorted_pandas = df_pandas.sort_values('feature_0', ascending=False) pandas_time = time.time() - start_time # Polars start_time = time.time() sorted_polars = df_polars.sort('feature_0', descending=True) polars_time = time.time() - start_time print(f"Pandas sorting time: {pandas_time:.4f} seconds") print(f"Polars sorting time: {polars_time:.4f} seconds") print(f"Polars is {pandas_time/polars_time:.2f}x faster than Pandas")
Результаты показали, что Polars выполнил операцию сортировки за 0,0656 сек., тогда как Pandas потребовалось 0,2027 сек. Polars оказался примерно в три раза быстрее Pandas.
Инжиниринг признаков, или feature engineering
Задача состояла в вычислении Z-оценки для каждого значения в столбце ‘feature_0’. Это включает вычитание среднего значения столбца и деление на стандартное отклонение.
Тестирование
# Pandas start_time = time.time() pandas_zscore = df_pandas.copy() for column in pandas_zscore.columns[:-1]: # Exclude the target column pandas_zscore[f'{column}_zscore'] = (pandas_zscore[column] - pandas_zscore[column].mean()) / pandas_zscore[column].std() pandas_time = time.time() - start_time # Polars start_time = time.time() feature_cols = [f'feature_{i}' for i in range(54)] polars_zscore = df_polars.with_columns([ ((pl.col(col) - pl.col(col).mean()) / pl.col(col).std()).alias(f'{col}_zscore') for col in feature_cols ]) polars_time = time.time() - start_time print(f"Pandas z-score calculation time: {pandas_time:.4f} seconds") print(f"Polars z-score calculation time: {polars_time:.4f} seconds") print(f"Polars is {pandas_time/polars_time:.2f}x faster than Pandas")
Результаты показали, что Polars выполнил вычисление Z-оценки для нормализации данных за 0,0919 сек., тогда как Pandas потребовалось 0,5154 сек. Вывод — Polars оказался примерно в 5,6 раза быстрее Pandas.
Почему Polars быстрее
У Pandas ограниченная поддержка работы с данными, превышающими объем оперативной памяти. Архитектура базируется на NumPy — это ограничивает обработку больших массивов данных, помещающихся в память одного потока. Для взаимодействия с большими наборами данных часто приходится использовать сторонние инструменты, такие как Dask.
Polars же написан на Rust — языке программирования, который славится своей безопасностью и высокой производительностью. Rust позволяет эффективно управлять памятью и гарантирует отсутствие состояния гонки. К тому же Polars использует несколько ключевых технологий:
-
Arrow Columnar Format — формат хранения информации, который оптимизирован для быстрого доступа. Данные хранятся в колонках, так что чтение и обработка становятся более эффективными.
-
Lazy Execution — отложенный запуск, при котором операции реализуются только тогда, когда требуется результат. Это позволяет оптимизировать план выполнения запросов и исключить лишние вычисления.
-
Многопоточность — Polars использует все ядра процессора для реализации задач.
-
Memory Mapping — позволяет работать с данными напрямую с диска, не загружая весь объем в оперативную память.
Что касается API, то у Polars свой собственный DataFrame API (иммутабельный, в отличие от Pandas), а еще есть поддержка SQL.
В целом, Pandas и Polars — мощные инструменты для анализа данных, но библиотека Polars выигрывает в производительности и масштабируемости. Она идеально подходит для работы с большими наборами данных, во многом как раз из-за использования современных технологий и языка Rust. Но Pandas списывать тоже нельзя, она была и остается удобным выбором для решения не слишком объемных задач, плюс не стоит забывать и про экосистему библиотеки. К тому же у Pandas одно из самых больших и активных сообществ среди инструментов для анализа данных в Python.
И напоследок. Polars превосходит Pandas и по логотипу. Белый медведь, в отличие от панды, — самый крупный сухопутный хищник на Земле: длина тела может достигать 3 метров. Он может учуять добычу на расстоянии около километра под метровой толщиной снега, проплыть без остановки 687 км (официально зарегистрированный рекорд) и много чего еще. Ну и как тут с ним бороться?
Теперь вам слово — пишите в комментариях, что предпочитаете.
Что еще почитать:
ссылка на оригинал статьи https://habr.com/ru/articles/870266/
Добавить комментарий