3. Tableaux et matrices

NumPy fournit les types de données de base pour la data-science, les statistiques et l'analyse numérique : tableaux et matrices. La différence entre ces deux types de données est la suivante:

  • Les tableaux peuvent avoir 1, 2, 3 dimensions ou plus, et les matrices ont toujours 2 dimensions. Cela signifie qu'un vecteur $1 \times n$ est stocké sous forme d'un tableau à une dimension et $n$ éléments, tandis que le même vecteur stocké sous forme de matrice à 2 dimensions, les tailles des dimensions étant $1$ et $n$ (dans l'un ou l'autre ordre).
  • Les opérations arithmétiques sur les tableaux se font élément-par-élément. Le cas des matrices fait exception où la multiplication * suit les règles de l'algèbre linéaire. Les tableaux de dimension 2 peuvent être multipliés suivants les règles de l'algèbre linéaire en utilisant la fonction dot, et si on utilise Python 3.5 (ou plus), les tableaux peuvent être multipliés à l'aide du symbole @. De manière similaire, la fonction multiply peut être utilisée pour multiplier deux matrices élément-par-élément.

  • Les tableaux sont plus courants que les matrices et toutes les fonctions sont minutieusement testées sur les tableaux. Les mêmes fonctions devraient également fonctionner avec les matrices, mais un bug rare lors de l'utilisation des matrices n'est pas écarté.

  • Les tableaux peuvent être traités comme des matrices à l'aide des fonctions asmatrix ou mat sans copier les données sous-jacentes.

Les meilleures pratiques consistent à utiliser le format tableau en utilisant le symbole @ pour la multiplication matricielle. La fonction asmatrix peut être utilisée lors l'écriture de code d'algèbre linéaire.

3.1 Array

Les tableaux constituent le type de données de base dans la librairie NumPy. Ils sont similaires aux listes ou aux tuples car ils contiennent tous les deux des collections d'éléments. Cette section est axée sur les tableaux de données numériques, c’est-à-dire un tableau où tous les éléments ont le même type numérique. De plus, les tableaux, contrairement aux listes, sont toujours rectangulaires, de sorte que toutes les dimensions ont le même nombre d'éléments.

In [ ]:
import numpy 
x = [0.0, 1, 2, 3, 4]
y = array(x)
y
array([ 0., 1., 2., 3., 4.])
type(y)
numpy.ndarray

Des tableaux à deux dimensions (ou plus) sont initialisés à l'aide de listes imbriquées.

In [ ]:
y = array([[0.0, 1, 2, 3, 4], [5, 6, 7, 8, 9]])
print(y)

print(y.shape)

y = array([[[1,2],[3,4]],[[5,6],[7,8]]])
print(y)

print(y.shape)

3.1.1 Array dtypes

Les tableaux homogènes peuvent contenir divers types de données numériques. Le type de données le plus courant est float64 (ou double), ce qui correspond au type de données flottant intégré à python (et double en C/C++). Par défaut, les appels à array préservent le type d'entrée, si possible. Si une entrée contient tous les entiers, elle aura le type int32 (similaire au type de données int). Si une entrée contient des entiers, des flottants ou un mélange des deux, le type de données du tableau sera float64. Si l'entrée contient un mélange d'entiers, de flottants et de types complexes, le tableau sera initialisé pour contenir des données complexes.

In [ ]:
>>> x = [0, 1, 2, 3, 4] # Integers
>>> y = array(x)
>>> y.dtype
dtype('int32')
>>> x = [0.0, 1, 2, 3, 4] # 0.0 is a float
>>> y = array(x)


>>> y.dtype
dtype('float64')
>>> x = [0.0 + 1j, 1, 2, 3, 4] # (0.0 + 1j) is a complex
>>> y = array(x)
>>> y
array([ 0.+1.j,
1.+0.j,
2.+0.j,
3.+0.j,
4.+0.j])
>>> y.dtype
dtype('complex128')

NumPy tente de trouver le type de données le plus petit pouvant représenter les données lors de la construction d'un tableau. Il est possible de forcer NumPy à sélectionner un type particulier en utilisant le mot clé argument dtype = type de données lors de l'initialisation du tableau.

