Librairie: Numpy-Scipy-Matplotlib¶

En Python, il existe un très grand nombre de librairie pour toute sorte de tâche. La syntaxe pour importer une librairie est la suivant

In [ ]:
import nom_du_module

Voici un exemple.

In [8]:
import math

Toutes les fonctions de la librairie math sont maintenant disponibles, et pour utiliser ces fonctions on utilise le prefixe qui correspond au nom du module. Par exemple,

In [5]:
math.cos(math.pi)
Out[5]:
-1.0

Vous remarquerez facilement que cette syntaxe n'est pas pratique, voir même lourde. Pour simplifier, on peut renommer la librairie lors de l'import de celle-ci. La syntaxe est la suivante.

In [ ]:
import nom_du_module as nm

Voici un exemple.

In [1]:
import numpy as np
In [8]:
np.cos(np.pi)
Out[8]:
-1.0

On peut aussi n'importer qu'une partie, ou qu'une fonction d'une librairie. La syntaxe est la suivante.

In [ ]:
from nom_du_module import sous_partie

Voici un exemple.

In [10]:
from numpy import pi

Ce qui nous permet d'avoir une syntaxe plus agréable qu'au départ

In [11]:
np.cos(pi)
Out[11]:
-1.0

On peut aussi faire

In [12]:
from numpy import cos, pi
In [ ]:
cos(pi)

Enfin, on peut renommer une sous partie comme suit.

In [ ]:
from nom_du_module import sous_partie as sp

Voici un exemple dans lequel on va extraire pyplot de matplotlib et qu'on va renommer plt

In [17]:
from matplotlib import pyplot as plt

Voici un exemple d'utilisation de la fonction plot de pyplot

In [28]:
x = range(10)
plt.plot(x, x, "o-")
Out[28]:
[<matplotlib.lines.Line2D at 0x7f25688e4160>]
No description has been provided for this image

Nous nous concentrerons sur les librairies

  • Numpy
  • Scipy (linalg, integrate, stats,...)
  • Matplotlib (pyplot)

Scipy (Scientifique python)¶

Il s'agit d'une librairie contenant différents sous modules pour l'optimisation optimize, l'integration numérique et les equations différentiells integrate, l'algèbre linéaire linalg, ou les statistiques stats, etc...

Matplotlib (2007 John Hunter)¶

Il s'agit d'un module de visualisation, avec des possibilités très proche de celles de Matlab.

Numpy (2006 Travis Oliphant)¶

  • Librairie de calcul scientifique.
  • Implémente des objets de typs ndarray (n dimensional array) qui sont des tableaux multidimensionnels qu'on peut appleler numpy array.
In [30]:
x = np.array([1, 2, 3, 4, 5, 6]) 
print(x)
[1 2 3 4 5 6]
In [32]:
type(x)
Out[32]:
numpy.ndarray
  • Ces tableaux permettent des opérations rapides.

Si on prend deux listes d'entier et qu'on veut faire leur somme, voici ce qu'il se passe.

In [33]:
x = [1, 2, 3, 4]
y = [5, 6, 7, 8]

x + y
Out[33]:
[1, 2, 3, 4, 5, 6, 7, 8]

Elles sont concaténées, mais on n'a pas une somme terme à terme. Par contre, pour si ce sont des numpy array on aura bien une somme terme à terme.

In [10]:
x = np.array([1, 2, 3, 4])
y = np.array([5, 6, 7, 8])

x + y
Out[10]:
array([ 6,  8, 10, 12])

Une remarque importante cependant! Créons une matrice de la façon suivante.

In [2]:
M = np.arange(1, 5).reshape(2,2)
print(M)
np.arange(1, 5)
[[1 2]
 [3 4]]
Out[2]:
array([1, 2, 3, 4])

Faisons maintenant

In [37]:
M1 = M

et modifions $M$

In [38]:
M[0,0] = 2  

Mais on remarque que

In [39]:
M1[0,0] 
Out[39]:
2

$M1$ a été modifié! Si on ne souhaite pas modifier $M1$ dans une telle opération, il faut faire une copie de $M$.

