NumPy
Contents
NumPy#
NumPy (Numerical Python) is “the fundamental package for scientific computing with Python”.
Le site officiel est : https://numpy.org/
Il faut tout d’abord installer la librairie. Si vous travaillez avec Anaconda, c’est très facile car elle est déjà installée. Sinon il est possible de l’installer via pip install numpy
. Ensuite, il faut l’importer. Il est coutume d’utiliser l’alias np
pour la NumPy.
import numpy as np
Le type de base est un “N-dimensional array”. Pour créer un ndarray
qui contient les chiffres de 1 à 5, nous pouvons écrire:
x = np.array([1, 2, 3, 4, 5])
print(x)
[1 2 3 4 5]
type(x)
numpy.ndarray
A première vue, rien ne le distingue d’une liste. Le code ci-dessous va nous permettre d’y voir plus clair:
my_list = [1, 2, 3, 4, 5]
double_list = my_list * 2
print(double_list)
my_array = np.array([1, 2, 3, 4, 5])
double_array = my_array * 2
print(double_array)
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
[ 2 4 6 8 10]
Comme on peut le constater, lorsqu’on multiplie une liste par 2, les différents éléments de la liste sont dupliqués. Les chiffres de 1 à 5 apparaissent donc deux fois. Par contre, lorsqu’on multiplie un ndarray
par 2, chaque élément au sein est multiplié par 2. Pour rappel, il est toutefois possible de multiplier chaque élément d’une liste par 2 à l’aide de:
my_list = [1, 2, 3, 4, 5]
double_list = [2*x for x in my_list]
print(double_list)
[2, 4, 6, 8, 10]
Rapidité d’exécution#
Un autre avantage important de NumPy est sa rapidité. Prenons l’exemple ci-dessous dans lequel nous créons une liste ou un ndarray
de 10,000,000 d’éléments. Ensuite, nous multiplions chaque élément par 2.
Note 1: pour comparer uniquement la vitesse de création de la liste de valeurs, nous n’imprimons pas cette liste (car cela nécessite également du temps).
Note 2: %%timeit
fonctionne dans un environement IPython (Interactive Python). Pour plus d’informations, vous pouvez consulter :
Le premier script ci-dessous initialise une liste de 10 millions d’éléments ainsi qu’une liste vide. Ensuite, le script parcourt l’entièreté de la première liste, multiplie chaque élément par 2 et insère cette valeur dans la deuxième liste (celle qui était vide au départ).
%%timeit
my_list = list(range(10_000_000))
double_list = []
for item in my_list:
double_list.append(2*item)
# print(double_list[:10])
1.44 s ± 276 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Une possibilité pour améliorer la rapidité d’exécution du code est de le vectoriser, c’est-à-dire que l’opération “multiplier par 2” est appliqué sur l’entièreté du vecteur. Les compréhensions de liste permettent d’arriver à ce résultat.
%%timeit
my_list = list(range(10_000_000))
double_list = [2*x for x in my_list]
# print(double_list[:10])
764 ms ± 16.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Les deux exemples précédents utilisent les listes. Le script ci-dessous utilise le ndarray
de NumPy. La diminution du temps nécessaire à accomplir la tâche est significative.
%%timeit
my_array = np.arange(10_000_000)
double_array = my_array * 2
#print(double_array[:10])
30.1 ms ± 785 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Fonctions statistiques#
NumPy contient également une liste importante de fonctions statistiques très utiles. Pour la plupart des fonctions ci-dessous, le nom est sufisamment explicite pour comprendre l’utilité de la fonction:
x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(np.sum(x))
print(np.mean(x))
print(np.min(x))
print(np.max(x))
print(np.median(x))
print(np.std(x)) # standard deviation = ecart-type
print(np.std(x, ddof=1)) # standard deviation with 1 delta degree of freedom
print(np.var(x)) # variance
print(np.var(x, ddof=1)) # variance with 1 delta degree of freedom
print(np.quantile(x, 0.25)) # first quartile = premier quartile
print(np.quantile(x, 0.75)) # third quartile = troisieme quartile
55
5.5
1
10
5.5
2.8722813232690143
3.0276503540974917
8.25
9.166666666666666
3.25
7.75
math vs NumPy#
Si nous prenons le cas de la fonction exponentielle, \(f(x) = e^x\), les modules math
et NumPy
permettent d’obtenir la valeur. La différence principale réside dans le fait que la fonction math.exp()
ne fonctionne qu’avec un scalaire alors que np.exp()
peut être appliqué soit sur un scalaire, soit sur un vecteur.
import math
%%timeit
x = list(range(1, 100))
y = [math.exp(x) for x in x]
22.8 µs ± 426 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%%timeit
x = np.arange(1, 100)
y = np.exp(x)
3.65 µs ± 98.5 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
Il en est de même pour la fonction racine carré, \(f(x) = \sqrt{x}\).
%%timeit
x = list(range(1, 10000))
y = [math.sqrt(x) for x in x]
2.77 ms ± 74.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit
x = np.arange(1, 10000)
y = np.sqrt(x)
24.2 µs ± 2.22 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
Array multidimensionnel#
Jusqu’à présent, nous avons principalement considéré des vecteurs. NumPy permet également de travailler avec des array
multidimensionnel, notamment des array
de dimension 2, ce qui correspond d’un point de vue mathématique à une matrice.
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
Tout ce que nous venons de découvrir sur les vecteurs peut donc également s’appliquer aux matrices. Prenons l’exemple de la fonction somme:
np.sum(matrix)
45
En spécifiant en paramètre axis=0
, la fonction somme s’appliquera colonne par colonne.
np.sum(matrix, axis=0)
array([12, 15, 18])
De même, en spécifiant axis=1
, la fonction somme s’appliquera ligne par ligne.
np.sum(matrix, axis=1)
array([ 6, 15, 24])
Fonctions spécifiques aux matrices#
Le module NumPy contient un sous-module spécifiquement dédié à l’algèbre linéaire. Ce sous-module s’appelle linalg
. Pour accéder à une fonction de ce sous-module, par exemple det
qui calcule le déterminant d’une matrice, il faut écrire:
matrix = np.array([[1, 2],
[3, 4]])
np.linalg.det(matrix)
-2.0000000000000004
Une autre façon d’avoir accès à la fonction det
est d’utiliser:
from numpy.linalg import det
matrix = np.array([[1, 2],
[3, 4]])
det(matrix)
-2.0000000000000004