In [ ]:
>>> x = [0, 1, 2, 3, 4] # Integers
>>> y = array(x)
>>> y.dtype
dtype('int32')
>>> y = array(x, dtype='float64') # String dtype
>>> y.dtype
dtype('float64')
>>> y = array(x, dtype=float32) # NumPy type dtype
>>> y.dtype
dtype('float32')

3.2 Matrices

Une matrice est un cas particulier de tableaux et se comportent de manière pratiquement identique. Les deux différences importantes sont:

  • Les matrices possèdes 2 dimensions

  • Les matrices sont compatibles avec les règles de calcul d'algèbre linéaire.

Les tableaux à 1 et 2 dimensions peuvent être copiés dans une matrice en appelant matrix sur un array. Les fonctions mat ou asmatrix sont des méthodes plus rapides pour contraindre un tableau à se comporter comme une matrice sans copier aucune donnée.

In [ ]:
>>> x = [0.0, 1, 2, 3, 4] # Any float makes all float
>>> y = array(x)
>>> type(y)
numpy.ndarray
>>> y * y # Element-by-element
array([ 0., 1., 4., 9., 16.])
>>> z = asmatrix(x)
>>> type(z)
numpy.matrixlib.defmatrix.matrix
>>> z * z # Error
ValueError: matrices are not aligned

3.3 Tableaux à 1 dimension

Un vecteur $$x = [1 \quad 2 \quad 3 \quad 4 \quad 5]$$

est déclaré comme un tableau en dimension 1 via

In [ ]:
>>> x=array([1.0, 2.0, 3.0, 4.0, 5.0])
array([ 1., 2., 3., 4., 5.])
>>> ndim(x)
1

Si un tableau à 2 dimensions est nécessaire, on utilise une liste imbriquée.

In [ ]:
>>> x=array([[1.0,2.0,3.0,4.0,5.0]])
array([[ 1., 2., 3., 4., 5.]])
>>> ndim(x)
2

Une matrice étant toujours bidimensionnelle, une liste imbriquée n'est pas nécessaire pour initialiser une matrice ligne.

In [ ]:
>>> x=matrix([1.0,2.0,3.0,4.0,5.0])
>>> x
matrix([[ 1., 2., 3., 4., 5.]])
>>> ndim(x)
2

Pour entrer une matrice colonne, nous avons besoin d'une série de listes imbriquées comme suit

In [ ]:
>>> x=matrix([[1.0],[2.0],[3.0],[4.0],[5.0]])
>>> x
matrix([[ 1.],
        [ 2.],
        [ 3.],
        [ 4.],
        [ 5.]])
>>> x = array([[1.0],[2.0],[3.0],[4.0],[5.0]])
>>> x
array([[ 1.],
       [ 2.],
       [ 3.],
       [ 4.],
       [ 5.]])

3.4 Tableaux à 2 dimensions

La déclaration d'une matrice ou d'un tableaux à 2 dimensions revient à entrer les lignes une par une dans une liste comme suit

In [ ]:
>>> x = array([[1.0,2.0,3.0],[4.0,5.0,6.0],[7.0,8.0,9.0]])
>>> x
array([[ 1., 2., 3.],
       [ 4., 5., 6.],
       [ 7., 8., 9.]])

3.5 Tableau multidimensionnel

Les tableaux de dimensions supérieures ont de nombreuses utilisations, par exemple pour modéliser une covariance variant dans le temps. Des tableaux multidimensionnels ($N$ dimensions) sont disponibles pour $N$ allant jusqu’à environ $30$, en fonction de la taille de chaque dimension marginale. Initialiser manuellement des tableaux multidimensionnels est fastidieux et sujet à erreur, il est donc préférable d’utiliser des fonctions telles que zeros((2, 2, 2)) ou empty((2, 2, 2)).

In [ ]:
import numpy as np
x  = np.zeros((2,2,2))
print(x)

y  = np.empty((2,2,2))
print(y)

3.6 Juxtaposition

La juxtaposition est le processus par lequel un vecteur ou une matrice est ajouté à un autre. Les tableaux et les matrices peuvent être concaténés horizontalement ou verticalement. Une juxtaposition se fait à l'aide de la fonction concatenate. Les entrées de la fonction concatenate doivent être regroupées dans un tuple et l’argument axis indique si les tableaux doivent être juxtaposés verticalement (axe=0) ou horizontalement (axe=1).

