Commit cb84f441 authored by Matthieu Boileau's avatar Matthieu Boileau
Browse files

Up 04

parent 6e27c409
%% Cell type:markdown id: tags:
# Une introduction à Numpy
![](fig/python-logo.png)
- Tableaux Numpy
- Création de tableaux
- Opérations basiques
- Indexation et slicing
- Algèbre linéaire
- Méthodes sur les tableaux
***
*Contenu sous licence [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0), largement inspiré de <https://github.com/pnavaro/python-notebooks>*
%% Cell type:markdown id: tags:
## Numpy
Le Python pur est peu performant pour le calcul. Les listes ne sont pas des objets efficaces pour représenter les tableaux numériques de grande taille. [Numpy](http://www.numpy.org/) a été créé à l'initiative de développeurs qui souhaitaient combiner la souplesse du langage python et des outils de calcul algébrique performants.
%% Cell type:markdown id: tags:
Numpy se base sur :
- le `ndarray` : tableau multidimensionnel
- des objets dérivés comme les *masked arrays* et les matrices
- les `ufunc`s : opérations mathématiques optimisées pour les tableaux
- des méthodes pour réaliser des opération rapides sur les tableaux :
- manipulation des *shapes*
- tri
- entrées/sorties
- FFT
- algébre linéaire
- statistiques
- calcul aléatoire
- et bien plus !
Numpy permet de calculer *à la Matlab* en Python. Il est un élément de base de l'écosystème [SciPy](https://www.scipy.org/)
%% Cell type:markdown id: tags:
## Démarrer avec Numpy
%% Cell type:code id: tags:
```
``` python
import numpy as np
print(np.__version__)
```
%% Cell type:markdown id: tags:
Utilisez l'auto-complétion pour lister les objets numpy :
%% Cell type:code id: tags:
```
``` python
#np.nd<TAB>
```
%% Cell type:markdown id: tags:
La rubrique d'aide est également précieuse
%% Cell type:code id: tags:
```
``` python
?np.ndarray
```
%% Cell type:markdown id: tags:
## Tableaux Numpy
### Une question de performance
%% Cell type:markdown id: tags:
- Les listes Python sont trop lentes pour le calcul et utilisent beaucoup de mémoire
- Représenter des tableaux multidimensionnels avec des listes de listes devient vite brouillon à programmer
%% Cell type:code id: tags:
```
``` python
from random import random
from operator import truediv
```
%% Cell type:code id: tags:
```
``` python
L1 = [random() for i in range(100000)]
L2 = [random() for i in range(100000)]
%timeit s = sum(map(truediv, L1, L2))
```
%% Cell type:code id: tags:
```
``` python
a1 = np.array(L1)
a2 = np.array(L2)
%timeit s = np.sum(a1/a2)
```
%% Cell type:markdown id: tags:
### La différence avec les listes
Les différences entre tableaux Numpy et listes Python :
- un `ndarray` a une taille fixée à la création
- un `ndarray` est composé d'éléments du même type
- les opérations sur les tableaux sont réalisées par des routines C pré-compilées et optimisées (éventuellement parallèles)
%% Cell type:code id: tags:
```
``` python
a = np.array([0, 1, 2, 3]) # list
b = np.array((4, 5, 6, 7)) # tuple
c = np.matrix('8 9 0 1') # string (syntaxe matlab)
```
%% Cell type:code id: tags:
```
``` python
print(a, b, c)
```
%% Cell type:markdown id: tags:
### Propriétés
%% Cell type:code id: tags:
```
``` python
a = np.array([1, 2, 3, 4, 5]) # On crée un tableau
```
%% Cell type:code id: tags:
```
``` python
type(a) # On vérifie son type
```
%% Cell type:code id: tags:
```
``` python
a.dtype # On affiche le type de ses éléments
```
%% Cell type:code id: tags:
```
``` python
a.itemsize # On affiche la taille mémoire (en octets) de chaque élément
```
%% Cell type:code id: tags:
```
``` python
a.shape # On retourne un tuple indiquant la longueur de chaque dimension
```
%% Cell type:code id: tags:
```
``` python
a.size # On retourne le nombre total d'éléments
```
%% Cell type:code id: tags:
```
``` python
a.ndim # On retourne le nombre de dimensions
```
%% Cell type:code id: tags:
```
``` python
a.nbytes # On retourne l'occupation mémoire
```
%% Cell type:markdown id: tags:
> - Toujours utiliser `shape` ou `size` pour les tableaux numpy plutôt que `len`
> - `len` est réservé aux tableaux 1D
%% Cell type:markdown id: tags:
## Création de tableaux Numpy
### Avec des valeur constantes
%% Cell type:code id: tags:
```
``` python
x = np.zeros((5, 3)) # On ne précise pas le type : on crée des flottants
print(x)
print(x.dtype)
```
%% Cell type:code id: tags:
```
``` python
x = np.zeros((5, 3), dtype=int) # On explicite le type
print(x)
print(x.dtype)
```
%% Cell type:markdown id: tags:
On dispose d'une panoplie de fonctions pour allouer des tableaux avec des valeurs constantes ou non initialisées (`empty`) :
`empty, empty_like, ones, ones_like, zeros, zeros_like, full, full_like`
%% Cell type:markdown id: tags:
### Création à partir d'une séquence
#### `arange`
C'est l'équivalent de `range` pour les listes.
%% Cell type:code id: tags:
```
``` python
np.arange(5) # entiers de 0 à 4
```
%% Cell type:code id: tags:
```
``` python
np.arange(5, dtype=np.double) # flottants de 0. à 4.
```
%% Cell type:code id: tags:
```
``` python
np.arange(2, 7) # entiers de 2 à 6.
```
%% Cell type:code id: tags:
```
``` python
np.arange(2, 7, 0.5) # flottants avec incrément de 0.5.
```
%% Cell type:markdown id: tags:
#### `linspace` et `logspace`
%% Cell type:code id: tags:
```
``` python
# 5 éléments régulièrement espacés entre 1 et 4, 1 et 4 inclus
np.linspace(1, 4, 5)
```
%% Cell type:code id: tags:
```
``` python
# 5 éléments régulièrement espacés selon une progression géométrique entre 10^1 et 10^4
np.logspace(1, 4, 5)
```
%% Cell type:markdown id: tags:
Consulter l'aide contextuelle pour plus de fonctionnalités
%% Cell type:code id: tags:
```
``` python
?np.logspace
```
%% Cell type:markdown id: tags:
### Exercice : créer les tableaux suivants
%% Cell type:markdown id: tags:
```python
[100 101 102 103 104 105 106 107 108 109]
```
> Astuce : `np.arange()`
%% Cell type:code id: tags:
```
``` python
# Votre code ici
```
%% Cell type:code id: tags:
```
``` python
# Solution
np.arange(100, 110)
```
%% Cell type:markdown id: tags:
```python
[-2. -1.8 -1.6 -1.4 -1.2 -1. -0.8 -0.6 -0.4 -0.2 0.
0.2 0.4 0.6 0.8 1. 1.2 1.4 1.6 1.8]
```
> Astuce : `np.linspace()`
%% Cell type:code id: tags:
```
``` python
# Votre code ici
```
%% Cell type:code id: tags:
```
``` python
# Solution
np.linspace(-2., 2., num=20, endpoint=False)
```
%% Cell type:markdown id: tags:
```python
[ 0.001 0.00129155 0.0016681 0.00215443 0.00278256
0.00359381 0.00464159 0.00599484 0.00774264 0.01]
```
> Astuce : `np.logspace()`
%% Cell type:code id: tags:
```
``` python
# Votre code ici
```
%% Cell type:code id: tags:
```
``` python
# Solution
np.logspace(-3, -2, num=10)
```
%% Cell type:markdown id: tags:
```python
[[ 0. 0. -1. -1. -1.]
[ 0. 0. 0. -1. -1.]
[ 0. 0. 0. 0. -1.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]]
```
> Astuce : `np.tri()`, `np.ones()`
%% Cell type:code id: tags:
```
``` python
# Votre code ici
```
%% Cell type:code id: tags:
```
``` python
# Solution
np.tri(7, 5, k=1) - np.ones((7, 5))
```
%% Cell type:markdown id: tags:
### Création de tableaux à partir de fichiers
Afin d'illustrer la création d'un tableau numpy à partir de données lues dans un fichier texte, on commence par sauvegarder un tableau dans un fichier.
%% Cell type:code id: tags:
```
``` python
x = np.arange(0.0, 5.0, 1.0)
y = x*10.
z = x*100.
```
%% Cell type:code id: tags:
```
``` python
np.savetxt('test.out', (x, y, z))
%pycat test.out
```
%% Cell type:code id: tags:
```
``` python
np.savetxt('test.out', (x, y, z), fmt='%1.4e') # Notation exponentielle
%pycat test.out
```
%% Cell type:code id: tags:
```
``` python
np.loadtxt('test.out')
```
%% Cell type:markdown id: tags:
`savetxt` et `loadtxt` ont leurs correspondants binaires :
- [`save`](https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.save.html#numpy.save) : enregistrer un tableau dans un fichier binaire au format `.npy`
- [`load`](https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.load.html#numpy.load): créer un tableau numpy à partir d'un fichier binaire numpy
%% Cell type:markdown id: tags:
### Format HDF5 avec H5py
Le format `.npy` n'est lisible que par Numpy.
À l'inverse, le format HDF5 est partagé par un grand nombre d'applications.
De plus, il permet de structurer des données binaires :
- en les nommants
- en ajoutant des métadonnées
- en assurant une portabilité indépendante de la plateforme
> voir le [manuel utilisateur](http://docs.h5py.org)
H5py est une interface Python pour HDF5.
%% Cell type:markdown id: tags:
On écrit dans le fichier `test.h5`
%% Cell type:code id: tags:
```
``` python
import h5py as h5
with h5.File('test.h5', 'w') as f:
f['x'] = x
f['y'] = y
f['z'] = z
```
%% Cell type:markdown id: tags:
On lit le fichier `test.h5` (qui pourrait provenir d'une autre application)
%% Cell type:code id: tags:
```
``` python
with h5.File('test.h5', 'r') as f:
for field in f.keys():
print(f"{field}: {f[field][()]}")
```
%% Cell type:markdown id: tags:
## Opérations basiques sur les tableaux
Par défaut, Numpy réalise les opérations arithmétiques éléments par éléments
%% Cell type:code id: tags:
```
``` python
a = np.array([0, 1, 2, 3])
b = np.array((4, 5, 6, 7))
print(a * b) # Produit éléments par éléments : pas le produit matriciel !
print(a + b)
```
%% Cell type:code id: tags:
```
``` python
print(a**2)
```
%% Cell type:code id: tags:
```
``` python
print(5 * a)
print(5 + a)
```
%% Cell type:code id: tags:
```
``` python
print(a < 2)
```
%% Cell type:code id: tags:
```
``` python
print(np.cos(a*np.pi)) # Utilisation de ufunc
```
%% Cell type:markdown id: tags:
De nombreuses ufunc dans la [doc officielle](https://docs.scipy.org/doc/numpy/user/quickstart.html#universal-functions).
%% Cell type:markdown id: tags:
## Indexation et slicing
### Indexation
Les règles différent légèrement des listes pour les tableaux multi-dimensionnels
%% Cell type:code id: tags:
```
``` python
# Indexation d'un tableau numpy
a = np.arange(9).reshape(3, 3) # Notez l'effet de la méthode reshape()
print(a)
print(a[1, 2])
```
%% Cell type:code id: tags:
```
``` python
# Indexation de la liste équivalente
liste = a.tolist()
print(liste)
print(liste[1][2])
```
%% Cell type:markdown id: tags:
### Slicing
Pour les tableaux unidimensionnels, les règles de slicing sont les mêmes que pour les séquences.
Pour les tableaux multidimensionnels numpy, le slicing permet d'extraire des séquences dans n'importe quelle direction.
%% Cell type:code id: tags:
```
``` python
print(a)
print(a[2, :]) # Retourne la 3ème ligne
print(a[:, 1]) # Retourne la deuxième colonne
```
%% Cell type:markdown id: tags:
**Attention :** contrairement aux listes, le slicing de tableaux ne renvoie pas une copie mais constitue une ***vue*** du tableau.
%% Cell type:code id: tags:
```
``` python
b = a[:, 1]
b[0] = -1
print(a)
```
%% Cell type:markdown id: tags:
`a` est aussi une vue du tableau `np.arange(9)` obtenue avec la méthode `reshape()` donc `a` et `b` sont deux vues différentes du même tableau :
%% Cell type:code id: tags:
```
``` python
b.base is a.base # b.base retourne le tableau dont b est la vue
```
%% Cell type:markdown id: tags:
Si on veut réaliser une copie d'un tableau, il faut utiliser la fonction `copy()`
%% Cell type:code id: tags:
```
``` python
b = a[:, 1].copy()
```
%% Cell type:markdown id: tags:
ici `b` n'est pas une vue mais une copie donc `b.base` vaut `None` et `a` n'est pas modifié.
%% Cell type:code id: tags:
```
``` python
print(b.base)
b[0] = 200
print(a)
```
%% Cell type:markdown id: tags:
### Exercice
Approcher la dérivée de $f(x) = \sin(x)$ par la méthode des différences finies :
$$\frac{\partial f}{\partial x} \approx \frac{f(x+\Delta x)-f(x)}{\Delta x}$$
Les valeurs seront calculées au milieu de deux abscisses discrètes successives.
%% Cell type:code id: tags:
```
``` python
# On crée un tableau de 40 points d'abscisse
x, dx = np.linspace(0., 4.*np.pi, 40, retstep=True)
y = np.sin(x)
```
%% Cell type:code id: tags:
```
``` python
%matplotlib inline
import matplotlib.pyplot as plt
plt.plot(x, np.cos(x)); # la dérivée analytique de sin() est cos()
# Votre code ici
# Décommentez la dernière ligne pour tracer la dérivée numérique dy_dx
# en fonction du milieu de 2 abscisses x_mil
#plt.plot(x_mil, dy_dx, 'rx');
```
%% Cell type:code id: tags:
```
``` python
# Solution
%matplotlib inline
import matplotlib.pyplot as plt
plt.plot(x, np.cos(x)) # la dérivée analytique de sin() est cos()
x_mil = 0.5*(x[1:] + x[:-1])
dy = y[1:] - y[:-1]
dy_dx = dy/dx
plt.plot(x_mil, dy_dx, 'rx') # x_mil est le milieu de deux abscisses
plt.title(r"$\rm{Derivative\ of}\ \sin(x)$");
```
%% Cell type:markdown id: tags:
## Quelques opérations d'algèbre linéaire
%% Cell type:code id: tags:
```
``` python
A = np.array([[1, 1],
[0, 1]])
B = np.array([[2, 0],
[3, 4]])
print(A*B) # produit élément par élément
print(A.dot(B)) # produit matriciel
print(np.dot(A, B)) # une autre syntaxe de produit matriciel
print(A@B) # encore un autre produit matriciel
```
%% Cell type:code id: tags:
```
``` python
a = np.array([[1.0, 2.0],
[3.0, 4.0]])
print(a)
```
%% Cell type:code id: tags:
```
``` python
# Deux syntaxes équivalentes pour la transposition
print(a.transpose())
print(a.T)
```
%% Cell type:code id: tags:
```
``` python
print(np.linalg.inv(a)) # inversion de matrice
```
%% Cell type:code id: tags:
```
``` python
print(np.trace(a)) # trace
```
%% Cell type:code id: tags:
```
``` python
print(np.eye(2)) # "eye" représente "I", la matrice identité
```
%% Cell type:code id: tags:
```
``` python
y = np.array([[5.], [7.]]) # Résoudre A*x = y
x = np.linalg.solve(a, y)
print(x)
print(a@x == y)
```
%% Cell type:code id: tags:
```
``` python
j = np.array([[0.0, -1.0], [1.0, 0.0]])
print(np.dot(j, j)) # produit matriciel
print(np.linalg.eig(j)) # Extraction des valeurs propres
```
%% Cell type:markdown id: tags:
## Les méthodes associées aux tableaux
%% Cell type:markdown id: tags:
Elles sont très nombreuses : impossible de toutes les lister dans le cadre de ce cours.
Citons brièvement :
- `min`, `max`, `sum`
- `sort`, `argmin`, `argmax`
- statistiques basiques : `cov`, `mean`, `std`, `var`
À chercher dans la [doc officielle](https://docs.scipy.org/doc/numpy/user/quickstart.html#).
%% Cell type:markdown id: tags:
## Programmation avec Numpy
- Les opérations sur les tableaux sont rapides, les boucles python sont lentes => **Éviter les boucles !**
- C'est une gymnastique qui nécessite de l'entraînement
- Le résultat peut devenir difficile à lire et à débugguer, par exemple dans le cas de boucles contenant de multiples conditions
- D'autres options sont alors envisageables (Cython, Pythran, Numba, etc.)
%% Cell type:markdown id: tags:
## Références
- [NumPy reference](http://docs.scipy.org/doc/numpy/reference/)
- [Numpy by Konrad Hinsen](http://calcul.math.cnrs.fr/Documents/Ecoles/2013/python/NumPy%20avance.pdf)
- [Cours de Pierre Navaro](https://github.com/pnavaro/python-notebooks)
- [Scipy Lecture notes](http://www.scipy-lectures.org/)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment