Vous êtes sur la page 1sur 21

Lycée Malherbe Année scolaire 2018-2019 MPSI - Informatique pour tous

TP no 9 - Tableaux numpy (Corrigé)


Entamons maintenant la partie ”Ingénierie numérique et simulation” du programme d’IPT de MPSI.

Dans les prochains TP, nous allons utiliser des modules Python contenant des commandes bien plus
avancées que précédemment :
§ numpy contient de nombreuses commandes de calcul numérique, notamment matriciel ;
§ matplotlib contient des outils graphiques ;
§ scipy contient divers outils scientifiques.

Importez le module numpy. L’usage est de l’importer sous le nom np. Entrez donc

import numpy as np
Toutes les commandes contenues dans le module seront donc accessibles sous la forme np.commande

Les objets manipulés par numpy sont des tableaux (array).

Les tableaux numpy ressemblent beaucoup aux listes Python. Seulement, ils sont plus performants,
et sont bien plus commodes à manipuler.
De la même façon qu’on peut utiliser des listes de listes, des listes de listes de listes, etc., il est possible
d’utiliser des tableaux numpy à plusieurs dimensions. Avec des listes ou des tableaux à une dimension,
on peut représenter des vecteurs. Et avec des listes de listes, ou des tableaux à deux dimensions, on peut
représenter des matrices. Vecteurs et matrices seront vus en temps voulu en cours de maths de MPSI !
Pour ce TP, dites-vous simplement qu’une matrice est un tableau (de nombres pour nous).

Dans ce TP, nous allons apprendre à définir, manipuler, transformer, etc., les tableaux numpy, à une
ou plusieurs dimensions.

1 Définir et modifier un tableau


Définissons deux tableaux :
T=np.array([1,3,7,9]) ; M=np.array([[1,2],[3,4],[5,6],[7,8]])

1 Quel est le type des objets T et M ainsi définis ?


Utiliser la commande type.
1 >>> type(T)
2 <class ’numpy.ndarray’>

Vous savez déjà comment accéder à une ligne ou à un élément d’une matrice représentée par une liste
de listes. . .
1 >>> M2 = [[1,2],[3,4],[5,6],[7,8]]
2 >>> M2[1] # la deuxième ligne
3 [3, 4]
4 >>> M2[0][1] # l’élément ligne 1 colonne 2
5 2
6 >>> M2[0,1] # incorrect

1
7 Traceback (most recent call last):
8 File "<stdin>", line 1, in <module>
9 TypeError: list indices must be integers, not tuple
Une deuxième syntaxe est possible avec les tableaux numpy, qui prendra toute son importance dans
la suite du TP au moment où on traitera le slicing.
1 >>> M[1] # la deuxième ligne
2 array([3, 4])
3 >>> M[0][1] # l’élément ligne 1 colonne 2
4 2
5 >>> M[0,1] # une deuxième syntaxe possible
6 2
Un tableau est un objet mutable, c’est-à-dire que l’on peut modifier un élément en affectant direc-
tement T[i,j].

2 Tentez de modifier des éléments d’une matrice existante.


Histoire d’écrire un corrigé complet. . . mais aucune difficulté !
1 >>> M[2,1]=1234
2 >>> M
3 array([[ 1, 2],
4 [ 3, 4],
5 [ 5, 1234],
6 [ 7, 8]])

3 Testez np.zeros((2,3)), np.ones((4,2)), np.eye(4), np.diag([1,4,-3,7,9]). Pourquoi des


doubles-parenthèses dans les deux premières ?
1 >>> np.zeros((2,3))
2 array([[ 0., 0., 0.],
3 [ 0., 0., 0.]])
4 >>> np.ones((4,2))
5 array([[ 1., 1.],
6 [ 1., 1.],
7 [ 1., 1.],
8 [ 1., 1.]])
9 >>> np.eye(4)
10 array([[ 1., 0., 0., 0.],
11 [ 0., 1., 0., 0.],
12 [ 0., 0., 1., 0.],
13 [ 0., 0., 0., 1.]])
14 >>> np.diag([1,4,-3,7,9])
15 array([[ 1, 0, 0, 0, 0],
16 [ 0, 4, 0, 0, 0],
17 [ 0, 0, -3, 0, 0],
18 [ 0, 0, 0, 7, 0],
19 [ 0, 0, 0, 0, 9]])
np.zeros((2,3)) donne la matrice nulle de taille 2 ˆ 3.
np.ones((4,2)) donne la matrice “Attila” de taille 4 ˆ 2.
Doubles-parenthèses, car les fonctions n’attendent pas deux arguments, mais bien un seul argument,

2
qui est un couple d’entiers.
np.eye(4) donne la matrice diagonale identité de taille 4 (souvent noté I4 et I se prononce “aille” en
anglais. . . ).
np.diag([1,4,7,9]) donne une matrice diagonale.

Remarque : on peut aussi donner un argument entier (un seul !) à zeros et ones.

1 import numpy as np
2 print(np.zeros(100))
3 print(np.ones(50))
 

4 Devinez la matrice obtenue par les commandes suivantes, et ensuite vérifiez.



1 A = np.zeros((4,5))
2 for i in range(4):
3 for j in range(5):
4 A[i,j]=j+100*i
 