In [ ]:
>>> x = array([[1.0,2.0],[3.0,4.0]])
>>> y = array([[5.0,6.0],[7.0,8.0]])
>>> z = concatenate((x,y),axis = 0)
>>> z
array([[ 1., 2.],
       [ 3., 4.],
       [ 5., 6.],
       [ 7., 8.]])
>>> z = concatenate((x,y),axis = 1)
>>> z
array([[ 1., 2., 5., 6.],
       [ 3., 4., 7., 8.]])

L'opération de juxtaposition donne des résultats équivalent à une matrice définie par blocs (vocabulaire d'algèbre linéaire). De manière similaire, les fonctions vstack et hstack peuvent être utilisées pour juxtaposer des tableaux verticalement ou horizontalement.

In [ ]:
>>> z = vstack((x,y)) # Similaire à z = concatenate((x,y),axis = 0)
>>> z = hstack((x,y)) # Similaire à z = concatenate((x,y),axis = 1)

3.7 Accéder aux éléments d'un tableau

Plusieurs méthodes sont possibles pour accéder aux éléments d'un tableau: la sélection scalaire, l'opération de slicing et l'indexation logique (ou booléenne). La sélection scalaire et le slicing sont les plus simples et sont donc présentés en premier.

3.7.1 Sélection scalaire

La sélection scalaire est la méthode la plus simple pour sélectionner des éléments dans un tableau. Elle est implémentée en utilisant $[i]$ pour les tableaux à une dimension, $[i, j]$ pour les tableaux à deux dimensions et $[i_1, i_2, \ldots, i_n]$ pour les tableaux à $n$ dimensions. Comme pour toute indexation en Python, l'indexation part de $0$, de sorte que $[0]$ soit le premier élément d'un tableau à une dimension, $[0,0]$ soit l'élément supérieur gauche d'un tableau à 2 dimensions, etc.

In [ ]:
>>> x = array([1.0,2.0,3.0,4.0,5.0])
>>> x[0]
1.0
>>> x = array([[1.0,2,3],[4,5,6]])
>>> x
array([[ 1., 2., 3.],
       [ 4., 5., 6.]])
>>> x[1, 2]
6.0
>>> type(x[1,2])
numpy.float64

La sélection scalaire nous renvoie toujours un seul élément qui n'est pas un tableau. Le type de données de l'élément sélectionné correspond au type de données du tableau utilisé dans la sélection. La sélection scalaire peut également être utilisée pour affecter des valeurs dans un tableau.

In [ ]:
>>> x = array([1.0,2.0,3.0,4.0,5.0])
>>> x[0] = -5
>>> x
array([-5., 2., 3., 4., 5.])

3.7.2 Opération de slicing

Les opérations de type slicing sur les tableaux sont pratiquement identiques aux listes. La syntaxe est données par [:,:,...,:] où le nombre de dimensions détermine la taille de la coupe. Rappelons la notation de coupe a:b:s sélectionnera chaque s ème élément où les indices $i$ vérifient la conditions $a ≤ i <b$, de sorte que la valeur de départ $a$ soit toujours incluse dans la liste et que la valeur finale $b$ soit toujours exclue.

  • Les notations : et :: sont les mêmes que 0:n:1n est la longueur d'un tableau (ou liste).

  • a: et a:n sont les mêmes que a:n:1n est la longueur d'un tableau (ou liste).

  • :b est la même que 0:b:1

  • ::s est la même que 0:n:sn est la longueur d'un tableau (ou liste).

Il n'est pas nécessaire d'inclure toutes les dimensions de fin de coupe et les éventuelles coupes de fin omises sont configurées pour sélectionner tous les éléments (la coupe:). Par exemple, si x est un tableau à 3 dimensions, x[0:2] est identique à x[0:2,:,:] et x[0:2,0:2] est identique à x[0:2,0:2,].  

L'opération de slicing de base de tableaux unidimensionnels est identique à celle d’une liste simple et le type renvoyé par une opération de slicing est compatible avec le tableau de départ.

In [ ]:
>>> x = array([1.0,2.0,3.0,4.0,5.0])
>>> y = x[:]
array([ 1., 2., 3., 4., 5.])

>>> y = x[:2]
array([ 1., 2.])

>>> y = x[1::2]
array([ 2.,4.])