In [6]:
M = np.arange(1,5).reshape(2,2)
M1 = M.copy()

M[0,0] = 2  
M1[0,0]  
Out[6]:
1
  • La vectorisation: Numpy est optimisé pour éviter l'usage des boucle for.

Voici une différence entre la fonction cos de numpy et celui de la librairie math.

In [11]:
math.cos(x)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-5aa3f5169bb5> in <module>
----> 1 math.cos(x)

TypeError: only size-1 arrays can be converted to Python scalars

On voit que le fonction de math ne supporte pas comme argument une liste de nombre. Si on veut toutes les images des point de x il faudrait faire une boucle for. Par contre, avec le cos de numpy, il n' y a aucun problème.

In [12]:
np.cos(x)
Out[12]:
array([ 0.54030231, -0.41614684, -0.9899925 , -0.65364362])
  • Broadcasting: Numpy gère les différencees de tailles de tableau durant les opérations, mais attention ceci peut être dangereux! Voici les rêgles.

    • $\begin{pmatrix} a & b & c \\ d & e & f \end{pmatrix} + z = \begin{pmatrix} a & b & c \\ d & e & f \end{pmatrix} + \begin{pmatrix} z & z & z \\ z & z & z \end{pmatrix} = \begin{pmatrix} a+z & b+z & c+z \\ d+z & e+z & f+z \end{pmatrix}$

    • $\begin{pmatrix} a & b & c \\ d & e & f \end{pmatrix} + \begin{pmatrix} x & y & z \end{pmatrix} = \begin{pmatrix} a & b & c \\ d & e & f \end{pmatrix} + \begin{pmatrix} x & y & z \\ x & y & z \end{pmatrix} = \begin{pmatrix} a+x & b+y & c+z \\ d+x & e+y & f+z \end{pmatrix}$

    • $\begin{pmatrix} a & b & c \\ d & e & f \end{pmatrix} + \begin{pmatrix} y \\ z \end{pmatrix} = \begin{pmatrix} a & b & c \\ d & e & f \end{pmatrix} + \begin{pmatrix} y & y & y \\ z & z & z \end{pmatrix} = \begin{pmatrix} a+y & b+y & c+y \\ d+z & e+z & f+z \end{pmatrix}$

    • $\begin{pmatrix} a & b & c \end{pmatrix} + \begin{pmatrix} y \\ z \end{pmatrix} = \begin{pmatrix} a & b & c \\ a & b & c \end{pmatrix} + \begin{pmatrix} y & y & y \\ z & z & z \end{pmatrix} = \begin{pmatrix} a+y & b+y & c+y \\ a+z & b+z & c+z \end{pmatrix}$

Voici les exemples correspondant. On remarquera qu'une matrice est une sorte de liste de liste dont les sous lignes correspondent aux ligne de la matrice.

In [48]:
x = np.array([[1, 2, 3], [4, 5, 6]])
print(x)
[[1 2 3]
 [4 5 6]]
In [49]:
x + 5
Out[49]:
array([[ 6,  7,  8],
       [ 9, 10, 11]])
In [50]:
y = np.array([7, 8, 9])
x + y
Out[50]:
array([[ 8, 10, 12],
       [11, 13, 15]])
In [51]:
y = np.array([[8], [9]])
x + y
Out[51]:
array([[ 9, 10, 11],
       [13, 14, 15]])
In [52]:
x = np.array([1, 2, 3])
x + y
Out[52]:
array([[ 9, 10, 11],
       [10, 11, 12]])
  • Fonction universelle: Ce sont des fonctions qui opèrent sur des tableaux éléments par éléments. On retrouve toutes les fonctions usuelles cos, sin, sqrt, exp, log,... Voici deux exemples moins triviaux.
In [54]:
x = np.array([1, 2, 3])
y = np.array([2, 4, 6])
In [55]:
np.divide(x, y)
Out[55]:
array([0.5, 0.5, 0.5])
In [56]:
np.power(x,y)
Out[56]:
array([  1,  16, 729])

Numpy manipulations¶