1 [[ 0. 1. 2. 3. 4.]
2 [ 100. 101. 102. 103. 104.]
3 [ 200. 201. 202. 203. 204.]
4 [ 300. 301. 302. 303. 304.]]
N “ pni,j q, avec ni,j “ j ´ 1 ` 100pi ´ 1q en numérotant à partir de 1 comme on le fait d’habitude

5 Comment aurait-on pu définir le tableau de la question précédente grâce aux listes définies en
compréhension ?
1 >>> np.array([[j+100*i for j in range(5)] for i in range(4)])
2 array([[ 0, 1, 2, 3, 4],
3 [100, 101, 102, 103, 104],
4 [200, 201, 202, 203, 204],
5 [300, 301, 302, 303, 304]])
¨ ˛
1 1 1 1
˚ 2 3 4 5
˚ ‹

˚ ‹
˚ ‹
˚ ‹
˚ ‹
˚ 2 2 2 2 ‹
6 Créez le plus simplement possible la matrice : A “ ˚ ‹.
˚ 3 4 5 6
˚ ‹

˚ ‹
˚ ‹
˚ ‹
˚ ‹
˝ 3 3 3 3 ‚
4 5 6 7
Voici 3 solutions, avec utilisation de listes définies en compréhension.
1 >>> np.array( [[ (i+1)/(i+j+2) for j in range(4) ] for i in range(3) ] )
2 array([[ 0.5 , 0.33333333, 0.25 , 0.2 ],
3 [ 0.66666667, 0.5 , 0.4 , 0.33333333],
4 [ 0.75 , 0.6 , 0.5 , 0.42857143]])
5 >>> np.array( [[ i/(i+j) for j in range(1,5) ] for i in range(1,4) ] )
6 array([[ 0.5 , 0.33333333, 0.25 , 0.2 ],

3
7 [ 0.66666667, 0.5 , 0.4 , 0.33333333],
8 [ 0.75 , 0.6 , 0.5 , 0.42857143]])
9 >>> np.array ( [[ (i+1)/j for j in range(i+2,i+6) ] for i in range(3) ])
10 array([[ 0.5 , 0.33333333, 0.25 , 0.2 ],
11 [ 0.66666667, 0.5 , 0.4 , 0.33333333],
12 [ 0.75 , 0.6 , 0.5 , 0.42857143]])
On aurait aussi pu procéder de façon plus classique, avec deux boucles imbriquées :

1 import numpy as np
2 M = np.zeros((3,4))
3 for i in range(3):
4 for j in range(4):
5 M[i,j] = (i+1)/(i+j+2)
6 print(M)
 


7 Créer simplement le tableau de tous les nombres cos pour k allant de 0 à 90.
180
1 >>> from math import pi,cos
2 >>> np.array( [ cos(k*pi/180) for k in range(91) ] )
3 array([ 1.00000000e+00, 9.99847695e-01, 9.99390827e-01,
4 9.98629535e-01, 9.97564050e-01, 9.96194698e-01,
5 9.94521895e-01, 9.92546152e-01, 9.90268069e-01,
6 9.87688341e-01, 9.84807753e-01, 9.81627183e-01,
7 9.78147601e-01, 9.74370065e-01, 9.70295726e-01,
8 9.65925826e-01, 9.61261696e-01, 9.56304756e-01,
9 9.51056516e-01, 9.45518576e-01, 9.39692621e-01,
10 9.33580426e-01, 9.27183855e-01, 9.20504853e-01,
11 9.13545458e-01, 9.06307787e-01, 8.98794046e-01,
12 8.91006524e-01, 8.82947593e-01, 8.74619707e-01,
13 8.66025404e-01, 8.57167301e-01, 8.48048096e-01,
14 8.38670568e-01, 8.29037573e-01, 8.19152044e-01,
15 8.09016994e-01, 7.98635510e-01, 7.88010754e-01,
16 7.77145961e-01, 7.66044443e-01, 7.54709580e-01,
17 7.43144825e-01, 7.31353702e-01, 7.19339800e-01,
18 7.07106781e-01, 6.94658370e-01, 6.81998360e-01,
19 6.69130606e-01, 6.56059029e-01, 6.42787610e-01,
20 6.29320391e-01, 6.15661475e-01, 6.01815023e-01,
21 5.87785252e-01, 5.73576436e-01, 5.59192903e-01,
22 5.44639035e-01, 5.29919264e-01, 5.15038075e-01,
23 5.00000000e-01, 4.84809620e-01, 4.69471563e-01,
24 4.53990500e-01, 4.38371147e-01, 4.22618262e-01,
25 4.06736643e-01, 3.90731128e-01, 3.74606593e-01,
26 3.58367950e-01, 3.42020143e-01, 3.25568154e-01,
27 3.09016994e-01, 2.92371705e-01, 2.75637356e-01,
28 2.58819045e-01, 2.41921896e-01, 2.24951054e-01,
29 2.07911691e-01, 1.90808995e-01, 1.73648178e-01,
30 1.56434465e-01, 1.39173101e-01, 1.21869343e-01,
31 1.04528463e-01, 8.71557427e-02, 6.97564737e-02,
32 5.23359562e-02, 3.48994967e-02, 1.74524064e-02,
33 6.12323400e-17])

4
Bien sûr, on aurait pu définir un tableau avec que des 0, et modifier les cases ensuite. . .

8 ‹ Vous verrez en cours de maths que la matrice de Vandermonde associée au n-uplet pa1 , ¨ ¨ ¨ , an q
est définie par A “ aj´1
` ˘
i 1ďi,jďn
. Ecrire une fonction vandermonde(A) qui renvoie le tableau numpy
représentant la matrice de Vandermonde associée à la liste de nombres A“ pa1 , ¨ ¨ ¨ , an q de longueur n.

1 import numpy as np
2 from random import randrange
3
4 def vandermonde(A):
5 n=len(A)
6 return np.array ( [ [ A[i] ** j for j in range(n) ] for i in range(n) ] )
7
8 # en parcourant A par valeurs
9 def vandermonde2(A):
10 n=len(A)
11 return np.array ( [ [ a ** j for j in range(n) ] for a in A ] )
12
13
14 if __name__=="__main__":
15 print(vandermonde([1,2,3]))
16 print(vandermonde2([1,2,3]))
17 print(vandermonde([1,2,-3,-10]))
18 print(vandermonde2([1,2,-3,-10]))
 

1 [[1 1 1]
2 [1 2 4]
3 [1 3 9]]
4 [[1 1 1]
5 [1 2 4]
6 [1 3 9]]
7 [[ 1 1 1 1]
8 [ 1 2 4 8]
9 [ 1 -3 9 -27]
10 [ 1 -10 100 -1000]]
11 [[ 1 1 1 1]
12 [ 1 2 4 8]
13 [ 1 -3 9 -27]
14 [ 1 -10 100 -1000]]

9 Essayez np.random.rand(3,6), np.random.randint(2,6,(3,7))


et np.random.uniform(-1,3,(2,5)).
np.random.rand(3,6) donne un tableau à 3 lignes et 6 colonnes avec des nombres aléatoires entre 0
et 1.
np.random.randint(2,6,(3,7)) donne un tableau à 3 lignes et 7 colonnes avec des nombres entre 2
inclus et 6 exclu.
np.random.uniform(-1,3,(2,5)) donne un tableau à 2 lignes et 5 colonnes avec des nombres
aléatoires entre 1 et 3.

5
On pourrait aussi utiliser l’opérateur * de concaténation de listes.
1 >>> A = np.array( [ [0] * 8 ] * 10 )
Une petite subtilité avec les listes (voir TP sur les listes) : chaque ligne est la copie d’un unique
pointeur.
1 >>> B = [ [0] * 8 ] * 10
2 >>> B[0][0]=1000
3 >>> B
4 [[1000, 0, 0, 0, 0, 0, 0, 0], [1000, 0, 0, 0, 0, 0, 0, 0], [1000, 0, 0, 0,
0, 0, 0, 0], [1000, 0, 0, 0, 0, 0, 0, 0], [1000, 0, 0, 0, 0, 0, 0, 0],
[1000, 0, 0, 0, 0, 0, 0, 0], [1000, 0, 0, 0, 0, 0, 0, 0], [1000, 0, 0, 0,
0, 0, 0, 0], [1000, 0, 0, 0, 0, 0, 0, 0], [1000, 0, 0, 0, 0, 0, 0, 0]]
Mais cette subtilité n’existe pas avec les tableaux numpy.
1 >>> A[0,0]=1000
2 >>> A
3 array([[1000, 0, 0, 0, 0, 0, 0, 0],
4 [ 0, 0, 0, 0, 0, 0, 0, 0],
5 [ 0, 0, 0, 0, 0, 0, 0, 0],
6 [ 0, 0, 0, 0, 0, 0, 0, 0],
7 [ 0, 0, 0, 0, 0, 0, 0, 0],
8 [ 0, 0, 0, 0, 0, 0, 0, 0],
9 [ 0, 0, 0, 0, 0, 0, 0, 0],
10 [ 0, 0, 0, 0, 0, 0, 0, 0],
11 [ 0, 0, 0, 0, 0, 0, 0, 0],
12 [ 0, 0, 0, 0, 0, 0, 0, 0]])

2 Taille d’un tableau

10 La commande len existe toujours... On sait à quoi s’attendre pour la longueur d’un tableau à une
dimension. Qu’obtient-on pour la longueur d’un tableau à deux dimensions ? trois dimensions ?
1 >>> V = np.random.randint( -10, 10, 3 )
2 >>> len(V)
3 3
4 >>> M = np.random.randint( -10, 10, (3,5) )
5 >>> len(M)
6 3
7 >>> U = np.random.randint( -10, 10, (3,5,2) )
8 >>> len(U)
9 3
La longueur d’un tableau à une dimension est son nombre d’éléments ; la longueur d’une matrice est
son nombre de lignes ; la longueur d’un tableau à 3 dimensions est égale à la taille correspondant à la
dimension la plus “externe”.

11 Essayez les commandes np.size et np.shape.


1 >>> np.size(V)
2 3

6
3 >>> np.shape(V)
4 (3,)
5 >>> np.size(M)
6 15
7 >>> np.shape(M)
8 (3, 5)
9 >>> np.size(U)
10 30
11 >>> np.shape(U)
12 (3, 5, 2)
np.size renvoie le nombre de coefficients dans un tableau.
np.shape renvoie un objet de type tuple (n-uplet).
§ Sur un tableau à une dimension, np.shape donne un tuple à un élément formé du nombre de
coordonnées.
§ Avec une matrice, np.shape donne un couple formé du nombre de lignes d’abord, et du nombre
de colonnes après.
§ Avec un tableau à 3 dimension, np.shape donne un triplet.

12 Comment accéder au nombre de lignes d’une matrice ? Et au nombre de colonnes ?



1 import numpy as np
2 A = np.random.random((6,9))
3 l,c = np.shape(A) # déconstruction d’un couple
4 print("nombre de lignes de A:",l," ou bien autrement: ",np.shape(A)[0])
5 print("nombre de colonnes de A:",c," ou bien autrement: ",np.shape(A)[1])
 

1 nombre de lignes de A: 6 ou bien autrement: 6
2 nombre de colonnes de A: 9 ou bien autrement: 9
Il existe bien une commande append dans le module numpy, que nous étudierons pas ici. Mais ne
vous y trompez pas : la commande append du module numpy renvoie une copie agrandie de la liste,
contrairement à la commande append pour les listes (que vous devez connaı̂tre), et qui fonctionne de
manière un peu plus subtile, avec une bonne complexité (en fait, les listes Python sont des tableaux
redimensionnables. . . ).
Et ne vous y trompez pas. . . vstack et hstack présentés dans la suite du TP ne font que créer
un nouveau tableau en recopiant les éléments des tableaux concaténés (tout comme l’opérateur + de
concaténation de listes).
À retenir : la forme d’un tableau numpy est définie à sa création, et ne peut pas être
modifiée ensuite.

Pour aller plus loin ‹‹ :


1 >>> D = np.array( [[1,2,3],[3,4]])
2 >>> E = np.random.randint(-10,10,(3,4,5))
3 >>> np.size(D)
4 2
5 >>> np.shape(D)
6 (2,)
7 >>> np.size(E)
8 60
9 >>> np.shape(E)
10 (3, 4, 5)

7
D n’est PAS une matrice (il faudrait qu’il y ait le même nombre de colonnes à chaque ligne), et Python
la voit comme un tableau uni-dimensionnel. Et E est un tableau tri-dimensionnel (on pourrait mettre les
coefficients dans un parallélépipède).

3 Slicing
Grâce au slicing, on peut découper une liste, et ainsi, sélectionner un ensemble de lignes d’une matrice.
Avec les tableaux numpy, on peut faire mieux. Par exemple, comment sélectionner la deuxième colonne
(la colonne d’indice 1) d’une matrice ? Avec les listes, on pouvait faire :

1 L=[[1,2,3,4],[5,6,7,8],[9,10,11,12]]
2 print([L[i][1] for i in range(len(L))])
3 print([l[1] for l in L])
 

Avec les tableaux numpy, on peut bien calquer la syntaxe, mais ce n’est guère plus réjouissant.

1 T=np.array(L)
2 print([T[i][1] for i in range(len(T))])
3 print([l[1] for l in T])
 

Autrement, on peut faire beaucoup mieux !

13 Après avoir défini M=np.random.randint(-100,100,(9,9)), essayez M, M[1:4,3:5], M[1:3,3:6],


M[3:,2], M[0,:2], M[:,:-2] et M[:,:] (je vous invite à deviner ce qui va se passer avant !).
1 >>> M = np.random.randint(-100,100,(9,9))
2 >>> M
3 array([[ 98, -42, -67, 57, -90, 85, 35, 67, -30],
4 [ 61, 70, -9, 68, 24, -65, 62, -89, -10],
5 [ -4, 98, 54, 4, 56, -58, -64, -47, -5],
6 [ 4, -84, -85, 71, -80, 12, 77, 83, -62],
7 [ 23, 35, -94, 2, 32, 10, 69, 61, -97],
8 [ -2, -52, 91, -49, -27, 5, -98, 67, -76],
9 [ 63, -2, 41, 79, 80, 60, 36, 64, 20],
10 [ 12, -51, -38, -70, 12, -5, 75, -75, -51],
11 [-96, 28, 56, -4, -79, -51, -58, -82, -91]])
12 >>> M[1:4,3:5]
13 array([[ 68, 24],
14 [ 4, 56],
15 [ 71, -80]])
16 >>> M[1:3,3:6]
17 array([[ 68, 24, -65],
18 [ 4, 56, -58]])
19 >>> M[3:,2]
20 array([-85, -94, 91, 41, -38, 56])
21 >>> M[0,:2]
22 array([ 98, -42])
23 >>> M[:,:-2]
24 array([[ 98, -42, -67, 57, -90, 85, 35],
25 [ 61, 70, -9, 68, 24, -65, 62],
26 [ -4, 98, 54, 4, 56, -58, -64],

8
27 [ 4, -84, -85, 71, -80, 12, 77],
28 [ 23, 35, -94, 2, 32, 10, 69],
29 [ -2, -52, 91, -49, -27, 5, -98],
30 [ 63, -2, 41, 79, 80, 60, 36],
31 [ 12, -51, -38, -70, 12, -5, 75],
32 [-96, 28, 56, -4, -79, -51, -58]])
33 >>> M[:,:]
34 array([[ 98, -42, -67, 57, -90, 85, 35, 67, -30],
35 [ 61, 70, -9, 68, 24, -65, 62, -89, -10],
36 [ -4, 98, 54, 4, 56, -58, -64, -47, -5],
37 [ 4, -84, -85, 71, -80, 12, 77, 83, -62],
38 [ 23, 35, -94, 2, 32, 10, 69, 61, -97],
39 [ -2, -52, 91, -49, -27, 5, -98, 67, -76],
40 [ 63, -2, 41, 79, 80, 60, 36, 64, 20],
41 [ 12, -51, -38, -70, 12, -5, 75, -75, -51],
42 [-96, 28, 56, -4, -79, -51, -58, -82, -91]])
Il faut avoir bien compris tout ça !

14 Comparez et comprenez M[1:2,3:4], M[1,3:4], M[1:2,3], et M[1,3].


1 >>> M = np.random.randint(-100,100,(9,9))
2 >>> M
3 array([[-14, 40, -6, -65, -88, -79, 21, -82, 78],
4 [ 58, -84, -57, -30, 6, -64, 29, -8, -72],
5 [-45, 17, 66, 19, 97, 37, 98, -52, -93],
6 [ 88, -92, -14, -36, 55, -53, -75, 3, 80],
7 [-86, 17, 18, 7, -59, -88, 37, -21, -11],
8 [ 87, -60, 14, 1, -82, -6, -14, -24, 96],
9 [ 89, -39, -24, -52, -79, -26, -13, -97, 12],
10 [-66, 42, -10, 99, -91, 70, 54, 12, -47],
11 [-65, -83, 81, -20, -23, -83, 95, -86, 10]])
12 >>> M[1:2,3:4]
13 array([[-30]])
14 >>> M[1,3:4]
15 array([-30])
16 >>> M[1:2,3]
17 array([-30])
18 >>> M[1,3]
19 -30
Chaque fois qu’on sélectionne une ligne ou une colonne (et non une tranche), la tableau perd une
dimension. . . A comparer avec :
1 >>> L = [1,2,3]
2 >>> L[2:3]
3 [3]
4 >>> L[2]
5 3

15 Comment obtenir la matrice M privée de sa dernière ligne ? et de ses deux premières colonnes ?
Simple application de ce qui précède.

9

1 import numpy as np
2 M = np.random.randint(1,10,(6,7))
3 print("M",M)
4 print("M privé de sa dernière ligne",M[:-1,:])
5 print("M privé de ses deux premières colonnes",M[:,2:])
 

16 On peut aussi affecter un sous-tableau. Essayez


1 >>> M = np.random.randint(-3,3,(5,5))
2 >>> M
3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
4 >>> M[2:4,:3]=[[1000,1000,1000],[1000,1000,1000]]
5 >>> M
6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
7 >>> M[2:4,:3]=9999
8 >>> M
9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