Pour les tableaux à $2$ dimensions, la première dimension spécifie la ou les lignes de la coupe et la seconde dimension spécifie la ou les colonnes.

Notons que la syntaxe de coupe en 2-dimensions y[a:b,c:d] est la même que y[a:b,:][:,c:d] ou y[a:b][:,c:d], bien que la version la plus courte soit préférée. Dans le cas où seul la coupe de lignes est nécessaire y[a:b], l'équivalent de y[a:b,:], est la syntaxe la plus courte.

In [ ]:
>>> y = array([[0.0, 1, 2, 3, 4],[5, 6, 7, 8, 9]])
>>> y
array([[ 0., 1., 2., 3., 4.],
       [ 5., 6., 7., 8., 9.]])

>>> y[:1,:] # Row 0, all columns
array([[ 0., 1., 2., 3., 4.]])

>> y[:1] # Same as y[:1,:]
array([[ 0., 1., 2., 3., 4.]])

>>> y[:,:1] # all rows, column 0
array([[ 0.],
       [ 5.]])

>>> y[:1,0:3] # Row 0, columns 0 to 2
array([[ 0., 1., 2.]])

>>> y[:1][:,0:3] # Same as previous
array([[ 0., 1., 2.]])

>>> y[:,3:] # All rows, columns 3 and 4
array([[ 3., 4.],
       [ 8., 9.]])

>>> y = array([[[1.0,2],[3,4]],[[5,6],[7,8]]])
>>> y[:1,:,:] # Panel 0 of 3D y
array([[[ 1., 2.],
        [ 3., 4.]]])

3.7.3 Sélection mixte scalaire et slicing

Lorsque les tableaux ont plus d'une dimension, il est souvent utile de combiner une sélection scalaire et par coupe (slicing) pour sélectionner une ligne, une colonne ou une partie entière d'un tableau à 3 dimensions. Ceci est similaire au slicing pur avec une mise en garde importante: les dimensions sélectionnées à l'aide de la sélection scalaire sont éliminées. Par exemple, si x est un tableau à $2$ dimensions, alors x[0,:] sélectionne la première ligne du tableau. Cependant, contrairement au tableau à 2 dimensions construit à l'aide de l'opération x[:1,:], x[0,:] sera un tableau à 1 dimension.

In [ ]:
>>> x = array([[1.0,2],[3,4]])
>>> x[:1,:] # Row 1, all columns, 2-dimensional
array([[ 1., 2.]])

>>> x[0,:] # Row 1, all columns, dimension reduced
array([ 1., 2.])

Bien que les deux sélections semblent similaires, la première produit un tableau à 2 dimensions (notons la syntaxe [[]]), tandis que la seconde est un tableau à $1$ dimension. Dans la plupart des cas où une seule ligne ou une seule colonne est requise, l'utilisation de sélecteurs scalaires tels que y[0,:] est la meilleure pratique.

Le principe adopté par NumPy est que l'opération de slicing doit toujours préserver la dimension du tableau sous-jacent, tandis que la sélection scalaire doit toujours réduire la ou les dimensions. Ceci est cohérent avec x[0,0] retournant un scalaire (un tableau à $0$ dimensions) puisque les deux sélections sont scalaires. Ceci est démontré dans l'exemple suivant qui souligne les différences entre slicing pur, la sélection mixte et la sélection scalaire pure. Notons que la fonction ndim renvoie le nombre de dimensions d’un tableau.

In [ ]:
>>> x = array([[0.0, 1, 2, 3, 4],[5, 6, 7, 8, 9]])
>>> x[:1,:] # Row 0, all columns, 2-dimensional
array([[ 0., 1., 2., 3., 4.]])

>>> ndim(x[:1,:])
2
>>> x[0,:] # Row 0, all column, dim reduction to 1-d array
array([ 0., 1., 2., 3., 4.])

>>> ndim(x[0,:])
1

>>> x[0,0] # Top left element, dim reduction to scalar (0-d array)
0.0

>>> ndim(x[0,0])
0

>>> x[:,0] # All rows, 1 column, dim reduction to 1-d array
array([ 0., 5.])

3.7.4 Affectation à l'aide d'une opération slicing

La sélection par coupe (slicing) et la sélection scalaire peuvent être utilisés pour affecter des tableaux ayant la même dimension que la coupe.

