web-dev-qa-db-ja.com

1台のマシン上のすべてのコアを利用するPandasデータフレームでapply()を並列化するにはどうすればよいですか?

2017年8月の時点で、Pandas DataFame.apply() は、残念ながらまだシングルコアでの動作に制限されています。つまり、マルチコアマシンはその計算の大半を無駄にします。 df.apply(myfunc, axis=1)を実行する時間。

すべてのコアを使用して、データフレームで適用を並行して実行するにはどうすればよいですか?

57
Roko Mijic

最も簡単な方法は、 Dask's map_partitions を使用することです。これらのインポートが必要です(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)  

(16コアの場合、30がパーティションの適切な数であると考えています)。完全を期すために、マシン(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

pandas applyからパーティションへのdask applyに行く10倍のスピードアップファクターを与える。もちろん、ベクトル化できる関数がある場合、この場合、関数(y*(x**2+1))は簡単にベクトル化されますが、ベクトル化できないものはたくさんあります。

70
Roko Mijic

swifter パッケージを使用できます:

pip install swifter

パンダのプラグインとして機能し、apply関数を再利用できます。

import swifter

def some_function(data):
    return data * 10

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

(上記の例のように)ベクトル化されているかどうかに関係なく、関数を並列化する最も効率的な方法を自動的に見つけ出します。

その他の例 および パフォーマンス比較 はGitHubで利用可能です。パッケージは活発に開発されているため、APIが変更される可能性があることに注意してください。

39
slhck

代わりにpandarallelを試すことができます:すべてのCPUでpandas操作を並列化するためのシンプルで効率的なツール(LinuxおよびmacOS)

  • 並列化にはコストがかかる(新しいプロセスのインスタンス化、共有メモリを介したデータの送信など)ため、並列化は、並列化する計算量が十分に大きい場合にのみ効率的です。非常に少ないデータ量で、常に価値があるとは限らない視差を使用します。
  • 適用される関数は、ラムダ関数であってはなりません。
from pandarallel import pandarallel
from math import sin

pandarallel.initialize()

# FORBIDDEN
df.parallel_apply(lambda x: sin(x**2), axis=1)

# ALLOWED
def func(x):
    return sin(x**2)

df.parallel_apply(func, axis=1)

https://github.com/nalepae/pandarallel を参照してください

10
G_KOBELIEF

ネイティブpythonにとどまる場合:

import multiprocessing as mp

pool = mp.Pool(mp.cpu_count())
df['newcol'] = pool.map(f, df['col'])
pool.terminate()
pool.join()

関数fをデータフレームcolの列dfに並列に適用します

2

Sklearnベースのトランスフォーマーの例を次に示します。pandas applyは並列化されています

import multiprocessing as mp
from sklearn.base import TransformerMixin, BaseEstimator

class ParllelTransformer(BaseEstimator, TransformerMixin):
    def __init__(self,
                 n_jobs=1):
        """
        n_jobs - parallel jobs to run
        """
        self.variety = variety
        self.user_abbrevs = user_abbrevs
        self.n_jobs = n_jobs
    def fit(self, X, y=None):
        return self
    def transform(self, X, *_):
        X_copy = X.copy()
        cores = mp.cpu_count()
        partitions = 1

        if self.n_jobs <= -1:
            partitions = cores
        Elif self.n_jobs <= 0:
            partitions = 1
        else:
            partitions = min(self.n_jobs, cores)

        if partitions == 1:
            # transform sequentially
            return X_copy.apply(self._transform_one)

        # splitting data into batches
        data_split = np.array_split(X_copy, partitions)

        pool = mp.Pool(cores)

        # Here reduce function - concationation of transformed batches
        data = pd.concat(
            pool.map(self._preprocess_part, data_split)
        )

        pool.close()
        pool.join()
        return data
    def _transform_part(self, df_part):
        return df_part.apply(self._transform_one)
    def _transform_one(self, line):
        # some kind of transformations here
        return line

詳細については、「 https://towardsdatascience.com/4-easy-steps-to-improve-your-machine-learning-code-performance-88a0b0eeffa8 」を参照してください

1
Maxim Balatsko