1 >>> M = np.random.randint(-3,3,(5,5))
2 >>> M
3 array([[ 2, 1, 0, 2, -1],
4 [ 0, 0, -2, -1, -2],
5 [-3, -1, 0, 0, 1],
6 [-2, 1, 1, 2, -1],
7 [-3, 1, -2, 0, -1]])
8 >>> M[2:4,:3]=[[1000,1000,1000],[1000,1000,1000]]
9 >>> M
10 array([[ 2, 1, 0, 2, -1],
11 [ 0, 0, -2, -1, -2],
12 [1000, 1000, 1000, 0, 1],
13 [1000, 1000, 1000, 2, -1],
14 [ -3, 1, -2, 0, -1]])
15 >>> M[2:4,:3]=9999
16 >>> M
17 array([[ 2, 1, 0, 2, -1],
18 [ 0, 0, -2, -1, -2],
19 [9999, 9999, 9999, 0, 1],
20 [9999, 9999, 9999, 2, -1],
21 [ -3, 1, -2, 0, -1]])

numpy est bien gentil d’accepter la syntaxe M[2:4,:3]=9999. . .

10
17 ‹ Avec les connaissances que vous avez, créez avec légèreté et élégance les matrices :
¨ ˛
˚ 0 0 0 0 0 0 0 ‹
˚ ‹
˚ ‹
¨ ˛ ˚
˚ 0 0 0 0 0 0 0 ‹

˚ 1 1 1 1 ‹ ˚ ‹
˚ ‹
˚
˚


˚
˚ 0 2 2 0 0 0 0 ‹

˚ 1 1 1 1 ‹ ˚ ‹
˚ ‹ ˚ ‹
A“˚
˚

‹ B“˚
˚ 0 2 2 0 0 0 0 ‹

˚ 1 1 1 2 ‹ ˚ ‹
˚ ‹ ˚ ‹
0 0 0 0 0 0 0 ‹
˚ ‹ ˚ ‹
˝ ‚ ˚
1 6 1 1
˚ ‹
˚ ‹
0 0 0 0 0 0 0 ‹
˚ ‹
˚
˚ ‹
˝ ‚
0 0 0 0 0 0 0

Avec des listes définies en compréhension :



1 import numpy as np
2
3 A = np.array([[ 1 for _ in range(4) ] for _ in range(4) ])
4 A[2,3]=2 ; A[3,1]=6
5 print(A)
6
7 B = np.array([[ 0 for _ in range(7) ] for _ in range(7) ])
8 B[2:4,1:3]= 2
9 print(B)
 

ou plus court :

1 import numpy as np
2
3 A = np.array([[ 1 ]*4]*4)
4 A[2,3]=2 ; A[3,1]=6
5 print(A)
6
7 B = np.array([[ 0 ]*7]*7)
8 B[2:4,1:3]= 2
9 print(B)
 

Le plus simple est d’utiliser les fonctions ones et zeros.

1 import numpy as np
2
3 A = np.ones((4,4))
4 A[2,3]=2 ; A[3,1]=6
5 print(A)
6
7 B = np.zeros((7,7))
8 B[2:4,1:3]= 2
9 print(B)
 

11
A noter que dans le 3ème cas, on obtient une matrice de flottants, alors que dans les deux premiers
cas, on obtient des matrices d’entiers.

4 Opérations sur les tableaux


L’intérêt essentiel des tableaux numpy, qu’ils soient à une ou deux dimensions, est que l’on peut
facilement appliquer une fonction à tous les éléments du tableau : ajouter 1 à chaque élément, élever
chaque élément au carré, appliquer la fonction cos à chaque élément, etc.

18 Essayer les commandes suivantes sur une matrice M de votre choix.

M+3 4*M 1/M M**2

1 >>> M = np.random.rand(4,3)
2 >>> M
3 array([[ 0.16090807, 0.53997896, 0.790279 ],
4 [ 0.61971264, 0.98432795, 0.84869875],
5 [ 0.00599074, 0.3947013 , 0.18317299],
6 [ 0.9537028 , 0.45596474, 0.57189268]])
7 >>> M+3
8 array([[ 3.16090807, 3.53997896, 3.790279 ],
9 [ 3.61971264, 3.98432795, 3.84869875],
10 [ 3.00599074, 3.3947013 , 3.18317299],
11 [ 3.9537028 , 3.45596474, 3.57189268]])
12 >>> 4*M
13 array([[ 0.64363229, 2.15991583, 3.161116 ],
14 [ 2.47885057, 3.93731179, 3.39479498],
15 [ 0.02396295, 1.57880521, 0.73269195],
16 [ 3.81481119, 1.82385895, 2.28757072]])
17 >>> 1/M
18 array([[ 6.2147286 , 1.85192402, 1.2653759 ],
19 [ 1.61365112, 1.01592158, 1.17827439],
20 [ 166.9243302 , 2.53356143, 5.45932026],
21 [ 1.04854469, 2.19315205, 1.74857982]])
22 >>> M**2
23 array([[ 2.58914078e-02, 2.91577274e-01, 6.24540896e-01],
24 [ 3.84043758e-01, 9.68901510e-01, 7.20289561e-01],
25 [ 3.58889469e-05, 1.55789119e-01, 3.35523432e-02],
26 [ 9.09549025e-01, 2.07903843e-01, 3.27061236e-01]])
Les opérations sont appliquées à chaque terme.

19 Les tableaux se comportent-ils comme les listes du point de vue des opérations ?
Rien de ce qui est au-dessus ne fonctionne avec les listes.

20 Essayer les commandes suivantes sur deux matrices M et N de même taille de votre choix, telles que
tous les coefficients de N soient non nuls.
M+N M+N+3 M-N M*N M/N
Là encore, les opérations sont effectuées terme à terme.

12
1 >>> M = np.random.uniform(-5,5,(2,3))
2 >>> N = np.random.uniform(1,10,(2,3))
3 >>> M
4 array([[-1.74398607, -2.65079659, -2.17983514],
5 [ 0.89349767, 3.92911842, -3.70227379]])
6 >>> N
7 array([[ 8.25350413, 4.29211985, 8.51696209],
8 [ 8.95485733, 2.27261183, 7.0019683 ]])
9 >>> M+N
10 array([[ 6.50951805, 1.64132326, 6.33712696],
11 [ 9.848355 , 6.20173025, 3.2996945 ]])
12 >>> M+N+3
13 array([[ 9.50951805, 4.64132326, 9.33712696],
14 [ 12.848355 , 9.20173025, 6.2996945 ]])
15 >>> M-N
16 array([[ -9.9974902 , -6.94291644, -10.69679723],
17 [ -8.06135967, 1.6565066 , -10.70424209]])
18 >>> M*N
19 array([[-14.39399626, -11.37753665, -18.56557322],
20 [ 8.00114414, 8.92936101, -25.92320373]])
21 >>> M/N
22 array([[-0.2113025 , -0.61759612, -0.25594045],
23 [ 0.09977799, 1.72889993, -0.52874758]])

