Slim Essid : slim.essid@telecom-paristech.fr
Alexandre Gramfort : alexandre.gramfort@telecom-paristech.fr
adapté du travail de J.R. Johansson (robert@riken.jp) http://dml.riken.jp/~rob/
# et JUSTE POUR MOI (pour avoir les figures dans le notebook)
%matplotlib inline
numpy
est un module utilisé dans presque tous les projets de calcul numérique sous Pythonnumpy
est écrit en C et en Fortran d'où ses performances élevées lorsque les calculs sont vectorisés (formulés comme des opérations sur des vecteurs/matrices)matplotlib
est un module performant pour la génération de graphiques en 2D et 3DPour utiliser numpy
et matplotlib
il faut commencer par les importer :
import numpy as np
import matplotlib.pyplot as plt
On peut plus simplement faire :
from numpy import *
from matplotlib.pyplot import *
numpy
¶Dans la terminologie numpy
, vecteurs, matrices et autres tenseurs sont appelés arrays.
# un vecteur: l'argument de la fonction est une liste Python
v = np.array([1, 3, 2, 4])
print v
print type(v)
On peut alors visualiser ces données :
v = np.array([1, 3, 2, 4])
x = np.array([0, 1, 2, 3])
plt.figure()
plt.plot(x,v, 'rv--', label='v(x)')
plt.legend(loc='lower right')
plt.xlabel('x')
plt.ylabel('v')
plt.title('Mon titre')
plt.xlim([-1, 4])
plt.ylim([0, 5])
plt.show()
plt.savefig('toto.png')
On peut omettre show()
, lorsque la méthode ion()
a été appelée ; c'est le cas dans Spyder et pylab
Pour définir une matrice (array de dimension 2 pour numpy):
# une matrice: l'argument est une liste emboitée
M = np.array([[1, 2], [3, 4]])
print M
M[0, 0]
Les objets v
et M
sont tous deux du type ndarray
(fourni par numpy
)
type(v), type(M)
v
et M
ne diffèrent que par leur taille, que l'on peut obtenir via la propriété shape
:
v.shape
M.shape
Pour obtenir le nombre d'éléments d'un array :
v.size
M.size
On peut aussi utiliser numpy.shape
et numpy.size
np.shape(M)
Les arrays ont un type qu'on obtient via dtype
:
print(M)
print(M.dtype)
Les types doivent être respectés lors d'assignations à des arrays
M[0,0] = "hello"
a = np.array([1,2,3])
a[0] = 3.2
print a
a.dtype
a = np.array([1,2,3], dtype=np.int64)
b = np.array([2,2,3], dtype=np.int64)
b = b.astype(float)
print a / b
On peut définir le type de manière explicite en utilisant le mot clé dtype
en argument:
M = np.array([[1, 2], [3, 4]], dtype=complex)
M
Autres types possibles avec dtype
: int
, float
, complex
, bool
, object
, etc.
On peut aussi spécifier la précision en bits: int64
, int16
, float128
, complex128
.
# create a range
x = np.arange(0, 10, 2) # arguments: start, stop, step
x
x = np.arange(-1, 1, 0.1)
x
# avec linspace, le début et la fin SONT inclus
np.linspace(0, 10, 25)
np.linspace(0, 10, 11)
xx = np.linspace(-10, 10, 100)
plt.plot(xx, np.sin(xx))
print(np.logspace(0, 10, 10, base=np.e))
x, y = np.mgrid[0:5, 0:5]
x
y
xx, yy = np.mgrid[-50:50, -50:50]
plt.imshow(np.angle(xx + 1j*yy))
plt.axis('on')
plt.colorbar()
plt.figure()
plt.imshow(np.abs(xx + 1j*yy))
plt.axis('on')
plt.colorbar()
from numpy import random
# tirage uniforme dans [0,1]
random.rand(5,5) # ou np.random.rand
# tirage suivant une loi normale standard
random.randn(5,5)
Affichage de l'histogramme des tirages
a = random.randn(10000)
hh = plt.hist(a, 40)
# une matrice diagonale
np.diag([1,2,3])
# diagonale avec décalage par rapport à la diagonale principale
np.diag([1,2,3], k=1)
np.zeros((3,), dtype=int) # attention zeros(3,3) est FAUX
np.ones((3,3))
print(np.zeros((3,), dtype=int))
print(np.zeros((1, 3), dtype=int))
print(np.zeros((3, 1), dtype=int))
Un format fichier classique est le format CSV (comma-separated values), ou bien TSV (tab-separated values). Pour lire de tels fichiers utilisez numpy.genfromtxt
. Par exemple:
!cat data.csv
data = np.genfromtxt('data.csv', delimiter=',')
data
data.shape
A l'aide de numpy.savetxt
on peut enregistrer un array numpy
dans un fichier txt:
M = random.rand(3,3)
M
np.savetxt("random-matrix.txt", M)
!cat random-matrix.txt
#!type random-matrix.txt
np.savetxt("random-matrix.csv", M, fmt='%.5f', delimiter=',') # fmt spécifie le format
!cat random-matrix.csv
#!type random-matrix.csv
Pour sauvegarder et recharger des array numpy
: numpy.save
et numpy.load
:
np.save("random-matrix.npy", M)
!cat random-matrix.npy
np.load("random-matrix.npy")
numpy
¶M
M.dtype
M.itemsize # octets par élément
M.nbytes # nombre d'octets
M.nbytes / M.size
M.ndim # nombre de dimensions
print(np.zeros((3,), dtype=int).ndim)
print(np.zeros((1, 3), dtype=int).ndim)
print(np.zeros((3, 1), dtype=int).ndim)
# v est un vecteur, il n'a qu'une seule dimension -> un seul indice
v[0]
# M est une matrice, ou un array à 2 dimensions -> deux indices
M[1,1]
Contenu complet :
M
La deuxième ligne :
M[1]
On peut aussi utiliser :
M[1,:] # 2 ème ligne (indice 1)
M[:,1] # 2 ème colonne (indice 1)
print(M.shape)
print(M[1,:].shape, M[:,1].shape)
On peut assigner des nouvelles valeurs à certaines cellules :
M[0,0] = 1
M
# on peut aussi assigner des lignes ou des colonnes
M[1,:] = -1
# M[1,:] = [1, 2, 3]
M
Slicing fait référence à la syntaxe M[start:stop:step]
pour extraire une partie d'un array :
A = np.array([1,2,3,4,5])
A
A[1:3]
Les tranches sont modifiables :
A[1:3] = [-2,-3]
A
On peut omettre n'importe lequel des argument dans M[start:stop:step]
:
A[::] # indices de début, fin, et pas avec leurs valeurs par défaut
A[::2] # pas = 2, indices de début et de fin par défaut
A[:3] # les trois premiers éléments
A[3:] # à partir de l'indice 3
M = np.arange(12).reshape(4, 3)
print M
On peut utiliser des indices négatifs :
A = np.array([1,2,3,4,5])
A[-1] # le dernier élément
A[-3:] # les 3 derniers éléments
Le slicing fonctionne de façon similaire pour les array multi-dimensionnels
A = np.array([[n+m*10 for n in range(5)] for m in range(5)])
A
A[1:4, 1:4] # sous-tableau
# sauts
A[::2, ::2]
A
A[[0, 1, 3]]
Lorsque qu'on utilise des listes ou des array pour définir des tranches :
row_indices = [1, 2, 3]
print(A)
print(A[row_indices])
A[[1, 2]][:, [3, 4]] = 0 # ATTENTION !
print(A)
print(A[[1, 2], [3, 4]])
A[np.ix_([1, 2], [3, 4])] = 0
print(A)
On peut aussi utiliser des masques binaires :
B = np.arange(5)
B
row_mask = np.array([True, False, True, False, False])
print(B[row_mask])
print(B[[0,2]])
# de façon équivalente
row_mask = np.array([1,0,1,0,0], dtype=bool)
B[row_mask]
# ou encore
a = np.array([1, 2, 3, 4, 5])
print(a < 3)
print(B[a < 3])
print(A)
print(A[:, a < 3])
Un masque binaire peut être converti en indices de positions avec where
x = np.arange(0, 10, 0.5)
print(x)
mask = (x > 5) * (x < 7.5)
print(mask)
indices = np.where(mask)
indices
x[indices] # équivalent à x[mask]
Extraire la diagonale ou une sous-diagonale d'un array :
print(A)
np.diag(A)
np.diag(A, -1)
La performance des programmes écrit en Python/Numpy dépend de la capacité à vectoriser les calculs (les écrire comme des opérations sur des vecteurs/matrices) en évitant au maximum les boucles for/while
On peut effectuer les opérations arithmétiques habituelles pour multiplier, additionner, soustraire et diviser des arrays avec/par des scalaires :
v1 = np.arange(0, 5)
print(v1)
v1 * 2
v1 + 2
plt.figure()
plt.subplot(1,2,1)
plt.plot(v1 ** 2,'g--', label='$y = x^2$')
plt.legend(loc=0)
plt.subplot(1,2,2)
plt.plot(sqrt(v1), 'r*-', label='$y = \sqrt{x}$')
plt.legend(loc=2)
plt.show()
A = np.array([[n+m*10 for n in range(5)] for m in range(5)])
print(A)
print(A * 2)
print(A + 2)
C = random.rand(300,200)
plt.figure()
plt.imshow(C)
plt.colorbar()
plt.show()
Les opérations par défaut sont des opérations terme-à-terme :
A = np.array([[n+m*10 for n in range(5)] for m in range(5)])
print(A)
A * A # multiplication terme-à-terme
(A + A.T) / 2
print v1
print v1 * v1
En multipliant des arrays de tailles compatibles, on obtient des multiplications terme-à-terme par ligne :
A.shape, v1.shape
print(A)
print(v1)
print(A * v1)
Sans utiliser de boucles (for/while
) :
Comment faire des multiplications de matrices ? Deux façons :
dot
; (recommandé)matrix
. (à éviter)print(A.shape)
print(A)
print(type(A))
print(np.dot(A, A)) # multiplication matrice
print(A * A) # multiplication élément par élément
A.dot(v1)
np.dot(v1, v1)
Avec le type matrix
de Numpy
M = np.matrix(A)
v = np.matrix(v1).T # en faire un vecteur colonne
M * v
# produit scalaire
v.T * v
# avec les objets matrices, c'est les opérations standards sur les matrices qui sont appliquées
v + M*v
Si les dimensions sont incompatibles on provoque des erreurs :
v = np.matrix([1,2,3,4,5,6]).T
np.shape(M), np.shape(v)
M * v
Voir également les fonctions : inner
, outer
, cross
, kron
, tensordot
. Utiliser par exemple help(kron)
.
.T
a été utilisé pour transposer l'objet matrice v
transpose
Autres transformations :
C = np.matrix([[1j, 2j], [3j, 4j]])
C
np.conjugate(C)
Transposée conjuguée :
C.H
Parties réelles et imaginaires :
np.real(C) # same as: C.real
np.imag(C) # same as: C.imag
Argument et module :
np.angle(C + 1)
np.abs(C)
Numpy propose des fonctions pour calculer certaines statistiques des données stockées dans des arrays :
data = np.vander([1, 2, 3, 4])
print(data)
print(data.shape)
# np.mean(data)
print(np.mean(data, axis=0))
# la moyenne de la troisième colonne
np.mean(data[:,2])
np.var(data[:,2]), np.std(data[:,2])
data[:,2].min()
data[:,2].max()
data[:,2].sum()
data[:,2].prod()
d = np.arange(0, 10)
d
# somme des éléments
np.sum(d)
ou encore :
d.sum()
# produit des éléments
np.prod(d + 1)
# somme cumulée
np.cumsum(d)
# produit cumulé
np.cumprod(d + 1)
# équivalent à diag(A).sum()
np.trace(data)
en utilisant l'indexation ou n'importe quelle méthode d'extraction de donnés à partir des arrays
data
np.unique(data[:,1])
mask = data[:,1] == 4
np.mean(data[mask,3])
Pour appliquer min
, max
, etc., par lignes ou colonnes :
m = random.rand(3,4)
m
# max global
m.max()
# max dans chaque colonne
m.max(axis=0)
# max dans chaque ligne
m.max(axis=1)
Plusieurs autres méthodes des classes array
et matrix
acceptent l'argument (optional) axis
keyword argument.
Pour des raisons de performance Python ne copie pas automatiquement les objets (par exemple passage par référence des paramètres de fonctions).
A = np.array([[0, 2],[ 3, 4]])
A
B = A
# changer B affecte A
B[0,0] = 10
B
A
B = A
print B is A
Pour éviter ce comportement, on peut demander une copie profonde (deep copy) de A
dans B
# B = np.copy(A)
B = A.copy()
# maintenant en modifiant B, A n'est plus affecté
B[0,0] = -5
B
A # A est aussi modifié !
print(A - A[:,0]) # FAUX
print(A - A[:,0].reshape((2, 1))) # OK
A
n, m = A.shape
B = A.reshape((1, n * m))
B
B[0,0:5] = 5 # modifier l'array
B
A
La variable originale est aussi modifiée ! B n'est qu'une nouvelle vue de A.
Pour transformer un array multi-dimmensionel en un vecteur. Mais cette fois-ci, une copie des données est créée :
B = A.flatten()
B
B[0:5] = 10
B
A # A ne change pas car B est une copie de A
newaxis
¶par exemple pour convertir un vecteur en une matrice ligne ou colonne :
v = np.array([1,2,3])
np.shape(v)
# créer une matrice à une colonne à partir du vectuer v
v[:, np.newaxis]
v[:, np.newaxis].shape
# matrice à une ligne
v[np.newaxis,:].shape
En utilisant les fonctions repeat
, tile
, vstack
, hstack
, et concatenate
, on peut créer des vecteurs/matrices plus grandes à partir de vecteurs/matrices plus petites :
a = np.array([[1, 2], [3, 4]])
a
# répéter chaque élément 3 fois
np.repeat(a, 3) # résultat 1-d
# on peut spécifier l'argument axis
np.repeat(a, 3, axis=1)
Pour répéter la matrice, il faut utiliser tile
# répéter la matrice 3 fois
np.tile(a, 3)
b = np.array([[5, 6]])
np.concatenate((a, b), axis=0)
np.concatenate((a, b.T), axis=1)
np.vstack((a,b))
np.hstack((a,b.T))
v = np.array([1,2,3,4])
for element in v:
print element
M = np.array([[1,2], [3,4]])
for row in M:
print "row", row
for element in row:
print element
Pour obtenir les indices des éléments sur lesquels on itère (par exemple, pour pouvoir les modifier en même temps) on peut utiliser enumerate
:
for row_idx, row in enumerate(M):
print "row_idx", row_idx, "row", row
for col_idx, element in enumerate(row):
print "col_idx", col_idx, "element", element
# update the matrix M: square each element
M[row_idx, col_idx] = element ** 2
# chaque élément de M a maintenant été élevé au carré
M
Losqu'on s'intéresse à des conditions sur tout on une partie d'un array, on peut utiliser any
ou all
:
M
if (M > 5).any():
print("au moins un élément de M est plus grand que 5")
else:
print("aucun élément de M n'est plus grand que 5")
if (M > 5).all():
print("tous les éléments de M sont plus grands que 5")
else:
print("tous les éléments de M sont plus petits que 5")
On peut créer une vue d'un autre type que l'original pour un array
M = array([[-1,2], [0,4]])
M.dtype
M2 = M.astype(float)
M2
M2.dtype
M3 = M.astype(bool)
M3