3.2. Fonctions vectorisées¶
Les fonctions vectorisées sont des fonctions qui calculent automatiquement sur des tableaux, point par point. La plus part des opérateurs de python fonctionne sur des tableaux.
Opérateurs mathématiques. On peut tout utiliser (comme les puissances ou modulos…).
def arctan(x, N_max=100): """ Calcule arctan par le développement limité""" n = np.arange(N_max) return np.sum((-1)**n * x**(2*n+1)/(2*n+1)) print(arctan(1), np.arctan(1))
0.782898225889638 0.7853981633974483
Opérateurs logiques:
a = np.array([1, 2, 5, 3, 5]) b = np.array([0, 2, 3, 7, 5]) print(a==b) print(a>b) print( (a>b) | (a<b) ) print(a!=b) print(~(a==b)) print((a>b) & ((a%2)==1)) # nb impaires
[False True False False True] [ True False True False False] [ True False True True False] [ True False True True False] [ True False True True False] [ True False True False False]
Avertissement
Ça n’a pas de sens d’utiliser les fonctions
and
ouor
. Rappelons que ce sont desif
implicites. Et dire, par exemple, « Si mon tableau est positif » n’a pas de sens. Seules les phrases du type : « si tous les élements sont positifs » ou « si au moins un élément est positif » ont un sens. C’est ce que le message d’erreur suivant dit :if (a>b): print(a)
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-4-bdbbc477d411> in <module> ----> 1 if (a>b): 2 print(a) ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Fonctions mathématiques: Les fonctions de bases sont dans le module
numpy
. Les fonctions spéciales peuvent se retrouver dans le modulescipy.special
Exemple : calcul de l”éclairement d’une tache d’Airy
from scipy.special import jv def eclairement_airy(theta, d=1E-4, lamb=632E-9): """ Eclairement d'un disque """ x = np.sin(theta)*d/lamb return (2*jv(1, np.pi*x)/(np.pi*x))**2 print(eclairement_airy(theta=2E-3))
0.7769307179570368
Fonctions définie par l’utilisateur. Il n’y a rien de magique : une fonction sera vectorisée, si sont contenu l’est.
def position(x0, v0, t, g=9.81): """ Calcule la position d'un point après un temps de chute """ return x0 + v0*t + g*t**2/2 N = 10 sigma_x = 100E-6 sigma_v = 10E-3 t = 100E-3 x0 = np.random.normal(sigma_x, size=(N,3)) v0 = np.random.normal(sigma_v, size=(N,3)) xf = position(x0, v0, t)
Par contre, le code suivant va créer une erreur :
def valeur_absolue(x): if x>=0: return x else: return -x print(valeur_absolue(-1)) print(valeur_absolue(np.array([-1, 1])))
1
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-7-9d3584a1f876> in <module> 6 7 print(valeur_absolue(-1)) ----> 8 print(valeur_absolue(np.array([-1, 1]))) <ipython-input-7-9d3584a1f876> in valeur_absolue(x) 1 def valeur_absolue(x): ----> 2 if x>=0: 3 return x 4 else: 5 return -x ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Si une fonction fonctionne avec des nombres scalaires, il est possible de la vectoriser automatiquement grâce à la fonction
np.vectorize
valeur_absolue_vec = np.vectorize(valeur_absolue) print(valeur_absolue_vec(np.array([-1, 1])))
[1 1]
Cette fonction va automatiser la création de la boucle
for
pour l’utilisateur. Elle sera lente comparée au fonction vectorisée native de numpy. On peut le voir en utilisant la commande “magic” de IPython%timeit
x = np.random.normal(size=100000) %timeit -n 10 np.abs(x) %timeit -n 10 valeur_absolue_vec(x)
36.5 µs ± 15.3 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
12.3 ms ± 623 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Il est possible d’utiliser une décorateur pour créer directement une fonction vectorisée :
@np.vectorize def my_abs(x): if x>=0: return x else: return -x