21 En supposant en plus que M et N contiennent des entiers, essayez

M+N M//N M%N

1 >>> M = np.random.randint(-50,50,(2,3))
2 >>> N = np.random.randint(1,10,(2,3))
3 >>> M
4 array([[-44, -7, -32],
5 [-16, -14, 38]])
6 >>> N
7 array([[8, 6, 5],
8 [8, 8, 3]])
9 >>> M//N
10 array([[-6, -2, -7],
11 [-2, -2, 12]])
12 >>> M%N
13 array([[4, 5, 3],
14 [0, 2, 2]])
Là encore, opérations terme à terme.
On voudrait pousser un peu plus loin en appliquant des fonctions plus compliquées, comme l’expo-
nentielle.

22 Quel module faut-il importer pour obtenir la fonction exponentielle ? Tentez d’appliquer cette
fonction à un tableau numpy.
Ça ne fonctionne pas avec la fonction exp du module math.

13
1 >>> from math import exp
2 >>> T = np.array([1,3,7,9])
3 >>> M = np.array([[1,2],[3,4],[5,6],[7,8]])
4 >>> exp(T)
5 Traceback (most recent call last):
6 File "<stdin>", line 1, in <module>
7 TypeError: only length-1 arrays can be converted to Python scalars
8 >>> exp(M)
9 Traceback (most recent call last):
10 File "<stdin>", line 1, in <module>
11 TypeError: only length-1 arrays can be converted to Python scalars

23 Avec les connaissances que vous avez, comment appliquer la fonction exponentielle à chaque terme
d’une matrice ?
Avec les listes en compréhension, tout est possible. . .
1 >>> from math import exp
2 >>> [ exp(x) for x in T ]
3 [2.718281828459045, 20.085536923187668, 1096.6331584284585,
8103.083927575384]
4 >>> [[ exp(x) for x in ligne ] for ligne in M ]
5 [[2.718281828459045, 7.38905609893065], [20.085536923187668,
54.598150033144236], [148.4131591025766, 403.4287934927351],
[1096.6331584284585, 2980.9579870417283]]
En fait, numpy dispose de sa propre fonction exponentielle.

24 Réessayez avec cette fonction exponentielle-là.


Cette fois, ça fonctionne ! numpy accepte de travailler avec ses fonctions à lui. . . Pratique, car le
module contient les fonctions usuelles : cos, sin, etc.
1 >>> np.exp(T)
2 array([ 2.71828183e+00, 2.00855369e+01, 1.09663316e+03,
3 8.10308393e+03])
4 >>> np.exp(M)
5 array([[ 2.71828183e+00, 7.38905610e+00],
6 [ 2.00855369e+01, 5.45981500e+01],
7 [ 1.48413159e+02, 4.03428793e+02],
8 [ 1.09663316e+03, 2.98095799e+03]])
Dans le cas précis de l’exponentielle, on aurait pu aussi utiliser la constante e du module math.
1 >>> from math import e
2 >>> e**T
3 array([ 2.71828183e+00, 2.00855369e+01, 1.09663316e+03,
4 8.10308393e+03])
5 >>> e**M
6 array([[ 2.71828183e+00, 7.38905610e+00],
7 [ 2.00855369e+01, 5.45981500e+01],
8 [ 1.48413159e+02, 4.03428793e+02],
9 [ 1.09663316e+03, 2.98095799e+03]])

14
Une autre solution pour “forcer” les choses à fonctionner, est de ”vectorialiser” la fonction qu’on
veut utiliser, c’est-à-dire qu’on en fait une fonction applicable à un tableau numpy. Retenez cette
possibilité.
1 >>> from math import exp
2 >>> np.vectorize(exp)(T)
3 array([ 2.71828183e+00, 2.00855369e+01, 1.09663316e+03,
4 8.10308393e+03])
5 >>> np.vectorize(exp)(M)
6 array([[ 2.71828183e+00, 7.38905610e+00],
7 [ 2.00855369e+01, 5.45981500e+01],
8 [ 1.48413159e+02, 4.03428793e+02],
9 [ 1.09663316e+03, 2.98095799e+03]])
Attention : la somme de deux matrices est bien la somme au sens mathématique que vous
verrez en cours de maths ; mais le produit de matrices (et le carré, le cube), ainsi que l’exponentielle, vus
dans ce TP, sont des opérations terme à terme, et NE correspondent PAS à la définition classique du
produit et de l’exponentielle matricielle des cours de maths. Bien sûr, on peut aussi calculer des produits
matriciels classiques avec numpy. Ceci fera l’objet d’un TP ultérieur. ` ˘
En fait, quand on fait la somme des coefficients de A*B, on définit un produit scalaire : ă A, B ą“ tr t AB
(voir un chapitre de maths de fin d’année).
De même, l’addition d’une matrice carrée avec un nombre M+3 peut être comprise en maths (avec un
abus de notation),¨ comme la somme de ˛ M avec 3 fois la matrice identité.
˚ ´3 8 1 5 ‹
˚ ‹
˚ ‹
˚ 9 6 ´2 3 ‹
˚ ‹
Par exemple ˚
˚
‹ ` 3 peut être compris en maths comme :