Voici maintenant plus de détail sur la manipulation des tableaux numpy.

Création d'un ndarray à partir d'une liste¶

In [63]:
x = np.array([1, 2, 3, 4, 5, 6]) 

print(x.dtype)   # indique le type des données dans le tableau
print(x.shape)   # retourne un tuple donnant la taille du tableau
int64
(6,)
In [67]:
x.shape = (2,3)  #  redéfinie la taille de x 
print(x)
[[1 2 3]
 [4 5 6]]
In [61]:
x2 = np.array([[1, 2, 3],[4, 5, 6]]) # pour obtenir la même matrice
print(x2)
[[1 2 3]
 [4 5 6]]
In [29]:
x = x.astype('complex') # modification du type des données
print(x.dtype) 
print(x)
complex128
[[ 1.+0.j  2.+0.j  3.+0.j]
 [ 4.+0.j  5.+0.j  6.+0.j]]

Génération de tableau¶

np.ones(size) renvoie une matrice de 1, où size est un tuple décrivant la taille du tableau.

In [68]:
print( np.ones((3,2)) ) 
[[1. 1.]
 [1. 1.]
 [1. 1.]]

Si on veut juste une matrice ligne, size est juste un entier

In [71]:
print( np.ones(3) )
[1. 1. 1.]

Pour une matrice ou ligne de zéros

In [39]:
print( np.zeros((3,2)) ) 
[[ 0.  0.]
 [ 0.  0.]
 [ 0.  0.]]
In [40]:
print( np.zeros(3) ) 
[ 0.  0.  0.]

Pour la matrice identité de taille n (=3 ci-dessous)

In [73]:
print( np.eye(3) ) 
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

Pour une matrice diagonale

In [74]:
print( np.diag([1,2,3]) )
[[1 0 0]
 [0 2 0]
 [0 0 3]]

On peut générer les coefficients d'une matrice à partir d'une fonction à l'aide de la fonction np.fromfunction

In [75]:
M = np.fromfunction(lambda i, j: (i-2)**2+(j-2)**2, (5,5))

print(M)
[[8. 5. 4. 5. 8.]
 [5. 2. 1. 2. 5.]
 [4. 1. 0. 1. 4.]
 [5. 2. 1. 2. 5.]
 [8. 5. 4. 5. 8.]]

Pour accéder aux éléments des tableaux on procède d'une manière similaire aux listes.

In [76]:
print(M[0,0])
8.0
In [77]:
print(M[-1,-1])
8.0
In [62]:
print(M[3,:])
[ 5.  2.  1.  2.  5.]
In [80]:
M[:,0]
Out[80]:
array([8., 5., 4., 5., 8.])
In [64]:
print(M[::2,::2])
[[ 8.  4.  8.]
 [ 4.  0.  4.]
 [ 8.  4.  8.]]
In [65]:
print(M[2:5,2:5])
[[ 0.  1.  4.]
 [ 1.  2.  5.]
 [ 4.  5.  8.]]
In [66]:
print(M[0, [1,2,4]])
[ 5.  4.  8.]

On peut aussi appliquer ce qu'on appelle des masques.

In [67]:
print(M[M % 2 == 0])  
[ 8.  4.  8.  2.  2.  4.  0.  4.  2.  2.  8.  4.  8.]
In [81]:
M % 2 == 0
Out[81]:
array([[ True, False,  True, False,  True],
       [False,  True, False,  True, False],
       [ True, False,  True, False,  True],
       [False,  True, False,  True, False],
       [ True, False,  True, False,  True]])

Ici, on ne considère que les indices pour lesquelles les coefficents sont pairs, et on peut leurs réaffecter une nouvelle valeur.

In [68]:
M[M % 2 == 0] = 0
print(M)
[[ 0.  5.  0.  5.  0.]
 [ 5.  0.  1.  0.  5.]
 [ 0.  1.  0.  1.  0.]
 [ 5.  0.  1.  0.  5.]
 [ 0.  5.  0.  5.  0.]]

L'équivalent de range est arange

