web-dev-qa-db-ja.com

パンダで適用を使用して多くの(ファジー)文字列比較を並列化する方法は?

次の問題があります

次のような文を含むデータフレームmasterがあります

master
Out[8]: 
                  original
0  this is a Nice sentence
1      this is another one
2    stackoverflow is Nice

マスターのすべての行について、fuzzywuzzyを使用して別のデータフレームslaveを検索し、最も一致するものを探します。 2つのデータフレーム間で一致する文が少し異なる可能性があるため(余分な文字など)、私はfuzzywuzzyを使用します。

たとえば、slave

slave
Out[10]: 
   my_value                      name
0         2               hello world
1         1           congratulations
2         2  this is a Nice sentence 
3         3       this is another one
4         1     stackoverflow is Nice

これは、完全に機能する、素晴らしい、コンパクトな作業例です:)

from fuzzywuzzy import fuzz
import pandas as pd
import numpy as np
import difflib


master= pd.DataFrame({'original':['this is a Nice sentence',
'this is another one',
'stackoverflow is Nice']})


slave= pd.DataFrame({'name':['hello world',
'congratulations',
'this is a Nice sentence ',
'this is another one',
'stackoverflow is Nice'],'my_value': [2,1,2,3,1]})

def fuzzy_score(str1, str2):
    return fuzz.token_set_ratio(str1, str2)

def helper(orig_string, slave_df):
    #use fuzzywuzzy to see how close original and name are
    slave_df['score'] = slave_df.name.apply(lambda x: fuzzy_score(x,orig_string))
    #return my_value corresponding to the highest score
    return slave_df.ix[slave_df.score.idxmax(),'my_value']

master['my_value'] = master.original.apply(lambda x: helper(x,slave))

100万ドルの質問は、上記の適用コードを並列化できますか?

結局、masterのすべての行がslaveのすべての行と比較されます(スレーブは小さなデータセットであり、RAMにデータの多くのコピーを保持できます)。

複数の比較を実行できなかった理由がわかりません(つまり、複数の行を同時に処理できません)。

問題:どうすればいいのか、それが可能かどうかわからない。

どんな助けも大歓迎です!

20

これはDask.dataframeと並列化できます。

_>>> dmaster = dd.from_pandas(master, npartitions=4)
>>> dmaster['my_value'] = dmaster.original.apply(lambda x: helper(x, slave), name='my_value'))
>>> dmaster.compute()
                  original  my_value
0  this is a Nice sentence         2
1      this is another one         3
2    stackoverflow is Nice         1
_

さらに、ここでスレッドとプロセスの使用のトレードオフについて考える必要があります。あいまいな文字列の照合ではほぼ確実にGILが解放されないため、複数のスレッドを使用してもメリットはありません。ただし、プロセスを使用すると、データがシリアル化されてマシン内を移動するため、状況が少し遅くなる可能性があります。

compute()メソッドへの_get=_キーワード引数を管理することにより、スレッドとプロセス、または分散システムの使用を試すことができます。

_import dask.multiprocessing
import dask.threaded

>>> dmaster.compute(get=dask.threaded.get)  # this is default for dask.dataframe
>>> dmaster.compute(get=dask.multiprocessing.get)  # try processes instead
_
28
MRocklin

私は似たようなものに取り組んでおり、この質問に出くわす可能性のある他の人のために、より完全な作業ソリューションを提供したいと考えていました。 @MRocklinは、残念ながら、提供されているコードスニペットに構文エラーがあります。私はDaskの専門家ではないので、パフォーマンスの考慮事項についてコメントすることはできませんが、@ MRocklinが提案したように、これでタスクを完了することができます。これはDaskバージョン0.17.2Pandasバージョン0.22.を使用しています:

import dask.dataframe as dd
import dask.multiprocessing
import dask.threaded
from fuzzywuzzy import fuzz
import pandas as pd

master= pd.DataFrame({'original':['this is a Nice sentence',
'this is another one',
'stackoverflow is Nice']})

slave= pd.DataFrame({'name':['hello world',
'congratulations',
'this is a Nice sentence ',
'this is another one',
'stackoverflow is Nice'],'my_value': [1,2,3,4,5]})

def fuzzy_score(str1, str2):
    return fuzz.token_set_ratio(str1, str2)

def helper(orig_string, slave_df):
    slave_df['score'] = slave_df.name.apply(lambda x: fuzzy_score(x,orig_string))
    #return my_value corresponding to the highest score
    return slave_df.loc[slave_df.score.idxmax(),'my_value']

dmaster = dd.from_pandas(master, npartitions=4)
dmaster['my_value'] = dmaster.original.apply(lambda x: helper(x, slave),meta=('x','f8'))

次に、結果を取得します(このインタープリターセッションと同様)。

In [6]: dmaster.compute(get=dask.multiprocessing.get)                                             
Out[6]:                                          
                  original  my_value             
0  this is a Nice sentence         3             
1      this is another one         4             
2    stackoverflow is Nice         5    
2
shellcat_zero