˚ 10 1 1 ´9 ‹
˚ ‹
˚ ‹
˝ ‚
5 ´3 9 2
¨ ˛ ¨ ˛ ¨ ˛ ¨ ˛
˚ ´3 8 1 5 ‹ ˚ ´3 8 1 5 ‹ ˚ 1 0 0 0 ‹ ˚ 0 8 1
5 ‹
˚ ‹ ˚ ‹ ˚ ‹ ˚ ‹
˚ ‹ ˚ ‹ ˚ ‹ ˚ ‹
˚ 9 6 ´2 3 ‹ ˚ 9 6 ´2 3 ‹ ˚ 0 1 0 0 ‹ ˚ 9 9 ´2 3 ‹
˚ ‹ ˚ ‹ ˚ ‹ ˚ ‹
˚ ‹ ` 3I4 “ ˚ ‹ ` 3˚ ‹“˚ ‹.
˚ ‹ ˚ ‹ ˚ ‹ ˚ ‹
˚ 10 1 1 ´9 ‹ ˚ 10 1 1 ´9 ‹ 0 0 1 0 ‹ 10 1 4 ´9 ‹
‹ ˚ ‹ ˚ ˚
˚ ˚ ‹ ˚ ‹
˚ ‹ ˚ ‹ ˚ ‹ ˚ ‹
˝ ‚ ˝ ‚ ˝ ‚ ˝ ‚
5 ´3 9 2 5 ´3 9 2 0 0 0 1 5 ´3 9 5

Mais Python le comprend bien différemment, comme on vient de l’expliquer.


Enfin, rappelons qu’avec des listes + et * sont les opérations de concaténation.
1 >>> L = [1,2,3]
2 >>> M = [4,5,6]
3 >>> L+M
4 [1, 2, 3, 4, 5, 6]
5 >>> L*3
6 [1, 2, 3, 1, 2, 3, 1, 2, 3]
7 >>> L2 = np.array(L)
8 >>> M2 = np.array(L)
9 >>> L2+M2

15
10 array([2, 4, 6])
11 >>> L2*3
12 array([3, 6, 9])

5 Types ‹
Les éléments d’un tableau peuvent être a priori de n’importe quel type, mais tous les éléments d’un
même tableau doivent être du même type (c’est une différence majeure avec les listes, où on peut mettre
n’importe quoi, et la contrepartie des bonnes performances). En effet, en machine, un tableau numpy
est une zone mémoire contiguë de données.
Le type des éléments du tableau T est donné par T.dtype.

25 Essayez
1 >>> T = np.array( [ True, False, True, True ])
2 >>> T.dtype
3 xxxxxxxxxxxxxxxxxxxxxxxxx

1 >>> T = np.array( [ True, False, True, True ])


2 >>> T.dtype
3 dtype(’bool’)
Ainsi si l’on définit un tableau contenant des données de types différents, Python va les convertir en
des données de type identique.

26 Entrez par exemple T2=np.array([3,5,7,'koala',10]). Quel est le type des données de T2 ? Que
remarquez-vous si vous demandez le premier élément de T2 ?
1 >>> T2 = np.array( [ 3,5,7,’koala’,10] )
2 >>> T2.dtype
3 dtype(’<U5’)
4 >>> T2
5 array([’3’, ’5’, ’7’, ’koala’, ’10’],
6 dtype=’<U5’)
Les éléments entiers de la chaı̂nes sont convertis en chaı̂ne de caractère (3 devient '3').

27 On peut “forcer” le type de données avec une définition du type


T3=np.array([1,4,7],dtype=float) (essayez !).

28 Comment pensez-vous que Python va réagir face au code qui suit ?


1 >>> np.array( [ 3,5,7,’koala’,10], dtype = int )
2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
erreur ! La chaı̂ne de caractères ne peut être convertie en entier.
1 >>> np.array( [ 3,5,7,’koala’,10], dtype = int )
2 Traceback (most recent call last):
3 File "<stdin>", line 1, in <module>
4 ValueError: invalid literal for int() with base 10: ’koala’

16
6 Passage par référence ‹
Tout comme les listes Python, les tableaux numpy sont passés en référence aux fonctions.

29 Testez par exemple



1 def toto(x):
2 x=6
3
4 def titi(M):
5 M[0]=100
6
7 x=8 ; print(x) ; toto(x) ; print(x) # bien s^
ur, x n’est pas modifié!
8 M = np.zeros(10) ; print(M) ; titi(M) ; print(M) # M a été modifié!
 

1 8
2 8
3 [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
4 [ 100. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

30 ‹‹ Comparez
1 >>> L = [[0]]*4
2 >>> M=np.array(L)
3 >>> L
4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5 >>> L[0][0]=100
6 >>> L
7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
8 >>> M
9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
10 >>> M[0][0]=100
11 >>> M
12 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

1 >>> L = [[0]]*4
2 >>> M=np.array(L)
3 >>> L
4 [[0], [0], [0], [0]]
5 >>> L[0][0]=100
6 >>> L
7 [[100], [100], [100], [100]]
8 >>> M
9 array([[0],
10 [0],
11 [0],
12 [0]])
13 >>> M[0][0]=100
14 >>> M
15 array([[100],
16 [ 0],

17
17 [ 0],
18 [ 0]])
Comme évoqué plus haut, les lignes de L sont des copies d’un unique pointeur, c’est-à-dire qu’elles
désignent le même objet (la même liste) : donc en modifier une modifie aussi les autres.
Ce comportement ne se retrouve pas avec les tableau numpy.
On peut le voir en demandant à Python les identifiants des objets.
1 >>> id( L[0] ), id( L[1] )
2 (140269327338312, 140269327338312)
3 >>> id( M[0] ), id( M[1] )
4 (41300544, 41304464)

7 Calculs de sommes ‹

31 Définissez un tableau M (à une ou deux dimensions). Que renvoie la fonction sum avec l’argu-
ment M ?
1 >>> M = np.array([[1,2,3],[4,5,6]])
2 >>> M
3 array([[1, 2, 3],
4 [4, 5, 6]])
5 >>> sum(M)
6 array([5, 7, 9])
7 >>> sum(sum(M))
8 21
sum enlève un niveau de profondeur ; commence à faire les sommes sur les colonnes, puis on obtient
un vecteur dont il somme les composantes.
On dispose aussi de la fonction np.sum de numpy.
1 >>> M = np.array([[1,2,3],[4,5,6]])
2 >>> np.sum(M)
3 21

32 Reprenez les calculs de moyenne et d’écart-type déjà réalisés pour les listes au TP 4, et voyez
comment tout est plus facile avec numpy. Vérifiez vos résultats avec les commandes déjà existantes
np.mean et np.std.
1 >>> n=10
2 >>> A = np.random.rand(n)
3 >>> A
4 array([ 0.28022385, 0.33172203, 0.29199176, 0.0825734 , 0.56793398,
5 0.88133212, 0.21601411, 0.28712047, 0.79173467, 0.72152186])
6 >>> moyenne = sum(A)/n
7 >>> moyenne
8 0.44521682558425424
9 >>> np.mean(A)
10 0.44521682558425424
11 >>> np.sqrt ( sum( (A-moyenne)**2) / n )
12 0.2596719868357974

18
13 >>> np.std(A)
14 0.2596719868357974

Les tableaux numpy permettent de faire plus que de simples listes.


¨ ˛
˚x‹
˚ ‹
˚ ‹
Ñ
Ý 3
Par exemple, soit un vecteur u P R de coordonnées ˚ ˚ y ‹, on sait que sa norme euclidienne est

˚ ‹
˝ ‚
z
a
donnée par }Ñ Ý
v } “ x2 ` y 2 ` z 2 . Plus Ñ
Ý n
a généralement, si x P R de coordonnées px1 , . . . , xn q, on peut
définir sa norme euclidienne }Ñ
Ý
x } “ x21 ` . . . ` x2n .

