https://numpy.org/doc/1.18/reference/generated/numpy.vectorize.html
Ce tutoriel mentionne que l'implémentation de vectorize est essentiellement une boucle for. Mais pour autant que je sache, une fonction vectorisée utilisera
SIMD, est-il donc exact de dire que l'implémentation de numpy.vectorize est essentiellement une boucle for? Si c'est vrai, c'est donc plus rapide que la fonction non vectorisée uniquement parce que c'est une boucle implémentée en langage C?
Merci d'avance.
Oui. Dans le contexte des langages de programmation de tableaux numériques interprétés comme Python (avec numpy) et MATLAB ™, nous utilisons souvent la «vectorisation» pour désigner le remplacement des boucles explicites dans le langage de programmation interprété par une fonction (ou un opérateur) qui prend en charge tous les boucle logique en interne. Dans numpy, les ufunc
implémentent cette logique. Ceci n'est pas lié à l'utilisation de la "vectorisation" pour se référer à l'utilisation d'instructions CPU SIMD qui calculent sur plusieurs entrées simultanément, sauf qu'elles utilisent toutes les deux une métaphore similaire: elles sont comme leurs homologues "scalaires", mais effectuent le calcul sur plusieurs valeurs d'entrée avec une seule invocation.
Avec numpy.vectorize()
, il n'y a généralement pas beaucoup d'avantages en termes de vitesse par rapport à la boucle Python explicite for
. Le point principal est de transformer la fonction Python en un ufunc
, qui implémente toute la sémantique de diffusion et traite donc toutes les tailles d'entrées. La fonction Python qui est "vectorisée" prend toujours la plupart du temps, ainsi que la conversion de la valeur brute de chaque élément en un objet Python à transmettre à la fonction. Vous ne vous attendriez pas à ce que np.vectorize(lambda x, y: x + y)
soit aussi rapide que ufunc np.add
, qui est C à la fois dans la boucle et dans le contenu de la boucle.
Merci pour votre explication détaillée. Mais pour être clair, permettez-moi de prendre un exemple.
import pandas as pd
import numpy as np
df = pd.DataFrame({'a': range(100000), 'b': range(1, 1000001)})
# method1
df.loc[:, 'c'] = df.apply(lambda x: x['a'] + x['b'], axis=1)
# method2
df.loc[:, 'c'] = np.vectorize(lambda x, y: x + y)(df['a'], df['b'])
# method3
df.loc[:, 'c'] = np.add(df['a'], df['b'])
donc avec votre explication, je suppose
méthode | boucle en C | contenu de la boucle en C | utiliser SIMD
- | - | - | -
1 | × | × | ×
2 | √ | × | ×
3 | √ | √ | √
Droite?
np.add
est plus rapide que np.vectorize(lambda x, y: x + y)
car cela évite de convertir des doubles C en objets Python et la surcharge d'appel de la fonction Python. Il est possible qu'il utilise également des instructions SIMD, selon que vous ayez ou non
np.add
est plus rapide quenp.vectorize(lambda x, y: x + y)
car cela évite de convertir des doubles C en objets Python et la surcharge d'appel de la fonction Python. Il est possible qu'il utilise _aussi_ les instructions SIMD, selon que vous ayez ou non
J? ai compris. Merci.
Vous pouvez utiliser les vectorize
de numba pour produire des ufuncs qui fonctionnent en parallèle sans frais généraux Python:
https://numba.pydata.org/numba-doc/latest/user/vectorize.html
Commentaire le plus utile
Oui. Dans le contexte des langages de programmation de tableaux numériques interprétés comme Python (avec numpy) et MATLAB ™, nous utilisons souvent la «vectorisation» pour désigner le remplacement des boucles explicites dans le langage de programmation interprété par une fonction (ou un opérateur) qui prend en charge tous les boucle logique en interne. Dans numpy, les
ufunc
implémentent cette logique. Ceci n'est pas lié à l'utilisation de la "vectorisation" pour se référer à l'utilisation d'instructions CPU SIMD qui calculent sur plusieurs entrées simultanément, sauf qu'elles utilisent toutes les deux une métaphore similaire: elles sont comme leurs homologues "scalaires", mais effectuent le calcul sur plusieurs valeurs d'entrée avec une seule invocation.Avec
numpy.vectorize()
, il n'y a généralement pas beaucoup d'avantages en termes de vitesse par rapport à la boucle Python explicitefor
. Le point principal est de transformer la fonction Python en unufunc
, qui implémente toute la sémantique de diffusion et traite donc toutes les tailles d'entrées. La fonction Python qui est "vectorisée" prend toujours la plupart du temps, ainsi que la conversion de la valeur brute de chaque élément en un objet Python à transmettre à la fonction. Vous ne vous attendriez pas à ce quenp.vectorize(lambda x, y: x + y)
soit aussi rapide que ufuncnp.add
, qui est C à la fois dans la boucle et dans le contenu de la boucle.