In [ ]:
>>> x = array([[0.0]*3]*3) # *3 repeats the list 3 times
>>> x
array([[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]])

>>> x[0,:] = array([1.0, 2.0, 3.0])
>>> x
array([[ 1., 2., 3.],
       [ 0., 0., 0.],
       [ 0., 0., 0.]])

>>> x[::2,::2]
array([[ 1., 3.],
       [ 0., 0.]])


>>> x[::2,::2] = array([[-99.0,-99],[-99,-99]]) # 2 by 2
>>> x
array([[-99., 2., -99.],
       [0., 0., 0.],
       [-99., 0., -99.]])

>>> x[1,1] = pi
>>> x
array([[-99., 2.,       -99.],
       [  0., 3.14159265, 0.],
       [-99., 0.,       -99.]])

NumPy tente une conversion automatique des types de données si un élément est inséré dans un tableau avec un type différent. Par exemple, si un tableau a un type de données entier, si on place un flottant dans le tableau, celui-ci sera tronqué et stocké sous la forme d'un entier. Ceci est dangereux et dans la plupart des cas, les tableaux doivent être initialisés pour contenir des flottants, sauf si tout est sous contrôle.

In [ ]:
>>> x = [0, 1, 2, 3, 4] # Integers
>>> y = array(x)
>>> y.dtype
dtype('int32')

>>> y[0] = 3.141592
>>> y
array([3, 1, 2, 3, 4])

>>> x = [0.0, 1, 2, 3, 4] # 1 Float makes all float
>>> y = array(x)
>>> y.dtype
dtype('float64')

>>> y[0] = 3.141592
>>> y
array([3.141592, 1., 2., 3., 4.])

3.7.5 Slicing linéaire à l'aide de flat

Le stockage des données d'une matrice se fait ligne après ligne. Cela peut être généralisé pour un tableau à plusieurs dimensions. Nous pouvons faire appel à la fonction flat comme suit

In [ ]:
import numpy as np
>>> y = np.reshape(np.arange(25.0),(5,5))
y

>>> y[0] # Same as y[0,:], first row
array([ 0., 1., 2., 3., 4.])

>>> y.flat[0] # Scalar slice, flat is 1-dimensional
0

>>> y[6] # Error
IndexError: index out of bounds

>>> y.flat[6] # Element 6
6.0

>>> y.flat[12:15]
array([12., 13., 14.])


>>> y.flat[:] # All element slice
array([[0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.,
        11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 21.,
        22., 23., 24.]])

3.8 Slicing et la gestion de mémoire

Propagation des modifications

In [ ]:
>>> x = reshape(arange(4.0),(2,2))
>>> x
array([[ 0., 1.],
       [ 2., 3.]])

>>> s1 = x[0,:] # First row
>>> s2 = x[:,0] # First column
>>> s1[0] = -3.14 # Assign first element
>>> s1
array([-3.14, 1. ])

>>> s2
array([-3.14, 2. ])


>>> x
array([[-3.14, 1.],
       [ 2., 3.]])

Si on souhaite bloquer la propagation, on utilise la fonction copy

In [ ]:
>>> x = reshape(arange(4.0),(2,2))
>>> s1 = copy(x[0,:]) # Function copy
>>> s2 = x[:,0].copy() # Method copy, more common
>>> s3 = array(x[0,:]) # Create a new array
>>> s1[0] = -3.14
>>> s1
array([-3.14, 1.])

>>> s2
array([ 0., 2.])

>>> s3
array([0., 1.])

>>> x[0,0]
array([[ 0., 1.],
       [ 2., 3.]])

Il existe une exception à cette règle: lors de l'utilisation d'une sélection scalaire pure, la valeur (scalaire) renvoyée est toujours une copie.

In [ ]:
>>> x = arange(5.0)
>>> y = x[0] # Pure scalar selection
>>> z = x[:1] # A pure slice
>>> y = -3.14
>>> y # y Changes
-3.14

>>> x # No propagation
array([ 0., 1., 2., 3., 4.])

>>> z # No changes to z either
array([ 0.])

>>> z[0] = -2.79

>>> y # No propagation since y used pure scalar selection
-3.14

>>> x # z is a view of x, so changes propagate
array([-2.79, 1., 2., 3., 4.])

