Je recherche une carte parallèle simple basée sur des processus pour python, c'est-à-dire une fonction
parmap(function,[data])
cela fonctionnerait sur chaque élément de [data] sur un processus différent (enfin, sur un noyau différent, mais autant que je sache, le seul moyen d'exécuter des tâches sur différents cœurs en python est de démarrer plusieurs interpréteurs) et de renvoyer une liste de résultats .
Est-ce que quelque chose comme ça existe? Je voudrais quelque chose simple, donc un module simple serait Nice. Bien sûr, si cela n'existe pas, je me contenterai d'une grande bibliothèque: - /
Il me semble que ce dont vous avez besoin est la méthode map en multitraitement.Pool () :
map (func, iterable [ chunksize])
A parallel equivalent of the map() built-in function (it supports only one iterable argument though). It blocks till the result is ready. This method chops the iterable into a number of chunks which it submits to the process pool as separate tasks. The (approximate) size of these chunks can be specified by setting chunksize to a positive integ
Par exemple, si vous voulez mapper cette fonction:
def f(x):
return x**2
pour aller dans la plage (10), vous pouvez le faire en utilisant la fonction map () intégrée:
map(f, range(10))
ou en utilisant la méthode map () d'un objet multiprocessing.Pool ():
import multiprocessing
pool = multiprocessing.Pool()
print pool.map(f, range(10))
Je sais que ceci est un ancien post, mais juste au cas où, j’ai écrit un outil pour rendre ce super, super facile appelé parmapper (j’appelle en réalité parmap dans mon utilisation mais le nom a été pris).
Il gère une grande partie de la configuration et de la déconstruction des processus et ajoute des tonnes de fonctionnalités. En ordre d'importance
Cela n'engendre qu'un faible coût, mais pour la plupart des utilisations, c'est négligeable.
J'espère que tu trouves cela utile.
(Remarque: comme map
dans Python 3+, il retourne un itératif. Par conséquent, si vous vous attendez à ce que tous les résultats le traversent immédiatement, utilisez list()
.)
Cela peut être fait élégamment avec Ray , un système qui vous permet de paralléliser et de distribuer facilement votre code Python.
Pour paralléliser votre exemple, vous devez définir votre fonction de carte avec le décorateur @ray.remote
, puis l’appeler avec .remote
. Cela garantira que chaque instance de la fonction distante sera exécutée dans un processus différent.
import time
import ray
ray.init()
# Define the function you want to apply map on, as remote function.
@ray.remote
def f(x):
# Do some work...
time.sleep(1)
return x*x
# Define a helper parmap(f, list) function.
# This function executes a copy of f() on each element in "list".
# Each copy of f() runs in a different process.
# Note f.remote(x) returns a future of its result (i.e.,
# an identifier of the result) rather than the result itself.
def parmap(f, list):
return [f.remote(x) for x in list]
# Call parmap() on a list consisting of first 5 integers.
result_ids = parmap(f, range(1, 6))
# Get the results
results = ray.get(result_ids)
print(results)
Cela va imprimer:
[1, 4, 9, 16, 25]
et il se terminera approximativement par len(list)/p
(arrondi à l'entier le plus proche), où p
est le nombre de cœurs de votre machine. En supposant une machine à 2 cœurs, notre exemple s’exécutera en 5/2
arrondi, c’est-à-dire en environ 3
sec.
L'utilisation de Ray sur le module multitraitement présente de nombreux avantages. En particulier, le code same s'exécutera sur une seule machine ainsi que sur un cluster de machines. Pour plus d’avantages de Ray, voir cet article connexe .
Pour ceux qui recherchent l’équivalent Python de mclapply () de R, voici mon implémentation. C'est une amélioration des deux exemples suivants:
Il peut être appliqué aux fonctions de mappage avec un ou plusieurs arguments.
import numpy as np, pandas as pd
from scipy import sparse
import functools, multiprocessing
from multiprocessing import Pool
num_cores = multiprocessing.cpu_count()
def parallelize_dataframe(df, func, U=None, V=None):
#blockSize = 5000
num_partitions = 5 # int( np.ceil(df.shape[0]*(1.0/blockSize)) )
blocks = np.array_split(df, num_partitions)
pool = Pool(num_cores)
if V is not None and U is not None:
# apply func with multiple arguments to dataframe (i.e. involves multiple columns)
df = pd.concat(pool.map(functools.partial(func, U=U, V=V), blocks))
else:
# apply func with one argument to dataframe (i.e. involves single column)
df = pd.concat(pool.map(func, blocks))
pool.close()
pool.join()
return df
def square(x):
return x**2
def test_func(data):
print("Process working on: ", data.shape)
data["squareV"] = data["testV"].apply(square)
return data
def vecProd(row, U, V):
return np.sum( np.multiply(U[int(row["obsI"]),:], V[int(row["obsJ"]),:]) )
def mProd_func(data, U, V):
data["predV"] = data.apply( lambda row: vecProd(row, U, V), axis=1 )
return data
def generate_simulated_data():
N, D, nnz, K = [302, 184, 5000, 5]
I = np.random.choice(N, size=nnz, replace=True)
J = np.random.choice(D, size=nnz, replace=True)
vals = np.random.sample(nnz)
sparseY = sparse.csc_matrix((vals, (I, J)), shape=[N, D])
# Generate parameters U and V which could be used to reconstruct the matrix Y
U = np.random.sample(N*K).reshape([N,K])
V = np.random.sample(D*K).reshape([D,K])
return sparseY, U, V
def main():
Y, U, V = generate_simulated_data()
# find row, column indices and obvseved values for sparse matrix Y
(testI, testJ, testV) = sparse.find(Y)
colNames = ["obsI", "obsJ", "testV", "predV", "squareV"]
dtypes = {"obsI":int, "obsJ":int, "testV":float, "predV":float, "squareV": float}
obsValDF = pd.DataFrame(np.zeros((len(testV), len(colNames))), columns=colNames)
obsValDF["obsI"] = testI
obsValDF["obsJ"] = testJ
obsValDF["testV"] = testV
obsValDF = obsValDF.astype(dtype=dtypes)
print("Y.shape: {!s}, #obsVals: {}, obsValDF.shape: {!s}".format(Y.shape, len(testV), obsValDF.shape))
# calculate the square of testVals
obsValDF = parallelize_dataframe(obsValDF, test_func)
# reconstruct prediction of testVals using parameters U and V
obsValDF = parallelize_dataframe(obsValDF, mProd_func, U, V)
print("obsValDF.shape after reconstruction: {!s}".format(obsValDF.shape))
print("First 5 elements of obsValDF:\n", obsValDF.iloc[:5,:])
if __== '__main__':
main()