In [83]:
print( np.arange(16) ) # vecteur d'élément de 0 à n par pas de 1 
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]
In [84]:
print( np.arange(0.1, 1, 0.2) ) # vecteur d'éléements de a à b par 
                                 # pas de p (jusqu'à b-p!): np.arange(a,b,p)
[0.1 0.3 0.5 0.7 0.9]

Par contre si on ne veut pas se donner le pas mais le nombre d'élément on utlise np.linspace(a,b,n), n étant le nombre d'éléments

In [86]:
print( np.linspace(0.1, 1, 12) )
[0.1        0.18181818 0.26363636 0.34545455 0.42727273 0.50909091
 0.59090909 0.67272727 0.75454545 0.83636364 0.91818182 1.        ]
In [89]:
a = np.linspace(0.1, 2, 12).reshape(3,4) # vecteur qu'on peut redimensionner
print(a)
[[0.1        0.27272727 0.44545455 0.61818182]
 [0.79090909 0.96363636 1.13636364 1.30909091]
 [1.48181818 1.65454545 1.82727273 2.        ]]
In [91]:
print( np.tril(a,-1) ) # extraction la partie triangulaire inférieure de a
[[0.         0.         0.         0.        ]
 [0.79090909 0.         0.         0.        ]
 [1.48181818 1.65454545 0.         0.        ]]
In [76]:
print( np.triu(a, 1) )  # de la même manière pour les triangulaires supérieures
[[ 0.          0.27272727  0.44545455  0.61818182]
 [ 0.          0.          1.13636364  1.30909091]
 [ 0.          0.          0.          2.        ]]
In [77]:
print( np.triu(a, 2) )
[[ 0.          0.          0.44545455  0.61818182]
 [ 0.          0.          0.          1.30909091]
 [ 0.          0.          0.          0.        ]]

Pour une matrice diagonale de même diagonale que a (on extrait la diagonale de a dans un premier temps).

In [78]:
print( np.diag(np.diag(a)) )
[[ 0.1         0.          0.        ]
 [ 0.          0.96363636  0.        ]
 [ 0.          0.          1.82727273]]

Operation sur les matrices¶

In [94]:
M = np.arange(16).reshape(4,4)

M.max()        # retourne le max des coefficients de M
Out[94]:
15
In [95]:
M.max(axis=0)  # retourne le max de chaque colonne 
Out[95]:
array([12, 13, 14, 15])
In [96]:
M.max(axis=1)  # retourne le max de chaque ligne
Out[96]:
array([ 3,  7, 11, 15])

Rque: np.max(M), np.max(M, axis = ?) marchent aussi! On a le même type de fonction pour le minimum en remplaçant max par min.

In [97]:
M.sum()        # retourne la somme des coefficients de M
Out[97]:
120
In [84]:
M.sum(axis=0)  # retourne la somme de chaque colonne 
Out[84]:
array([24, 28, 32, 36])
In [85]:
M.sum(axis=1)  # retourne la somme de chaque ligne
Out[85]:
array([ 6, 22, 38, 54])

Rque: np.sum(M), np.sum(M, axis = ?) marchent aussi!

In [86]:
M1 = np.arange(1,5).reshape(2,2)
M2 = np.arange(1,5).reshape(2,2)

print(M1 + M2)
[[2 4]
 [6 8]]
In [87]:
print(2*M1) 
[[2 4]
 [6 8]]
In [88]:
print(M1 * M2)  # produit terme à terme
[[ 1  4]
 [ 9 16]]
In [89]:
print(M1**2)  # carré terme à terme
[[ 1  4]
 [ 9 16]]
In [90]:
np.concatenate((M1,M2), axis = 1) # concatenation
Out[90]:
array([[1, 2, 1, 2],
       [3, 4, 3, 4]])
In [91]:
np.concatenate((M1,M2), axis = 0) 
Out[91]:
array([[1, 2],
       [3, 4],
       [1, 2],
       [3, 4]])
In [92]:
x = np.arange(3)
y = 2*np.arange(3)

print(np.dot(x,y)) # produit scalaire de vecteurs
10
In [104]:
M = np.arange(6).reshape(2, 3)
x = np.ones((3, 1)) # vecteur de taille 3x1