Enfin, les assignations qui sont des résultats d'applications de fonctions créeront automatiquement une copie du tableau sous-jacent.

In [ ]:
>>> x = array([[0.0, 1.0],[2.0,3.0]])
>>> y = x

>>> print(id(x),id(y)) # même id, même objet
129186368 129186368

>>> y = x + 1.0
>>> y
array([[ 1., 2.],
       [ 3., 4.]])

>>> print(id(x),id(y)) 
129186368 129183104
>>> x # non modifié
array([[ 0., 1.],
       [ 2., 3.]])

>>> y = exp(x)
>>> print(id(x),id(y)) 
129186368 129185120

3.9 import et modules

Python, par défaut, n'a accès qu'à un petit nombre de types et de fonctions intégrés. La grande majorité des fonctions sont founies par des modules. Avant de pouvoir accéder à une fonction, le module contenant cette fonction doit être importé. Par exemple, lors de l'utilisation de %pylab dans une session IPython, un grand nombre de modules sont automatiquement importés, notamment NumPy, SciPy et matplotlib.

import peut être utilisé de différentes manières. Le plus simple est d'utiliser from module import* qui importe toutes les fonctions du module. L'utilisation de la fonction import peut être problématique. Il est possible qu'une fonction importée d'un module soit cachée par un autre import d'un autre module. Il est conseillé d'importer seulement les fonctions nécessaires pour le programme.

In [ ]:
from pylab import log2 # Will import log2 only
from scipy import log10 # Will import the log10 only

Souvent on utilise

In [ ]:
import pylab
import scipy
import numpy

ou encore

In [ ]:
import pylab as pl
import scipy as sp
import numpy as np

Cette seconde option est préférable pour contrôler la provenance des fonctions. Par exemple, on peut accéder à la fonction racine carrée issue de SciPy à l'aide de sp.sqrt et celle issue de pylab à l'aide de pl.sqrt.

3.10 Appels aux fonctions

Les appels aux fonctions ont des conventions différentes de celles de la plupart des autres expressions. La différence la plus importante est que les fonctions peuvent prendre plusieurs entrées et renvoyer plusieurs sorties. La structure générique d'un appel de fonction est out1, out2, out3,. . . = functionname(in1, in2, in3,...).

Arguments requis

La plupart des fonctions ont des arguments requis. Par exemple, considérons la définition de array à partir de help(array), array(object, dtype=None, copy=True, order=None, subok=False, ndmin=0). array possède un seul argument dit requis object qui est une liste ou un tuple qui contient les données destinées à la création du tableau. L'aide de la fonction permet de lister les arguments où la plupart des arguments sont de la forme keyword=default à l'exception de object qui est un argument requis qui ne possède pas de valeur par défaut.

In [ ]:
>>> array([[1.0,2.0],[3.0,4.0]])
array([[ 1., 2.], 
       [ 3., 4.]])

>>> array([[1.0,2.0],[3.0,4.0]], 'int32')
array([[1, 2],
       [3, 4]])

Arguments keyword

array(object=[[1.0,2.0],[3.0,4.0]])

array([[1.0,2.0],[3.0,4.0]], dtype=None, copy=True, order=None, subok=False, ndmin=0)

Les arguments keyword de mots présentent deux avantages importants. Premièrement, ils ne doivent apparaître dans aucun ordre (Remarque: les arguments de classement aléatoire ne sont pas une bonne pratique et il ne s'agit que d'un exemple), et deuxièmement, les arguments keyword ne peuvent être utilisés qu'en cas de besoin, car une valeur par défaut est toujours fournie.  

In [ ]:
>>> array(dtype='complex64', object = [[1.0,2.0],[3.0,4.0]], copy=True)
array([[1.+0.j, 2.+0.j],
       [3.+0.j, 4.+0.j]], dtype=complex64)

Arguments par défaut

Les fonctions ont des valeurs par défaut pour les arguments optionnels. Celles-ci sont répertoriées dans la définition de la fonction et apparaissent dans l'aide sous la forme keyword=default. En retournant au tableau, toutes les entrées ont des arguments par défaut, à l'exception de object, le argument requis.

Fonction à sorties multiples

Certaines fonctions peuvent avoir plus d’une sortie. Ces fonctions peuvent être utilisées en mode de sortie unique ou en mode de sortie multiple. Par exemple, shape peut être utilisée sur un tableau pour déterminer la taille de chaque dimension.

In [ ]:
>>> x = array([[1.0,2.0],[3.0,4.0]])
>>> s = shape(x)
>>> s
(2, 2)

Comme la forme retournera autant de sorties qu'il y a de dimensions, elle peut être appelée avec 2 sorties lorsque l'entrée est un tableau à 2 dimensions.

In [ ]:
>>> x = array([[1.0,2.0],[3.0,4.0]])
>>> M, N = shape(x)

>>> M
2

>>> N
2

Demander plus de sorties que nécessaire produit une erreur.

In [ ]:
>>> M, N, P = shape(x) # Error
ValueError: need more than 2 values to unpack

De même, fournir peu de sorties peut aussi générer une erreur. Prenons le cas d'un tableau à 3 dimensions.

In [ ]:
>>> x = randn(10,10,10)
>>> shape(x)
(10, 10, 10)

>>> M, N = shape(x) # Error
ValueError: too many values to unpack

A. Un peu plus sur numpy

A.1 Générer des nombres aléatoires, sauvegarde et lecture de données

In [ ]:
import os as os
import numpy as np

M=np.random.randn(10,10)
print(M)
M.shape
In [ ]:
## sauvegarder dans fichier csv 
print(os.getcwd()) ## afficher le répertoire courant 
#os.chdir(path)  # pour changer de répertoire de travail
np.savetxt('data.csv',M,fmt='%2.2f',delimiter=',')

## on peut sauvegarder au format propre à numpy : npy
np.save('data.npy',M)
np.load('data.npy')

A.2 Manipuler un tableau à une dimension

Identifier le résultat de chacune des instructions suivantes

In [ ]:
#1.
v=np.array(range(5)) + 1

#2.
print(v)

#3.
v[1:4]

#4.
v[::]

#5.
v[ : :2]

#6.
v[ : 3]

#7.
v[3 :]

#8.
v[-1]

#9.
v[-2 :]

A.3 Manipuler un tableau à 2 dimensions

Identifier le résultat de chacune des instructions suivantes

In [ ]:
#1.
M=random.rand(4,3)

#2.
ind=[1,2]
M[ind]

#3.
M[:,ind]

#4.
M[[0,2],[1,2]]

#5.
M[np.ix_([0,2],[1,2])]

#6.
(M>0.5)

#7.
M[M>0.5]

A.4 Manipuler un tableau à 2 dimensions

Identifier le résultat de chacune des instructions suivantes

In [ ]:
#1.
a=np.array([[0,1],[2,3],[4,5]])

#2.
np.ndim(a)

#3.
np.size(a)

#4.
np.shape(a)

#5.
np.transpose(a)

#6.
a.T

#7.
a.min(), np.min(a)

#8.
a.sum(), np.sum(a)

#9.
a.sum(axis=0)

#10.
a.sum(axis=1)

#11.
np.r_[1:4,10,11]

#12.
np.c_[1:4,11:14]

#13.
np.arange(6).reshape(3,2)

#14.
A=np.array([[1,2],[3,4]])
np.tile(A,(3,2))

#15.
A=np.array([[1,2],[3,4]])
B=np.array([[11,12],[13,14]])
np.concatenate((A,B),axis=0)

#16.
np.vstack((A,B))

#17.
np.concatenate((A,B),axis=1)

#18.
np.hstack((A,B))

A.5 Opérations sur les tableaux + algèbre linéaire

Identifier le résultat de chacune des instructions suivantes

In [ ]:
#1.
a=np.arange(6).reshape(3,2)
b=np.arange(3,9).reshape(3,2)
c=np.transpose(b)
a+b

#2.
a*b

#3.
np.dot(a,c)

#4.
np.power(a,2)

#5.
np.power(2,a)

#6.
a/3

#7.
import numpy as np
from scipy import linalg

#8.
A = np.array([[1,2],[3,4]])
linalg.inv(A)

#9.
linalg.det(A)

#10.
la,v = linalg.eig(A)
l1,l2 = la
print(l1, l2)

#11.
print(v[:,0])

#12.
U,s,V = linalg.svd(A)
print(s**2)

#13.
linalg.eig(np.dot(np.transpose(A),A))