Introduction à numpy¶

Les tableaux avec numpy¶

In [1]:
import numpy as np

Numpy ajoute le type array qui est similaire à une liste (list) avec la condition supplémentaire que tous les éléments sont du même type. Nous concernant ce sera donc un tableau d’entiers, de flottants voire de booléens.

In [2]:
print(np.array([1.0, 2, "a"]))
f = np.array([1,3,7,10], float)
print(f)
entiers = np.array([1, 3, 7, 8.9], int)
print(entiers)
['1.0' '2' 'a']
[  1.   3.   7.  10.]
[1 3 7 8]

Initialiser un tableau 1D (liste)¶

Sans numpy

Du classique avec une liste compréhension que l'on a déjà écrit plusieurs fois dans ce cours

In [3]:
m = [0 for i in range(10)]
print(m)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
In [4]:
m = [ i*i for i in range(10)]
print(m)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Avec numpy

Numpy possède deux fonctions bien pratiques pour initialiser un tableau :

In [5]:
m = np.zeros(10)
print(m)
print type(m)
[ 0.  0.  0.  0.  0.  0.  0.  0.  0.  0.]
<type 'numpy.ndarray'>
In [6]:
m = np.ones(10, dtype=int)
print(m)
[1 1 1 1 1 1 1 1 1 1]

On remarquera qu'il n'y a pas de caractère de séparation entre les valeurs.

La fonction arange de numpy

In [7]:
m = np.arange(0,10,.5)#arange([debut, fin[, pas)
print(m)
[ 0.   0.5  1.   1.5  2.   2.5  3.   3.5  4.   4.5  5.   5.5  6.   6.5  7.
  7.5  8.   8.5  9.   9.5]

La fonction linspace de numpy

In [8]:
m = np.linspace(1, 10, 10)
print(m)
[  1.   2.   3.   4.   5.   6.   7.   8.   9.  10.]

Initialiser un tableau 2D¶

Sans numpy

In [9]:
tab = []
for i in range(4):
    ligne = []
    for j in range(5):
        ligne.append(0)
    tab.append(ligne)
print(tab)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
In [10]:
tab = [[0 for row in range(5)]for line in range(4)]
print(tab)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

Avec numpy

In [11]:
tab = np.zeros([4,5]) #np.zeros([lines, rows])
print(tab)
[[ 0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.]]

Redimensionner un tableau numpy

In [12]:
line = np.linspace(1, 10, 10)
tab = line.reshape(5,2)
print(tab)
[[  1.   2.]
 [  3.   4.]
 [  5.   6.]
 [  7.   8.]
 [  9.  10.]]

Simplification de l'indexation¶

In [13]:
tab = np.arange(24).reshape(4,6)
print(tab)
[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]
 [18 19 20 21 22 23]]
In [14]:
print tab[2,4]
16

Attributs de dimension

In [15]:
print m.shape # Le nombre de lignes et de colonnes du tableau
print m.size  # Le nombre d'éléments dans le tableau
print m.ndim  # Dimensions du tableau
(10,)
10
1

Exercices¶

Exo n°1 :

Écrire une fonction $generer\_points(n, tmax)$ qui prend en argument un entier $n$ indiquant le nombre de points de coordonnées $(x, y)$ à générer dans une zone géographique carrée plan de largeur $tmax \in\mathbb{N}$. Cette fonction renvoie un tableau numpy de dimension $(n\times 2)$ contenant un ensemble de points généré aléatoirement.

Exo n°2 :

Un robot se dépalce dans cette zone géographique, sa position est initialisée avec une variable globale. Écrire une fonction $get\_position\_robot()$ qui renvoie la position courante du robot et une fonction $set\_position\_robot(pt)$ qui met à jour la position du robot.

Exo n° 3 :

Écrire une fonction $distance\_a\_parcourir(A, B)$ qui prend en paramètre deux points sous forme de leurs coordonnées $[x, y]$ et renvoie la distance à parcourir entre ces deux points.

Exo n°4 :