print( np.dot(M, x) ) # produit de M1 par x ( M1.dot(x) marche aussi)
[[ 3.]
 [12.]]
In [105]:
# produit de matrice

print( np.dot(M1,M) )  # Attention au dimension des matrice
                       # Rque: M1.dot(M) marche aussi
[[ 6  9 12]
 [12 19 26]]
In [98]:
# transposée de M
                     
print( np.transpose(M) )      #  M.transpose() marche aussi
[[0 3]
 [1 4]
 [2 5]]
In [109]:
M.T
Out[109]:
array([[0, 3],
       [1, 4],
       [2, 5]])
In [99]:
# trace de M1

print( np.trace(M) )         # M.trace() marche aussi
4

Quelques figures pour finir avec pyplot¶

In [125]:
w = np.linspace(-1, 1, 50)

plt.plot(w, np.sin(2*np.pi*w), label = 'fonction') # label sert pour la legende

plt.ylabel('fct $\sin(2\pi \omega)$')
plt.xlabel('argument $\omega$')
plt.title("tracer d'une fonction periodique")
plt.legend()
plt.show()
No description has been provided for this image

Pour des formules plus avancées il faut plutôt utiliser les commandes suivantes :

In [ ]:
plt.rc('text', usetex = True)
In [127]:
w = np.linspace(-1, 1, 50)

plt.plot(w, np.sin(2*np.pi*w), label = 'fonction') # label sert pour la legende

plt.rc('text',usetex = True)
plt.ylabel(r'fct $\sin(2\pi \omega)$')

plt.xlabel('argument $\omega$')
plt.title("tracer d'une fonction periodique")
plt.legend()
plt.show()
No description has been provided for this image

Nombres aléatoires¶

Pour génerer des nombres aléatoires, on peut utiliser le sous module random de numpy.

In [112]:
import numpy.random as npr

Voir https://docs.scipy.org/doc/numpy/reference/routines.random.html pour les différentes lois et possibilités du module.

La graine peut être fixé pour forcer des tirages identiques lors de l'élaboration du code. Par exemple, npr.seed(123).

matrice aléatoire de coefficients indépendants de loi uniforme sur [-1,1]¶

In [113]:
x = npr.uniform(-1, 1, (200, 2)) 

On peut représenter ces points tirés aléatoirement sur un graphique.

In [131]:
plt.scatter(x[:,0], x[:,1], s = 20, label = 'point')

plt.axis([-1.05, 1.05, -1.05, 1.05]); # défini les axes

plt.ylabel('$U_2$')
plt.xlabel('$U_1$')
plt.title('$(U_1,U_2)$')
plt.show()
No description has been provided for this image

On peut faire des histogrammes.

In [117]:
x = npr.randn(100000)
n_bins = 50 # nbre de classe de l'histogramme 
plt.hist(x,n_bins, density=True, facecolor='green', label = 'loi N(0,1)')
plt.title('histogramme')
plt.ylabel('probabilite')
plt.xlabel('variable')
plt.legend()
plt.show()
No description has been provided for this image

ou aussi des figures avec des sous-figures.

In [119]:
plt.figure(1) # On décalre la figure

w = np.linspace(-1, 1, 50)

plt.subplot(121) # On déclare la première sous-figure

plt.plot(w, np.sin(2*np.pi*w),'b-', label = 'sin')
plt.plot(w, np.cos(2*np.pi*w),'g-',label = 'cos')
plt.ylabel('fct $\sin(2\pi \omega)$')
plt.xlabel('argument $\omega$')
plt.title("tracer d'une fonction periodique")
plt.legend()

plt.subplot(122)  # On déclare la deuxième sous-figure

x = npr.uniform(-1, 1, (200, 2))  
plt.scatter(x[:,0], x[:,1], s = 20)
plt.axis([-1.05, 1.05, -1.05, 1.05]);

plt.ylabel('$U_2$')
plt.xlabel('$U_1$')
plt.title('$(U_1,U_2)$')

plt.show()
No description has been provided for this image