33 Un vecteur étant entré comme un tableau numpy unidimensionnel, comment calculer très simple-
ment sa norme ?
1 >>> V = np.random.rand(10)
2 >>> V
3 array([ 0.72291762, 0.64474318, 0.95178733, 0.99608139, 0.4413842 ,
4 0.99856803, 0.38981072, 0.87087263, 0.20634464, 0.60676378])
5 >>> np.sqrt(sum(V**2))
6 2.3128880108772321

8 Assemblage de matrices ‹‹

34 Plusieurs commandes existent pour assembler des matrices afin d’en créer une plus grande (repeat,
tile, concatenate, hstack, vstack).
En utilisant hstack et vstack, ainsi que les commandes de création de tableaux particuliers vues plus
haut (ones, eye, diag...), créez la matrice

¨ ˛
˚ 8 5 0 0 1 0 0 0 0 4 ‹
˚ ‹
˚ ‹
˚
˚ 8 0 6 0 0 1 0 0 0 4 ‹

˚ ‹
˚ ‹
˚
˚ 8 0 0 7 0 0 1 0 0 4 ‹

˚ ‹
˚ ‹
˚
˚ 8 1 1 1 0 0 0 1 0 4 ‹

˚ ‹
˚ ‹
8 1 1 1 0 0 0 0 1 4 ‹
˚ ‹
˚
˚ ‹
˝ ‚
8 3 3 3 3 3 3 3 3 3

Il faut savoir découper correctement !

19
8 5 0 0 1 0 0 0 0 4

8 0 6 0 0 1 0 0 0 4

8 0 0 7 0 0 1 0 0 4

8 1 1 1 0 0 0 1 0 4

8 1 1 1 0 0 0 0 1 4

8 3 3 3 3 3 3 3 3 3
1 >>> A = 8*np.ones((6,1))
2 >>> B = np.diag([5,6,7])
3 >>> C = np.eye(5)
4 >>> D = np.ones((2,3))
5 >>> E = 4*np.ones((5,1))
6 >>> F = 3*np.ones((1,9))
7 >>>
8 >>> G = np.vstack((B,D))
9 >>> H = np.hstack((G,C,E))
10 >>> I = np.vstack((H,F))
11 >>> J = np.hstack((A,I))
12 >>> J
13 array([[ 8., 5., 0., 0., 1., 0., 0., 0., 0., 4.],
14 [ 8., 0., 6., 0., 0., 1., 0., 0., 0., 4.],
15 [ 8., 0., 0., 7., 0., 0., 1., 0., 0., 4.],
16 [ 8., 1., 1., 1., 0., 0., 0., 1., 0., 4.],
17 [ 8., 1., 1., 1., 0., 0., 0., 0., 1., 4.],
18 [ 8., 3., 3., 3., 3., 3., 3., 3., 3., 3.]])

Remarque. Comme évoqué plus haut, hstack et vstack ne modifient pas les tableaux qui leur sont
passés en argument, mais renvoient un nouveau tableau.

20
9 Pour les curieux : traitement d’images ‹‹
En machine, les images couleurs sont des tableaux de triplets de nombres entre 0 et 255 représentant
les taux respectifs de rouge, vert et bleu.
Une image de h pixels de hauteurs par ` pixels de largeur, est représentée par une matrice à h lignes
et ` colonnes.
On suppose qu’il existe un fichier image.jpg dans le répertoire courant. Ou peut-être, si vous travaillez
en salle de TP, feriez-vous mieux de travailler avec un fichier au format png. . .

35 Si vraiment vous avez terminé le reste, vous pouvez tenter de comprendre le petit programme
images.py, placé sur mon site web pour vous, et que vous exécuterez la première fois en tant que script
(Ctrl+Shift+E).

1 import imageio
2 import matplotlib.pyplot as plt
3 import numpy as np
4
5 image = imageio.imread("image.jpg")
6 taille=np.shape(image)
7 largeur=taille[1] ; hauteur=taille[0]
8 print("largeur: ",largeur," hauteur: ",hauteur)
9
10 # 1 pixel sur 3
11 image2 = np.array([[image[i][j] for j in range(largeur) if j%3==0] for i in range(
hauteur) if i%3==0])
12
13 # un petit morceau de l’image
14 image3 = np.array([[image[i][j] for j in range(400,700) ] for i in range(100,200)
])
15
16 plt.imshow(image2)
17 plt.show()
18 plt.imshow(image3)
19 plt.show()
 

Le traitement d’images fera l’objet d’un TP entier en MP.

21

Vous aimerez peut-être aussi