Écrire une fonction $calculer\_distances(pts)$ qui prend en argument un tableau de $n$ points de type ndarray (comme celui de l'exercice n°1) et renvoie un tableau numpy de dimension ($n\times n$), tel que l’élément d’indice $i,j$ fournit la distance entre les points $i$ et $j$

Numpy : calcul avec les tableaux¶

Effectuer de nombreux calculs¶

Sans numpy

In [16]:
def carre(x):
    fin = len(x)
    for i in range(fin):
        x[i] = x[i]**2
    return x
In [17]:
%timeit x = range(10000) ; carre(x)
100 loops, best of 3: 2.69 ms per loop

Avec numpy

In [18]:
%timeit x = np.arange(10000) ; x**2
The slowest run took 7.65 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 23 µs per loop

Opérations sur les tableaux¶

In [19]:
m = [[1,1], [1,1]]
In [20]:
m = m + 1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-20-4731ab4cb092> in <module>()
----> 1 m = m + 1

TypeError: can only concatenate list (not "int") to list
In [21]:
m = np.ones((2,2))
m = m + 1
print m
[[ 2.  2.]
 [ 2.  2.]]
In [22]:
p = 5
print m*p
[[ 10.  10.]
 [ 10.  10.]]

Les tableaux sont importants parce qu’ils vous permettent de réaliser des opérations de traitement par lots sur des données sans écrire de boucles.

Cela est généralement appelé vectorisation.

Remplacer une boucle for sur une liste par une opération sur tableau entraîne un important gain de performance , mais cela peut aussi demander de reformuler le problème.

Tracer des graphiques¶

In [24]:
import matplotlib.pyplot as plt

def f(x):
    return x + np.cos(x)**2

x = np.linspace(-2, 2, 100)
y = f(x)

plt.plot(x,y)
plt.grid()
plt.show()

Ajouter des éléments dans un tableau numpy¶

On continue d'utiliser la fonction append mais attention :

In [25]:
help(np.append)
Help on function append in module numpy.lib.function_base:

append(arr, values, axis=None)
    Append values to the end of an array.
    
    Parameters
    ----------
    arr : array_like
        Values are appended to a copy of this array.
    values : array_like
        These values are appended to a copy of `arr`.  It must be of the
        correct shape (the same shape as `arr`, excluding `axis`).  If
        `axis` is not specified, `values` can be any shape and will be
        flattened before use.
    axis : int, optional
        The axis along which `values` are appended.  If `axis` is not
        given, both `arr` and `values` are flattened before use.
    
    Returns
    -------
    append : ndarray
        A copy of `arr` with `values` appended to `axis`.  Note that
        `append` does not occur in-place: a new array is allocated and
        filled.  If `axis` is None, `out` is a flattened array.
    
    See Also
    --------
    insert : Insert elements into an array.
    delete : Delete elements from an array.
    
    Examples
    --------
    >>> np.append([1, 2, 3], [[4, 5, 6], [7, 8, 9]])
    array([1, 2, 3, 4, 5, 6, 7, 8, 9])
    
    When `axis` is specified, `values` must have the correct shape.
    
    >>> np.append([[1, 2, 3], [4, 5, 6]], [[7, 8, 9]], axis=0)
    array([[1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]])
    >>> np.append([[1, 2, 3], [4, 5, 6]], [7, 8, 9], axis=0)
    Traceback (most recent call last):
    ...
    ValueError: arrays must have same number of dimensions

Axis = None

In [26]:
print(m)
[[ 2.  2.]
 [ 2.  2.]]
In [27]:
np.append(m, 24)
Out[27]:
array([  2.,   2.,   2.,   2.,  24.])
In [28]:
np.append(m, [25, 26, 27, 28, 29, 30])
Out[28]:
array([  2.,   2.,   2.,   2.,  25.,  26.,  27.,  28.,  29.,  30.])
In [29]:
print(m)
[[ 2.  2.]
 [ 2.  2.]]
In [30]:
m = np.append(m, [25, 26, 27, 28, 29, 30])
In [31]:
print(m)
[  2.   2.   2.   2.  25.  26.  27.  28.  29.  30.]

Axis = 0 Ajouter une ligne

In [32]:
m = np.arange(24).reshape(4,6)
In [33]:
np.append(m, [[0, 0, 0, 0, 0, 0]], axis=0)
Out[33]:
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],
       [ 0,  0,  0,  0,  0,  0]])

