1.4 Tableaux multidimensionnels et matrices

Si les tableaux à une dimension sont omniprésents dans le calcul scientifique, les tableaux multidimensionnels revêtent également une grande importance.

Ainsi, par exemple, une image en niveaux de gris (ou de bleus) peut être représentée par un tableau bidimensionnel (que l'on appelle également parfois une ''matrice''), alors qu'une image en couleurs correspond à un tableau de dimension trois.

Une image en couleurs peut être représentée par un tableau de dimension trois. Les deux premières dimensions fournissent la localisation (les coordonnées) des pixels colorés de l'image, alors que la troisième dimension donne l'information de couleur. Dans cette troisième dimension, on trouve trois nombres (entre \(0\) et \(255\)) correspondant à la proportion (luminosité) de rouge, de vert et de bleu (canaux RGB en anglais) permettant de reconstruire la couleur du pixel concerné. Cette façon de faire permet de produire \(256\cdot 256\cdot 256 = 16777216\) couleurs différentes ! Parfois, un quatrième nombre est ajouté dans cette troisième dimension pour avoir une information de transparence. On parle alors des quatre canaux RGBA de l'image, où A désigne le quatrième canal appelé également canal alpha.

Une image en niveaux de gris (ou en niveaux de bleu) peut être représentée par un tableau de dimension deux. En effet, l'information de couleur se résume alors à un seul nombre et une simple matrice permet de reconstuire l'image.

Construction à partir d'une fonction de NumPy

Numpy offre plusieurs fonctions permettant de construire des tableaux multidimensionnels.

  1. La fonction np.array

    Il est possible de créer un tableau NumPy en convertissant une liste à l'aide de la fonction np.array(object) :

    import numpy as np x = np.array([[1,2,3], [4,5,6]]) print(x) print(x.ndim,x.shape,x.dtype) [[1 2 3] [4 5 6]] 2 (2, 3) int64

    A la deuxième ligne du code ci-dessus, deux listes à une dimension, \([1,2,3]\) et \([4,5,6]\), sont placées dans des crochets pour créer une liste à deux dimensions. Lors de la conversion de cette liste par la fonction np.array, les éléments prennent le type le plus général rencontré en parcourant la liste.

  2. Les fonctions np.zeros, np.ones et np.empty

    Les fonctions np.zeros(shape), np.ones(shape) et np.empty(shape) que nous avons déjà rencontrées s'appliquent également dans le contexte plus général des tableaux multidimensionnels. Ainsi, le code

    import numpy as np print(np.zeros((3,20), dtype=int))

    produit un tableau possédant \(3\) lignes et \(20\) colonnes dont toutes les entrées sont posées égales à \(0\), \(0\) étant ici un entier :

    [[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]

    L'argument obligatoire de ces fonctions est un tuple dont le nombre d'éléments fournit la dimension du tableau.

    En observant le résultat produit par les trois lignes suivantes

    import numpy as np y = np.ones((2,3,1,8)) print(y,y.ndim,y.shape, sep=' ') [[[[1. 1. 1. 1. 1. 1. 1. 1.]] [[1. 1. 1. 1. 1. 1. 1. 1.]] [[1. 1. 1. 1. 1. 1. 1. 1.]]] [[[1. 1. 1. 1. 1. 1. 1. 1.]] [[1. 1. 1. 1. 1. 1. 1. 1.]] [[1. 1. 1. 1. 1. 1. 1. 1.]]]] 4 (2, 3, 1, 8)

    on note que l'axe correspondant au dernier élément du tuple est affiché horizontalement.

  3. La fonction np.eye

    NumPy propose également une fonction np.eye(N) qui permet de créer un tableau bidimensionnel carré \(N\times N\) avec des \(1\) sur la diagonale principale et des \(0\) ailleurs, c'est-à-dire une ''matrice identité'' :

    import numpy as np print(np.eye(3)) [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]]
  4. La fonction np.reshape

    Notons encore qu'un tableau multidimensionnel peut être obtenu à partir d'un vecteur (i.e. d'un tableau unidimensionnel) grâce à la fonction np.reshape(a,shape).

    Le code suivant fournit par exemple un tableau bidimensionnel \(3\times 6\) :

    import numpy as np oned = np.arange(18) multid = np.reshape(oned, (3,6)) print(oned, multid, sep = '\n') [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17] [[ 0 1 2 3 4 5] [ 6 7 8 9 10 11] [ 12 13 14 15 16 17]]
Accès aux éléments d'un tableau

L'accès aux éléments individuels d'un tableau peut se faire en utilisant la syntaxe employée avec les listes (quatrième ligne du code ci-dessous), ou en utilisant la syntaxe proposée à la cinquième ligne du code ci-dessous :

import numpy as np oned = np.arange(24) multid = np.reshape(oned, (3,8)) print(multid[1][3]) print(multid[1,3]) 11 11
Opérations ''matricielles''

Les opérations arithmétiques décrites plus haut s'appliquent toujours dans le cas plus général des tableaux multidimensionnels :

import numpy as np x = np.array([[1,2,3], [4,5,6]]) print(1-x) print(x*5) print(x**3) [[ 0 -1 -2] [-3 -4 -5]] [[ 5 10 15] [20 25 30]] [[ 1 8 27] [ 64 125 216]]

Ces opérations sont effectuées élément par élément.

Une multiplication de deux tableaux (de forme (shape) identique) va donc produire un résultat différent de celui attendu en effectuant le produit matriciel des deux objets (matrices) :

import numpy as np A = np.array([[1,2,3], [4,5,6]]) B = 2*np.ones((2,3)) print(A) print(B) print(A*B) [[1 2 3] [4 5 6]] [[2. 2. 2.] [2. 2. 2.]] [[ 2. 4. 6.] [ 8. 10. 12.]]

La multiplication matricielle habituelle en algèbre linéaire peut être obtenue en exploitant la fonction np.dot(a,b) :

import numpy as np A = np.array([[1,2,3], [4,5,6]]) B = 2*np.ones((2,3)) #print(np.dot(A,B)) #produit une erreur print(np.dot(A,B.T)) [[12. 12.] [30. 30.]]

Ici, la transposition de la matrice B (à l'aide de la méthode .transpose() ou .T) est nécessaire.

Le sous-module linalg de NumPy permet d'effectuer un grand nombre d'opérations matricielles plus spécialisées, par exemple sur les matrices carrées :

import numpy as np C = np.array([[0,2,1], [6,5,4], [2,0,0]]) print(np.linalg.det(C)) print(np.linalg.inv(C)) valeurs_propres, vecteurs_propres = np.linalg.eig(C) print(np.linalg.eig(C))

Les troisième et quatrième lignes du code ci-dessus calculent le déterminant et l'inverse de la matrice C :

6.0 [[ 0. 0. 0.5 ] [ 1.33333333 -0.33333333 1. ] [-1.66666667 0.66666667 -2. ]] (array([ 7.09302745, -1.54580305, -0.54722439]), array([[ 0.28085964, 0.60972679, -0.24417939], [ 0.95647598, -0.07681862, -0.37940392], [ 0.07919316, -0.78888031, 0.89242876]]))

Nous reviendrons sur quelques-unes de ces opérations en exercices et de plus amples informations peuvent être trouvées en parcourant la documentation (par exemple grâce à np.linalg?).