web-dev-qa-db-fra.com

comment paralléliser de nombreuses comparaisons de chaînes (floues) à l'aide de apply in Pandas?

J'ai le problème suivant

J'ai une base de données master qui contient des phrases telles que

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

Pour chaque ligne du maître, je cherche dans un autre Dataframe slave la meilleure correspondance en utilisant fuzzywuzzy. J'utilise fuzzywuzzy parce que les phrases correspondantes entre les deux images pourraient être légèrement différentes (caractères supplémentaires, etc.).

Par exemple, slave pourrait être

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

Voici un exemple de travail compact, fonctionnel et merveilleux:

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))

La question de 1 million de dollars est: puis-je paralléliser mon code d'application ci-dessus?

Après tout, chaque ligne de master est comparée à toutes les lignes de slave (esclave est un petit jeu de données et je peux conserver de nombreuses copies des données dans la RAM).

Je ne vois pas pourquoi je ne pouvais pas exécuter plusieurs comparaisons (c'est-à-dire traiter plusieurs lignes en même temps).

Problème: je ne sais pas comment faire cela ou si c'est même possible.

Toute aide grandement appréciée!

17
ℕʘʘḆḽḘ

Vous pouvez paralléliser cela avec 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

De plus, vous devriez penser aux compromis entre l'utilisation de threads et de processus ici. Votre correspondance de chaîne fuzzy ne publie probablement pas le GIL, vous ne tirerez donc aucun avantage de l'utilisation de plusieurs threads. Cependant, l'utilisation de processus entraînera la sérialisation et le déplacement des données sur votre ordinateur, ce qui pourrait ralentir un peu les choses. 

Vous pouvez expérimenter entre l'utilisation de threads et de processus et un système distribué en gérant l'argument de mot clé get= dans la méthode compute().

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
26
MRocklin

Je travaille sur quelque chose de similaire et je voulais fournir une solution de travail plus complète à quiconque pourrait tomber sur cette question. @MRocklin a malheureusement des erreurs de syntaxe dans les extraits de code fournis. Je ne suis pas un expert de Dask, je ne peux donc pas commenter certaines considérations relatives aux performances, mais ceci devrait vous permettre d'accomplir votre tâche, comme l'a suggéré @MRocklin. Ceci utilise Dask version 0.17.2 et Pandas version 0.22.0 :

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'))

Ensuite, obtenez vos résultats (comme dans cette session d'interprétation):

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    
1
shellcat_zero