Attention les valeurs ajoutées doivent être contenues dans un tableau de même dimension

Axis = 1 Ajouter une colonne

In [34]:
np.append(m, [[0], [0], [0], [0]], axis=1)
Out[34]:
array([[ 0,  1,  2,  3,  4,  5,  0],
       [ 6,  7,  8,  9, 10, 11,  0],
       [12, 13, 14, 15, 16, 17,  0],
       [18, 19, 20, 21, 22, 23,  0]])

Transtypage list vers numpy array : numpy.asarray(a, dtype=None)¶

Liste simple

In [35]:
m = [i for i in range(15)]
print type(m), m
m = np.asarray(m, dtype = float)
print type(m)
m = m.reshape(3,5)
print m
<type 'list'> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
<type 'numpy.ndarray'>
[[  0.   1.   2.   3.   4.]
 [  5.   6.   7.   8.   9.]
 [ 10.  11.  12.  13.  14.]]

Liste 2-D

m = [[i for i in range(5)] for j in range(3)] print type(m), m m = np.asarray(m, dtype = float) print type(m) print m

Algèbre linéaire avec numpy¶

Somme de matrices¶

Si $A = (a_{ij})_{ \begin{array}{c}1\leq i\leq m\\1\leq j\leq n \end{array}}$ et $B = (b_{ij})_{ \begin{array}{c}1\leq i\leq m\\1\leq j\leq n \end{array}}$ sont deux matrices $m \times n$, on définit l’addition des matrices par $$A + B = (a_{ij} + b_{ij})_{ \begin{array}{c}1\leq i\leq m\\1\leq j\leq n \end{array}}$$

Produit de matrices¶

Si $A = (a_{ik})_{ \begin{array}{c}1\leq i\leq m\\1\leq k\leq n \end{array}}$ et $B = (b_{kj})_{ \begin{array}{c}1\leq k\leq n\\1\leq j\leq p \end{array}}$ sont deux matrices $m \times n$ et $n \times p$, on définit le produit des matrices par $$A \times B = \left( \sum_{\substack{k=1}}^n a_{ik}\times b_{kj} \right)_{ \begin{array}{c}1\leq i\leq m\\1\leq j\leq p \end{array}}$$

La somme sans numpy

In [36]:
a = [[-1, 4, 1],[0, 1, 1]]
b = [[1, 3, -1],[1, 5, 8]]
In [37]:
def matSum(a, b):
    try:                                                 #En cas de non respect de cette condition une erreur est levée 
        assert len(a)==len(b) and len(a[0])==len(b[0]) 
    except AssertionError:                               #Traitement de l'erreur
        print "Les matrices n'ont pas la meme dimension" #Message d'erreur
    s = [[0 for i in a[0]] for j in a]
    for i in range(len(a)):
        for j in range(len(a[0])):
            s[i][j] = a[i][j] + b[i][j]
    return s
matSum(a, b)
Out[37]:
[[0, 7, 0], [1, 6, 9]]

La somme avec numpy

In [38]:
a = np.asarray(a)
b = np.asarray(b)
print(a+b)
[[0 7 0]
 [1 6 9]]
In [39]:
c = np.array([[-1, 4],[0, 1]])
d = np.array([[1, 3],[1, 5]])
print(c + d)
[[0 7]
 [1 6]]

Le produit sans numpy

  • Soit $i$ l'indice pour parcourir les $m$ lignes de la matrice A ($i\in \mathbb{N}$)
  • Soit $j$ l'indice pour parcourir les $p$ colonnes de la matrice B ($j\in \mathbb{N}$)

Pour chaque ligne de A on parcourt toutes les colonnes de B. Le nombre d'éléments d'une ligne de A doit être égal au nombre d'éléments d'une colone de B. En d'autres termes le nombre de colonnes de A doit être égal au nombre de lignes de B

  • Soit $n$ le nombre de colonnes de A et le nombre de lignes de B

Pour une ligne et une colonne donnée on effectue la somme des produits : $a_{ik}$ et $b_{kj}$ avec $k\in [1, n]$

$$\sum_{\substack{k=1}}^n a_{ik}\times b_{kj}$$

Figure extraite d'un cours de : Gloria Faccanoni

