Как вы распараллеливаете apply () на кадрах данных Pandas, использующих все ядра на одной машине?

по состоянию на август 2017 года, панды DataFame.apply () к сожалению, все еще ограничивается работой с одним ядром, что означает, что многоядерная машина будет тратить большую часть своего вычислительного времени при запуске df.apply(myfunc, axis=1).

Как вы можете использовать все свои ядра для запуска apply на фрейме данных параллельно?

2 ответов


самый простой способ-это использовать ДАСК по. Вам нужен этот импорт (вам нужно будет pip install dask):

import pandas as pd
import dask.dataframe as dd
from dask.multiprocessing import get

и синтаксис

data = <your_pandas_dataframe>
ddata = dd.from_pandas(data, npartitions=30)

def myfunc(x,y,z, ...): return <whatever>

res = ddata.map_partitions(lambda df: df.apply((lambda row: myfunc(*row)), axis=1)).compute(get=get)  

(Я считаю, что 30-это подходящее количество разделов, если у вас есть 16 ядер). Просто для полноты я приурочил разницу на своей машине (16 ядер):

data = pd.DataFrame()
data['col1'] = np.random.normal(size = 1500000)
data['col2'] = np.random.normal(size = 1500000)

ddata = dd.from_pandas(data, npartitions=30)
def myfunc(x,y): return y*(x**2+1)
def apply_myfunc_to_DF(df): return df.apply((lambda row: myfunc(*row)), axis=1)
def pandas_apply(): return apply_myfunc_to_DF(data)
def dask_apply(): return ddata.map_partitions(apply_myfunc_to_DF).compute(get=get)  
def vectorized(): return myfunc(data['col1'], data['col2']  )

t_pds = timeit.Timer(lambda: pandas_apply())
print(t_pds.timeit(number=1))

28.16970546543598

t_dsk = timeit.Timer(lambda: dask_apply())
print(t_dsk.timeit(number=1))

2.708152851089835

t_vec = timeit.Timer(lambda: vectorized())
print(t_vec.timeit(number=1))

0.010668013244867325

дав фактор 10 ускорение переход от панд применяется к dask применяется на разделах. Конечно, если у вас есть функция, вы можете векторизовать, вы должны - в этом случае функция (y*(x**2+1)) это тривиально векторизовать, но есть много вещей, которые невозможно собрать.


можно использовать swifter пакет:

pip install swifter

он работает как плагин для панд, что позволяет повторно использовать :

import swifter

def some_function(data):
    return data * 10

data['out'] = data['in'].swifter.apply(some_function)

он автоматически определит наиболее эффективный способ распараллеливания функции, независимо от того, является ли она векторизованной (как в приведенном выше примере) или нет.

примеры и a сравнение производительности доступны на GitHub. Обратите внимание, что пакет находится под активным развития, поэтому API может измениться.