- Регистрация
- 12.04.17
- Сообщения
- 19.095
- Реакции
- 107
- Репутация
- 0
В этой статье я расскажу о шести инструментах, способных значительно ускорить ваш pandas код. Инструменты я собрал по одному принципу — простота интеграции в существующую кодовую базу. Для большинства инструментов вам достаточно установить модуль и добавить пару строк кода.
Pandas давно стал незаменимым инструментом любого разработчика благодаря простому и понятному API, а также богатому набору средств для очистки, исследования и анализа данных. И все бы хорошо, но когда речь идет о данных, которые не влезают в оперативную память или требуют сложных вычислений, его производительности начинает не хватать.
В этой статье я не буду описывать качественно другие подходы к анализу данных, вроде Spark или DataFlow. Вместо этого я опишу шесть интересных инструментов и продемонстрирую результаты их использования:
Часть 1:
Часть 2:
Numba
Этот инструмент ускоряет непосредственно сам Python.
Смоделируем типичную ситуацию — нужно добавить новую колонку, применив какую-то функцию к уже существующей с помощью метода apply.
import numpy as np
import numba
# создадим таблицу в 100 000 строк и 4 колонки, заполненную случайными числами от 0 до 100
df = pd.DataFrame(np.random.randint(0,100,size=(100000, 4)),columns=['a', 'b', 'c', 'd'])
# функция для создания новой колонки
def multiply(x):
return x * 5
# оптимизированная с помощью numba версия
@numba.vectorize
def multiply_numba(x):
return x * 5
Как видите, вам не нужно ничего менять в своем коде. Достаточно всего лишь добавить декоратор. А теперь посмотрите на время работы.
# наша функция
In [1]: %timeit df['new_col'] = df['a'].apply(multiply)
23.9 ms ± 1.93 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# встроенная имплементация Pandas
In [2]: %timeit df['new_col'] = df['a'] * 5
545 µs ± 21.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# наша функция с numba
# мы отдаем весь вектор значений, чтобы numba сам провел оптимизацию цикла
In [3]: %timeit df['new_col'] = multiply_numba(df['a'].to_numpy())
329 µs ± 2.37 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Оптимизированная версия быстрее в ~70 раз! Впрочем, в абсолютных величинах, версия от Pandas отстала совсем несильно, поэтому возьмем более сложный кейс. Определим новые функции:
# возводим значения строки в квадрат и берем их среднее
def square_mean(row):
row = np.power(row, 2)
return np.mean(row)
# применение:
# df['new_col'] = df.apply(square_mean, axis=1)
# numba не умеет работать с примитивами pandas (Dataframe, Series и тд.)
# поэтому мы даем ей двумерный массив numpy
@numba.njit
def square_mean_numba(arr):
res = np.empty(arr.shape[0])
arr = np.power(arr, 2)
for i in range(arr.shape[0]):
res = np.mean(arr)
return res
# применение:
# df['new_col'] = square_mean_numba(df.to_numpy())
Построим график зависимости времени вычислений от количества строк в датафрейме:
Итоги
Multiprocessing
Первое, что приходит в голову, когда встает задача обработать большую пачку данных, это конечно же распараллелить все вычисления. В этот раз мы обойдемся без каких-либо сторонних библиотек, используя только средства python.
В качестве примера будем использовать обработку текста. Ниже я взял
df = pd.read_csv('abcnews-date-text.csv', header=0)
# увеличиваю датасет в 10 раз, добавляя копии в конец
df = pd.concat([df] * 10)
df.head()
Pandas давно стал незаменимым инструментом любого разработчика благодаря простому и понятному API, а также богатому набору средств для очистки, исследования и анализа данных. И все бы хорошо, но когда речь идет о данных, которые не влезают в оперативную память или требуют сложных вычислений, его производительности начинает не хватать.
В этой статье я не буду описывать качественно другие подходы к анализу данных, вроде Spark или DataFlow. Вместо этого я опишу шесть интересных инструментов и продемонстрирую результаты их использования:
Часть 1:
- Numba
- Multiprocessing
- Pandarallel
Часть 2:
- Swifter
- Modin
- Dask
Numba
Этот инструмент ускоряет непосредственно сам Python.
You must be registered for see links
— это JIT компилятор, который очень любит циклы, математические операции и работу с Numpy, поверх которого как раз и построен Pandas. Проверим на практике, какие преимущества он дает.Смоделируем типичную ситуацию — нужно добавить новую колонку, применив какую-то функцию к уже существующей с помощью метода apply.
import numpy as np
import numba
# создадим таблицу в 100 000 строк и 4 колонки, заполненную случайными числами от 0 до 100
df = pd.DataFrame(np.random.randint(0,100,size=(100000, 4)),columns=['a', 'b', 'c', 'd'])
# функция для создания новой колонки
def multiply(x):
return x * 5
# оптимизированная с помощью numba версия
@numba.vectorize
def multiply_numba(x):
return x * 5
Как видите, вам не нужно ничего менять в своем коде. Достаточно всего лишь добавить декоратор. А теперь посмотрите на время работы.
# наша функция
In [1]: %timeit df['new_col'] = df['a'].apply(multiply)
23.9 ms ± 1.93 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# встроенная имплементация Pandas
In [2]: %timeit df['new_col'] = df['a'] * 5
545 µs ± 21.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# наша функция с numba
# мы отдаем весь вектор значений, чтобы numba сам провел оптимизацию цикла
In [3]: %timeit df['new_col'] = multiply_numba(df['a'].to_numpy())
329 µs ± 2.37 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Оптимизированная версия быстрее в ~70 раз! Впрочем, в абсолютных величинах, версия от Pandas отстала совсем несильно, поэтому возьмем более сложный кейс. Определим новые функции:
# возводим значения строки в квадрат и берем их среднее
def square_mean(row):
row = np.power(row, 2)
return np.mean(row)
# применение:
# df['new_col'] = df.apply(square_mean, axis=1)
# numba не умеет работать с примитивами pandas (Dataframe, Series и тд.)
# поэтому мы даем ей двумерный массив numpy
@numba.njit
def square_mean_numba(arr):
res = np.empty(arr.shape[0])
arr = np.power(arr, 2)
for i in range(arr.shape[0]):
res = np.mean(arr)
return res
# применение:
# df['new_col'] = square_mean_numba(df.to_numpy())
Построим график зависимости времени вычислений от количества строк в датафрейме:
Итоги
- Возможно добиться ускорения в тысячи раз
- Иногда нужно переписать код, чтобы использовать Numba
- Можно использовать далеко не везде, в основном для оптимизации математических операций
- Стоит учесть, что Numba поддерживает не все возможности
You must be registered for see linksиYou must be registered for see links
Multiprocessing
Первое, что приходит в голову, когда встает задача обработать большую пачку данных, это конечно же распараллелить все вычисления. В этот раз мы обойдемся без каких-либо сторонних библиотек, используя только средства python.
В качестве примера будем использовать обработку текста. Ниже я взял
You must be registered for see links
с новостными заголовками. Как и в прошлый раз, мы попытаемся ускорить метод apply:df = pd.read_csv('abcnews-date-text.csv', header=0)
# увеличиваю датасет в 10 раз, добавляя копии в конец
df = pd.concat([df] * 10)
df.head()
| publish_date | headline_text |
|---|