<img src="images/produit.png" width=600 ; height = 600>

In [40]:
def matProd(a, b):
    m, n1 = len(a), len(a[0])
    n2, p = len(b), len(b[0])
    try: # mn * np = mp
        assert n1 == n2 
    except AssertionError:
        print "Les matrices n'ont pas les bonnes dimensions"
    
    s = [[0 for i in b[0]] for j in a]
    for i in range(m):                          # Les lignes de A
        for j in range(p):                      # Les colonnes de B
            for k in range(n1):                 
                s[i][j] += a[i][k] * b[k][j]    # Résultat du produit d'une ligne A par une colonne B     
    return s

Exemple 1:

In [41]:
a = [[1, 3, 0], [-1, 1, 2]]
b = [[1, 2, 0], [0, 2, 3], [0, -1, -2]]
matProd(a, b)
Out[41]:
[[1, 8, 9], [-1, -2, -1]]

Exemple 2 : $(A\times B) \neq (B\times A)$

In [42]:
a = [[1, -1], [3, 0]]
b = [[6, -5], [2, 1]]
print matProd(a, b)
print matProd(b, a)
[[4, -6], [18, -15]]
[[-9, -6], [5, -2]]

Le Produit avec numpy

Attention comme pour la somme le produit se fait terme à terme

In [43]:
c = np.asarray(a)
d = np.asarray(b)
print c*d
[[6 5]
 [6 0]]

Utilisation du slicing

In [44]:
def matProd(a,b):
    lines, columns = len(a), len(b[0])
    s = np.zeros((lines, columns))
    #return np.asarray([[sum(a[i,:]*b[:,j]) for j in range(columns)]for i in range(lines)])
    for i in range(lines):
        for j in range(columns):
            s[i,j] = sum(a[i,:]*b[:,j])   # Produit terme à terme pour une ligne de A et une colonne de B puis on somme
    return s
In [45]:
a = np.array([[1, 3, 0], [-1, 1, 2]])
b = np.array([[1, 2, 0], [0, 2, 3], [0, -1, -2]])
print(matProd(a, b))
[[ 1.  8.  9.]
 [-1. -2. -1.]]
In [46]:
print matProd(c, d)
print matProd(d, c)
[[  4.  -6.]
 [ 18. -15.]]
[[-9. -6.]
 [ 5. -2.]]

Opérations sur les fichiers¶

In [47]:
t = np.loadtxt ( "fichiers/loto.csv" , delimiter = ',')
print t
[[ 44.  31.  19.  44.  19.  12.]
 [ 45.  28.  33.  40.  35.  27.]
 [ 44.  14.  32.  47.  20.   6.]]

Avec le sclicing

In [48]:
print t[:,0]
[ 44.  45.  44.]
In [49]:
print t[:,0:1]
[[ 44.]
 [ 45.]
 [ 44.]]
In [ ]:
help(np.savetxt)

Application du slicing au traitement des images¶

Comment référencer une image avec numpy ?

In [ ]:
from PIL import Image
#chemin_absolu = "/chemin_depuis_la_racine/"
src = Image.open("images/chaplin.png")
#dst = Image.new("L",src.size)
width, height = src.size
print width, height
src_data = np.asarray(src.getdata()).reshape(height, width)
src.show()

On modifie l'intensité de niveau de gris sur la totalité de l'image

In [ ]:
src_data = src_data + 100
src_data = src_data.flatten().tolist()
src.putdata(src_data)#De la liste vers l'image
src.show()

On applique un effet de moiré avec le slicing sur les pixels définis par l'expression [::2,::2]

In [ ]:
tmp = src_data[::2,::2] #effet moiré
tmp *= 1.2
tmp += 100
src_data = src_data.flatten().tolist()
src.putdata(src_data)#De la liste vers l'image
src.show()

On modifie uniquement la luminosité de la zone centrale avec l'expression [height/6: 5height/6,width/6: 5width/6]

In [ ]:
tmp = src_data[height/6: 5*height/6,width/6: 5*width/6]
tmp *= 1.2
tmp += 100
src_data = src_data.flatten().tolist()
src.putdata(src_data)#De la liste vers l'image
src.show()

In [ ]: