Informatique Python Readthedocs Io FR m2
Informatique Python Readthedocs Io FR m2
Yannick Copin
22/02/21, 11:47
Table des matières
1 Introduction 1
1.1 Pourquoi un module d’analyse scientifique ? . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Pourquoi Python ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Informations pratiques 2021 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4 Index et recherche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 Installation et interpréteurs 5
2.1 Notions d’Unix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3 Interpréteurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3 Initiation à Python 11
3.1 Types de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2 Structures de programmation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.3 Les chaînes de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.4 Objets itérables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.5 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.6 Bibliothèques et scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.7 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.8 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.9 Entrées-sorties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4 Python avancé 27
4.1 Fonctionnalités avancées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.2 Programmation Orientée Objet avancée . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.3 Éléments passés sous silence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.4 Python 3.x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5 Bibliothèque standard 39
5.1 Gestion des arguments/options de la ligne de commande . . . . . . . . . . . . . . . . . . 39
5.2 Pickle : sérialisation des données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.3 Batteries included . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.4 Text/Graphical User Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
i
7.2 Astropy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
7.3 Autres bibliothèques scientifiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
8 Développer en Python 79
8.1 Le zen du Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8.2 Développement piloté par les tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
8.3 Outils de développement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
9 Références supplémentaires 89
9.1 Documentation générale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
9.2 Listes de liens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
9.3 Livres libres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
9.4 Cours en ligne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
10 Exemples 93
10.1 Mean power (fonction, argparse) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
10.2 Animal (POO) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
10.3 Cercle circonscrit (POO, argparse) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
10.4 Matplotlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
11 Exercices 109
11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
11.2 Manipulation de listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
11.3 Programmation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
11.4 Manipulation de tableaux (arrays) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
11.5 Méthodes numériques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
11.6 Visualisation (matplotlib) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
11.7 Mise en oeuvre de l’ensemble des connaissances acquises . . . . . . . . . . . . . . . . . . . 116
11.8 Exercices en vrac . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
12 Projets 119
12.1 Physique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
12.2 Astrophysique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
12.3 Divers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
12.4 Mini-projets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
ii
20 Carré magique 157
24 Animaux 167
25 Particules 171
29 Quadrature 189
Bibliographie 231
Index 233
iii
iv
CHAPITRE 1
Introduction
1
Analyse scientifique avec Python, Version Janvier 2021
Liens :
— Getting Started
— Python Advocacy
Calendrier
Date TD
lun 25/01/21 9h-12h 16h-18h
mar 26 9h-12h 16h-18h
jeu 28 9h-12h 16h-18h
Participants (etu.univ-lyon1.fr)
2 Chapitre 1. Introduction
Analyse scientifique avec Python, Version Janvier 2021
Évaluation
L’évaluation de l’atelier se fera sur un mini-projet à développer durant la semaine (intégralement dédiée
à cette activité, y compris en dehors des heures de cours) :
— s’organiser en binôme, et choisir un sujet de Mini-projets, ou proposer le sien propre (à discuter) ;
— partager le code correspondant sous la forme d’un notebook Datalore NOM1_NOM2 (pouvant évi-
demment faire appel à des bibliothèques externes) partagé avec yncopin[AT]gmail[DOT]com,
ou bien (p.ex. pour les programmes demandant une intéraction avec l’utilisateur) d’un fichier
NOM1_NOM2.py (qui doit être fonctionnel et autonome) envoyé à la même adresse ;
— date limite de soumission des projets : jeu. 28/01, 16h
— lors de la soutenance, éxécuter et présenter oralement en 5 min les objectifs, les méthodes choisies
et les résultats.
— Planning des soutenances
4 Chapitre 1. Introduction
CHAPITRE 2
Installation et interpréteurs
— Notions d’Unix
— Installation
— Interpréteurs
— L’interpréteur de base python
— L’interpréteur avancé ipython
— Les interfaces jupyter
— Interface jupyter notebook
— Interface jupyter lab
— Interpréteurs en ligne
Python est un langage disponible sur de très nombreuses plateformes 1 ; cependant, dans le cadre de ce
cours, nous supposerons être sous un système d’exploitation de la famille Unix (p.ex. Linux, Mac OS X).
Les concepts suivants sont supposés connus :
— ligne de commande : éxécutables et options ;
— arborescence : chemin relatif ([./]...) et absolu (/...), navigation (cd) ;
— gestion des fichiers (ls, rm, mv) et répertoires (mkdir) ;
— gestion des éxécutables : $PATH, chmod +x ;
— gestion des processus : &, Ctrl-c, Ctrl-z + bg ;
— variables d’environnement : export, .bashrc.
1. Y compris maintenant sur des calculettes!
5
Analyse scientifique avec Python, Version Janvier 2021
Liens :
2.2 Installation
Ce cours repose essentiellement sur les outils suivants :
— Python 3.6+ (inclus l’interpréteur de base et la bibliothèque standard) ;
— les bibliothèques scientifiques Numpy et Scipy ;
— la bibliothèque graphique Matplotlib ;
— un interpréteur interactif évolué, p.ex. ipython ou jupyter ;
— un éditeur de texte évolué, p.ex. emacs, vi, gedit ou Atom.
Si vous souhaitez utiliser votre ordinateur personnel, ces logiciels peuvent être installés indépendamment,
sous Linux, Windows ou MacOS. Il existe également des distributions python « clés en main », p.ex.
Conda (multi-plateforme).
Installations locales
Si vous avez le contrôle entier de votre ordinateur, il peut être préférable d’utiliser le gestionnaire de
paquets du système (p.ex. synaptic sur Ubuntu), avec le risque d’installer des versions un peu anciennes.
Même si vous travaillez sur un ordinateur public (p.ex. en salle Ariane) , il est relativement aisé d’ins-
taller sous votre compte les programmes ou librairies Python (p.ex. ipython) manquantes à l’aide du
gestionnaire d’installation pip.
— Vérifier la disponibilité du code sous PYthon Package Index (plus de 270 000 projets !)
— Installer p.ex. ipython (mode single user) :
pip3 install --user ipython
2.3 Interpréteurs
Le python peut également faire office d’interpréteur interactif de commandes, mais avec peu de fonc-
tionnalités :
$ python3
Python 3.6.11 (default, Jun 29 2020, 05:15:03)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Liens :
Note : Je ne parle pas ici d”Integrated Development Environment, surcouche logicielle à l’interpréteur
de base (p.ex. spyder, pyCharm, etc.).
$ ipython
Python 3.6.11 (default, Jun 29 2020, 05:15:03)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.16.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]:
Liens :
— Tutorial
— IPython Tips & Tricks
Issu du développement de ipython, jupyter découple strictement le kernel 2 (le backend), en charge de
l’interprétation et de l’exécution des commandes, de l’interface (le frontend), en charge de l’interaction
avec l’utilisateur et le reste du monde.
L’interface jupyter notebook introduit la notion de notebook (fichier JSON d’extension .ipynb), accessible
via une application web (utilisable depuis le navigateur) incorporant lignes de code, résultats, textes
formatés, équations, figures, etc., et fournissant des outils d’édition et de conversion (HTML, LaTeX,
présentation, etc.) et une documentation en ligne :
$ jupyter notebook
Cette commande initialise un kernel en arrière plan (qui peut servir plusieurs notebooks), et ouvre le
notebook dashboard, à partir duquel vous pouvez créer de nouveaux notebooks ou en ouvrir d’anciens.
2. Pas nécessairement Python, d’où le nom de jupyter pour Julia-Python-R, les trois langages initialement supportés.
Il en existe maintenant plusieurs dizaines.
2.3. Interpréteurs 7
Analyse scientifique avec Python, Version Janvier 2021
Liens
— nbviewer, visualiseur en ligne de notebook (non interactif, voir ci-dessous pour des interpréteurs
en ligne) ;
— Python Notebook Viewer, une extension firefox de visualisation de notebook ;
— A gallery of interesting Jupyter Notebooks ;
— Unofficial Jupyter Notebook Extensions.
L’interface JupyterLab permet une expérience encore plus intégrée, incluant des outils de développement
(notebook, console ipython, explorateur de fichiers, terminal, etc.) :
$ jupyter lab
Fig. 2.2 – Copie d’écran d’un Jupyter Lab, incluant le notebook canon.ipynb.
Note : L’univers JupyterLab est en développement très actif, et peut être complété de nombreuses
extensions (incompatibles avec les extensions notebook).
Comparatif
MyBinder
Le lien suivant permet d’ouvrir ce cours avec une interface de type jupyter via le service MyBinder :
Datalore
Pour le cours en distanciel, nous utiliserons le service datalore, qui permet une collaboration en temps
réel.
— Créer un compte sur https://datalore.io/,
— Suivre le tutoriel,
— Créer un notebook,
— Le partager avec un collègue (p.ex. yncopin[AT]gmail[DOT]com) pour tester la collaboration en
temps réel,
— Le publier, pour tester les commentaires en ligne.
2.3. Interpréteurs 9
Analyse scientifique avec Python, Version Janvier 2021
Initiation à Python
— Types de base
— Structures de programmation
— Les chaînes de caractères
— Indexation
— Sous-liste ( slice)
— Méthodes
— Formatage
— Objets itérables
— Fonctions
— Bibliothèques et scripts
— Bibliothèques externes
— Bibliothèques personnelles et scripts
— Exceptions
— Classes
— Entrées-sorties
— Intéractif
— Fichiers texte
— None (rien)
— Chaînes de caractères : str
— Entre (simples ou triples) apostrophes ' ou guillemets " : 'Calvin', "Calvin'n'Hobbes",
'''Deux\nlignes''', """'Pourquoi?' demanda-t-il."""
— Conversion : str(3.2)
— Types numériques :
— Booléens bool (vrai/faux) : True, False, bool(3)
— Entiers int (pas de valeur limite explicite, correspond au moins au long du C) : -2, int(2.1),
int("4")
— Réels float (entre ±1.7e±308, correspond au double du C) : 2., 3.5e-6, float(3)
— Complexes complex : 1+2j (sans espace), 5.1j, complex(-3.14), complex('j')
11
Analyse scientifique avec Python, Version Janvier 2021
— Objets itérables :
— Listes list : ['a', 3, [1, 2], 'a']
— Listes immuables tuple : (2, 3.1, 'a', []) (selon les conditions d’utilisation, les paren-
thèses ne sont pas toujours nécessaires)
— Listes à clés dict : {'a':1, 'b':[1, 2], 3:'c'}
— Ensembles non ordonnés d’éléments uniques set : {1, 2, 3, 2}
Attention : en Python 3, range() n’est plus un constructeur de liste, mais un itérateur, qui
doit être converti en liste explicitement (équivalent à xrange de Python 2) :
>>> range(3) # Itérateur
range(0, 3)
>>> list(range(3)) # Liste
[0, 1, 2]
>>> type(l)
<type 'list'>
>>> isinstance(l, tuple)
False
Liens :
— Les blocs sont définis par l”indentation (en général par pas de quatre espaces) 1 .
Avertissement : Évitez autant que possible les caractères de tabulation, source de confusion.
Configurez votre éditeur de texte pour qu’il n’utilise que des espaces.
— Une instruction par ligne en général (ou instructions séparées par ;).
— Les commentaires commencent par #, et s’étendent jusqu’à la fin de la ligne.
1. ou from __future__ import braces :-)
— Expression booléenne : une condition est une expression s’évaluant à True ou False :
— False : test logique faux (p.ex. 3 > 4), valeur nulle, chaîne vide (''), liste vide ([]), etc.
— True : test logique vrai (p.ex. 2 in [1, 2, 3]), toute valeur ou objet non nul (et donc s’éva-
luant par défaut à True sauf exception)
— Tests logiques : ==, !=, >, >=, in, etc.
>>> x = 3
>>> not ((x <= 0) or (x > 5))
True
>>> 0 < x <= 5 # Conditions cha^ınées
True
— Boucle for : for element in iterable :, s’éxecute sur chacun des éléments d’un objet itérable :
>>> for val in ['un', (2, 3), 4]: # Itération sur une liste de 3 éléments
... print(val)
un
(2, 3)
4
Note : la logique des boucles Python est assez différente des langages C[++]/fortran, pour
lesquels l’itération porte sur les indices plutôt que sur les éléments eux-mêmes.
— Boucle while : while condition : se répéte tant que la condition est vraie, ou jusqu’à une sortie
explicite avec break.
Attention : aux boucles infinies, dont la condition d’exécution reste invariablement vraie
(typiquement un critère de convergence qui n’est jamais atteint). On peut toujours s’en protéger
en testant en outre sur un nombre maximal (raisonnable) d’itérations :
niter = 0
while (error > 1e-6) and (niter < 100):
error = ... # A priori, error va décro^ıtre, et la boucle s'interrompre...
niter += 1 # ... mais on n'est jamais assez prudent!
if niter == 100: # Ne pas oublier de tester l'absence de convergence!!!
print("Erreur de convergence!")
Note : Il n’y a pas en Python d’équivalent natif à l’instruction switch du C, ni à la structure do ...
while True:
# calcul de la condition d'arr^et
if condition:
break
Exercices :
3.3.1 Indexation
Les chaînes de caractères sont des objets itérables – c.-à-d. constitués d’éléments (ici les caractères)
sur lesquels il est possible de « boucler » (p.ex. avec for) – et immuables – c.-à-d. dont les éléments
individuels ne peuvent pas être modifiés intrinsèquement.
Note : Comme en C[++], l’indexation en Python commence à 0 : le 1er élément d’une liste est l’élément
n°0, le 2e est le n°1, etc. Les n éléments d’une liste sont donc indexés de 0 à n-1.
>>> alpha[3:7] # De l'élément n°3 (inclus) au n°7 (exclu), soit 7-3=4 éléments
'defg'
>>> alpha[:3] # Du n°0 (défaut) au n°3 (exclu), soit 3 éléments
'abc'
>>> alpha[-3:] # Du n°26-3=23 (inclus) au dernier inclus (défaut)
'xyz'
>>> alpha[3:9:2] # Du n°3 (inclus) au n°9 (exclu), tous les 2 éléments
'dfh'
>>> alpha[::5] # Du 1er au dernier élément (défauts), tous les 5 éléments
'afkpuz'
3.3.3 Méthodes
Comme la plupart des objets en Python, les chaînes de caractères disposent de nombreuses fonctionnalités
– appelées « méthodes » en POO (Programmation Orientée Objet) – facilitant leur manipulation :
>>> enfant, peluche = "Calvin", 'Hobbes' # Affectations mutiples
>>> titre = enfant + ' et ' + peluche; titre # +: Concaténation de cha^ınes
'Calvin et Hobbes'
>>> titre.replace('et', '&') # Remplacement de sous-cha^ınes (→ nouvelle cha^ıne)
'Calvin & Hobbes'
>>> titre # titre est immuable et reste inchangé
'Calvin et Hobbes'
>>> ' & '.join(titre.split(' et ')) # Découpage (split) et jonction (join)
'Calvin & Hobbes'
>>> 'Hobbes' in titre # in: Test d'inclusion
True
>>> titre.find("Hobbes") # str.find: Recherche de sous-cha^ıne
10
>>> titre.center(30, '-')
'-------Calvin et Hobbes-------'
>>> dir(str) # Liste toutes les méthodes des cha^ınes
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__
˓→format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__
˓→', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__
3.3.4 Formatage
Le système de formatage permet un contrôle précis de la conversion de variables en chaînes de caractères.
Après quelques tergiversations historiques 2 , le système de choix est dorénavant (Python 3.6+) celui de
la chaîne formatée (f-string), qui interprète directement les éléments du type "{var[:fmt]}" dans une
chaîne :
>>> nom, age = 'calvin', 6
>>> f"{ nom} a { age} ans." # Interpolation simple
'calvin a 6 ans.'
>>> f"L'année prochaine, { nom.capitalize()} aura { age+1} ans" # Interprétation
"L'année prochaine, Calvin aura 7 ans."
print() affiche à l’écran (plus spécifiquement la sortie standard) la conversion d’une variable en chaîne
de caractères :
>>> print("Calvin and Hobbes\nScientific progress goes 'boink'!")
Calvin and Hobbes
Scientific progress goes 'boink'!
>>> print(f"{ 3: 2d} fois { 4: 2d} font { 3*4: 2d} ") # Formatage et affichage
3 fois 4 font 12
2. Utilisation native du % avec la grammaire C printf, et plus récemment de la méthode de formatage des chaînes
str.format(); ces deux options sont encore valables et largement utilisées.
Exercice :
Tables de multiplication *
— Sous-listes (slices) :
>>> [ i**2 for i in range(5) ] # Carré de tous les éléments de [0, ..., 4]
[0, 1, 4, 9, 16]
>>> [ 2*i for i in range(10) if (i%3 != 0) ] # Compréhension conditionnelle
[2, 4, 8, 10, 14, 16]
>>> [ 10*i+j for i in range(3) for j in range(4) ] # Double compréhension
[0, 1, 2, 3, 10, 11, 12, 13, 20, 21, 22, 23]
>>> [ [ 10*i+j for i in range(3) ] for j in range(4) ] # Compréhensions imbriquées
[[0, 10, 20], [1, 11, 21], [2, 12, 22], [3, 13, 23]]
>>> { i: i**2 for i in range(1, 5) } # Dictionnaire en compréhension
{1: 1, 2: 4, 3: 9, 4: 16}
Exercices :
3.5 Fonctions
Une fonction est un regroupement d’instructions impératives – assignations, branchements, boucles, etc.
– s’appliquant sur des arguments d’entrée. C’est le concept central de la programmation impérative.
def permet de définir une fonction : def fonction (arg1 , arg2 , ..., option1 =valeur1 ,
option2 =valeur2 , ...):. Les « args » sont des arguments nécessaires (c.-à-d. obligatoires), tandis
que les « kwargs » – arguments de type option =valeur – sont optionnels, puisqu’ils possèdent une
valeur par défaut. Si la fonction doit retourner une valeur, celle-ci est spécifiée par le mot-clé return.
Exemples :
1 def temp_f2c(tf):
2 """
3 Convertit une température en d° Fahrenheit `tf` en d° Celsius.
4
5 Exemple:
6 >>> temp_f2c(104)
7 40.0
8 """
9
12 return tc
Dans la définition d’une fonction, la première chaîne de charactères (appelé docstring) servira de docu-
mentation pour la fonction, accessible de l’interpréteur via p.ex. help(temp_f2c), ou temp_f2c? sous
ipython. Elle se doit d’être tout à la fois pertinente, concise et complète. Elle peut également inclure
des exemples d’utilisation (doctests, voir Développement piloté par les tests).
11 Exemples:
12 >>> mean_power([1, 2, 3])
13 2.0
14 >>> mean_power([1, 2, 3], power=2)
15 2.160246899469287
16 """
17
21 return mean
contenant des éléments pour lesquels les opérations effectuées – somme, exponentiation, division
par un entier – ont été préalablement définies (p.ex. des entiers, des complexes, des matrices,
etc.) : c’est ce que l’on appelle le duck-typing 3 , favorisant le polymorphisme des fonctions ;
— le typage est fort, c.-à-d. que le type d’une variable ne peut pas changer à la volée. Ainsi, "abra"
+ "cadabra" a un sens (concaténation de chaînes), mais pas 1 + "2" ou 3 + "cochons" (entier
+ chaîne) ;
— la définition d’une fonction se fait dans un « espace parallèle » où les variables ont une portée
(scope) locale 4 . Ainsi, la variable s définie dans la fonction mean_power n’interfère pas avec le
« monde extérieur » ; inversement, la définition de mean_power ne connaît a priori rien d’autre
que les variables explicitement définies dans la liste des arguments ou localement.
Pour les noms de variables, fonctions, etc. utilisez de préférence des caractères purement ASCII 7
(a-zA-Z0-9_) ; de manière générale, favorisez plutôt la langue anglaise (variables, commentaires, affi-
chages).
Exercice :
Avertissement : Il est possible d’importer toutes les fonctionnalités d’une bibliothèque dans l’espace
de noms courant :
Cette pratique est cependant fortement déconseillée du fait des confusions dans les espaces de noms
qu’elle peut entraîner :
>>> from cmath import *
>>> sqrt(-1) # Quel sqrt: le réel ou le complexe?
Nous verrons par la suite quelques exemples de modules de la Bibliothèque standard, ainsi que des
Bibliothèques numériques de base orientées analyse numérique.
Exercice :
Attention : Toutes les instructions d’un module qui ne sont pas encapsulées dans le __main__ (voir
plus bas) sont interprétées et exécutées lors de l”import du module. Elles doivent donc en général se
limiter à la définition de variables, de fonctions et de classes (en particulier, éviter les affichages ou
les calculs longs).
Exemple :
Le code mean_power.py peut être importé comme une bibliothèque (p.ex. import mean_power) dans un
autre code Python, ou bien être exécuté depuis la ligne de commande (p.ex. python mean_power.py),
auquel cas la partie __main__ sera exécutée.
— #! (Hash-bang) : la première ligne d’un script défini l’interpréteur à utiliser 6 :
#!/usr/bin/env python3
— """doc""" : la chaîne de documentation de la bibliothèque (docstring, PEP 257), qui sera utilisée
comme aide en ligne du module (help(mean_power)), doit être la 1re instruction du script.
— if __name__ == '__main__': permet de séparer le __main__ (c.-à-d. le corps du programme, à
exécuter lors d’une utilisation en script) des définitions de fonctions et classes, permettant une
utilisation en module.
5. Parfois prononcé dunder main (dunder désigne le double _).
6. Il s’agit d’une fonctionnalité des shells d’Unix, pas spécifique à Python.
3.7 Exceptions
Lorsqu’il rencontre une erreur dans l’exécution d’une instruction, l’interpréteur Python génère (raise)
une erreur (Exception), de nature différente selon la nature de l’erreur : KeyError, ValueError,
AttributeError, NameError, TypeError, IOError, NotImplementedError, KeyboardInterrupt, etc.
La levée d’une erreur n’est cependant pas nécessairement fatale, puisque Python dispose d’un méca-
nisme de gestion des erreurs.
Il est d’usage en Python d’utiliser la philosophie EAFP (Easier to Ask for Forgiveness than Permission) 8 :
plutôt que de tester explicitement toutes les conditions de validité d’une instruction, on « tente sa chance »
d’abord, quitte à gérer les erreurs a posteriori. Cette gestion des exceptions se fait par la construction
try ... except.
1 def lireEntier():
2 while True:
3 chaine = input('Entrez un entier: ') # Lecture du clavier → str
4 try:
5 # La conversion en type entier génère `ValueError` si nécessaire
6 return int(chaine)
7 except ValueError: # Gestion de l'exception ValueError
8 print(f"{ chaine!r} n'est pas un entier")
>>> lireEntier()
Entrez un entier: toto
'toto' n'est pas un entier
Entrez un entier: 3,4
'3,4' n'est pas un entier
Entrez un entier: 4
4
Dans l’élaboration d’un programme, gérez explicitement les erreurs que vous auriez pu tester a priori et
pour lesquels il existe une solution de repli, et laissez passer les autres (ce qui provoquera éventuellement
l’interruption du programme).
Danger : Évitez à tout prix les except nus, c.-à-d. ne spécifiant pas la ou les exceptions à gérer, car
ils intercepteraient alors toutes les exceptions, y compris celles que vous n’aviez pas prévues ! Trouvez
l’erreur dans le code suivant :
y = 2
try:
x = z # Copie y dans x
print("Tout va bien")
except:
print("Rien ne va plus")
Vos procédures doivent également générer des exceptions (documentées) – avec l’instruction raise
Exception() – si elles ne peuvent conclure leur action, à charge pour la procédure appelante de les
gérer si besoin :
5 Exemples:
6 >>> diff_sqr(5, 3)
7 16
8 >>> diff_sqr(3, 5)
(suite sur la page suivante)
8. Par opposition au LBYL (Look Before You Leap) du C/C++, basé sur une série exhaustive de tests a priori.
3.7. Exceptions 21
Analyse scientifique avec Python, Version Janvier 2021
14 if x < y:
15 raise ValueError(f"x={ x} < y={ y} ")
16
Avant de se lancer dans un calcul long et complexe, on peut vouloir tester la validité de certaines
hypothèses fondamentales, soit par une structure if ... raise, ou plus facilement à l’aide d”assert
(qui, si l’hypothèse n’est pas vérifiée, génère une AssertionError) :
Exercice :
3.8 Classes
Un objet est une entité de programmation, disposant de son propre état interne et de fonctionnalités
associées : c’est le concept central de la Programmation Orientée Objet (POO).
Au concept d’objet sont liées les notions de :
— Classe : il s’agit d’un modèle d’objet, dans lequel sont définis ses propriétés usuelles. P.ex. la
classe Animal peut représenter un animal caractérisé par sa masse, et disposant de fonctionnalités
propres, p.ex. grossit() ;
— Instanciation : c’est le fait générer un objet concret (une instance) à partir d’un modèle (une
classe). P.ex. vache = Animal(500.) crée une instance vache à partir de la classe Animal et d’une
masse (float) :
— Attributs : variables internes décrivant l’état de l’objet. P.ex., vache.masse donne la masse de
l”Animal vache ;
— Méthodes : fonctions internes, s’appliquant en premier lieu sur l’objet lui-même (self), décrivant
les capacités de l’objet. P.ex. vache.grossit(10) modifie la masse de l”Animal vache ;
Attention : Toutes les méthodes d’une classe doivent au moins prendre self – représentant
l’instance même de l’objet – comme premier argument.
— Surcharge d’opérateurs : cela permet de redéfinir les opérateurs et fonctions usuels (+, abs(),
str(), etc.), pour simplifier l’écriture d’opérations sur les objets. Ainsi, on peut redéfinir les
opérateurs de comparaison (<, >=, etc.) dans la classe Animal pour que les opérations du genre
animal1 < animal2 aient un sens (p.ex. en comparant les masses).
— Héritage de classe : il s’agit de définir une classe à partir d’une (ou plusieurs) classe(s) pa-
rente(s). La nouvelle classe hérite des attributs et méthodes de sa (ses) parente(s), que l’on peut
alors modifier ou compléter. P.ex. la classe AnimalFeroce hérite de la classe Animal (elle partage
la notion de masse), et lui ajoute des méthodes propres à la notion d’animal féroce (p.ex. dévorer
un autre animal).
1 class Animal:
2 """
3 Un animal, défini par sa masse.
4 """
5
14 self.estVivant = True
15
16 self.masse = float(masse)
17 if self.masse < 0:
18 raise ValueError("La masse ne peut pas ^etre négative.")
19
20
21 def __str__(self):
22 """
23 Surcharge de la fonction `str()`.
24
34
35 def meurt(self):
36 """
37 L'animal meurt.
38 """
39
40 self.estVivant = False
41
42
51 self.masse += float(masse)
3.8. Classes 23
Analyse scientifique avec Python, Version Janvier 2021
1 class AnimalFeroce(Animal):
2 """
3 Un animal féroce est un animal qui peut dévorer d'autres animaux.
4
16 def __str__(self):
17 """
18 Surcharge de la fonction `str()`.
19 """
20
29 * Si other est également un animal féroce, il faut que self soit plus
30 gros que other pour le dévorer. Sinon, other se défend et self meurt.
31 * Si self dévore other, other meurt, self grossit de la masse de other
32 (jusqu'à 10% de sa propre masse) et other maigrit d'autant.
33
48 return prise
1 class AnimalGentil(Animal):
2 """
3 Un animal gentil est un animal avec un petit nom.
4
26 def __str__(self):
27 """
28 Surcharge de la fonction `str()`.
29 """
30
35 def meurt(self):
36 """
37 L'animal gentil meurt, avec un éloge funéraire.
38 """
39
40 Animal.meurt(self)
41 print(f"Pauvre { self.nom} meurt, paix à son ^ame...")
Note : Il est traditionnel d’écrire les noms de classes en CamelCase (AnimalGentil), et les noms
d’instances de classe (les variables) en minuscules (vache).
Exemples
Études de cas
— turtle.Vec2D
— fractions.Fraction
Exercices :
3.8. Classes 25
Analyse scientifique avec Python, Version Janvier 2021
3.9 Entrées-sorties
3.9.1 Intéractif
Comme nous avons pu le voir précédemment, l’affichage à l’écran se fait par print, la lecture du clavier
par input.
Python avancé
— Fonctionnalités avancées
— Arguments anonymes
— Dépaquetage des arguments
— Dépaquetage des itérables
— Décorateurs
— Fonction anonyme
— Programmation Orientée Objet avancée
— Variables de classe
— Méthodes statiques
— Méthodes de classe
— Attributs et méthodes privées
— Propriétés
— Éléments passés sous silence
— Python 3.x
— Transition Python 2 à Python 3
27
Analyse scientifique avec Python, Version Janvier 2021
Attention : Cela laisse une grande flexibilité dans l’appel de la fonction, mais au prix d’une très
mauvaise lisibilité de sa signature (interface de programmation). À utiliser avec parcimonie. . .
À partir de Python 3.5, il est encore plus facile d’utiliser un ou plusieurs de ces opérateurs conjointement
aux [kw]args traditionnels (PEP 448), dans la limite où les args sont toujours situés avant les kwargs :
>>> a, b, c = 1, 2, 3, 4
ValueError: too many values to unpack (expected 3)
>>> a, *b, c = 1, 2, 3, 4
>>> a, b, c
(1, [2, 3], 4)
4.1.4 Décorateurs
Les fonctions (et méthodes) sont en Python des objets comme les autres, et peuvent donc être utilisées
comme arguments d’une fonction, ou retournées comme résultat d’une fonction.
8 return result
Les décorateurs sont des fonctions s’appliquant sur une fonction ou une méthode pour en modifier le
comportement : elles retournent de façon transparente une version « décorée » (augmentée) de la fonction
initiale.
9 return result
10
Il est possible de décorer une fonction à la volée lors de sa définition avec la notation @ :
@verbose
def null(*args, **kwargs):
pass
Noter qu’il est possible d’ajouter plusieurs décorateurs, et de passer des arguments supplémentaires aux
décorateurs.
1 def add_attrs(**kwargs):
2 """
3 Decorator adding attributes to a function, e.g.
4 ::
5
6 @attrs(source='NIST/IAPWS')
7 def func(...):
8 ...
9 """
10
11 def decorate(f):
12 for key, val in kwargs.iteritems():
13 setattr(f, key, val)
14 return f
15
16 return decorate
1 def make_method(obj):
2 """
3 Decorator to make the function a method of `obj` (*monkey patching*), e.g.
4 ::
5
6 @make_method(MyClass)
7 def func(myClassInstance, ...):
8 ...
9
12 myClassInstance.func()
13 """
14
15 def decorate(f):
16 setattr(obj, f.__name__, f)
17 return f
18
19 return decorate
Liens :
La définition d’une fonction lambda ne peut inclure qu”une seule expression, et est donc contrainte de
facto à être très simple, généralement pour être utilisée comme argument d’une autre fonction :
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1]) # tri sur le 2e élément de la paire
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
Cependant, cela est considéré comme une faute de style, puisque ce n’est justement pas l’objectif d’une
fonction anonyme ! Il n’y a p.ex. pas de docstring associée.
class MyClass:
class MyClass:
@staticmethod
def ms_to_kmh(speed):
"Conversion m/s → km/h."
Une méthode statique peut être invoquée directement via la classe en dehors de toute instanciation (p.ex.
MyClass.ms_to_kmh()), ou via une instance (p.ex. self.ms_to_kmh()).
class MyClass:
self.x, self.y = x, y
@classmethod
def init_from_file(cls, filename):
"Initialisation à partir d'un fichier."
@classmethod
def init_from_web(cls, url):
"Initialisation à partir d'une URL."
Exemple
1 class Date:
2 "Source: https://stackoverflow.com/questions/12179271"
3
7 self.day = day
8 self.month = month
9 self.year = year
10
11 @classmethod
12 def from_string(cls, astring):
13 """Initialize from (verified) 'day-month-year' string."""
14
15 if cls.is_valid_date(astring):
16 day, month, year = map(int, astring.split('-'))
17
22 @staticmethod
23 def is_valid_date(astring):
24 """Check validity of 'day-month-year' string."""
25
26 try:
27 day, month, year = map(int, astring.split('-'))
28 except ValueError:
29 return False
30 else:
31 return (0 < day <= 31) and (0 < month <= 12) and (0 < year <= 2999)
— Les attributs/méthodes qui commencent par un simple _ sont réputées privées (mais sont en fait
parfaitement publiques) : une interface est généralement prévue (setter et getter), même si vous
pouvez y accéder directement à vos risques et périls.
1 class AnimalPrive:
2
5 self.set_mass(mass)
6
13 self._mass = float(mass)
14
15 def get_mass(self):
16 """Getter de l'attribut privé `mass`."""
17
18 return self._mass
— Les attributs/méthodes qui commencent par un double __ (dunder) sont « cachées » sous un nom
complexe mais prévisible (cf. PEP 8).
1 class AnimalTresPrive:
2
5 self.set_mass(mass)
6
10 if float(mass) < 0:
11 raise ValueError("Mass should be a positive float.")
12
13 self.__mass = float(mass)
14
15 def get_mass(self):
16 """Getter de l'attribut privé `mass`."""
17
18 return self.__mass
4.2.5 Propriétés
Compte tenu de la nature foncièrement publique des attributs, le mécanisme des getters et setters n’est
pas considéré comme très pythonique. Il est préférable d’utiliser la notion de property (utilisée en
décorateur).
1 class AnimalProperty:
2
7 @property
(suite sur la page suivante)
10 return self._mass
11
12 @mass.setter
13 def mass(self, mass): # Setter de la propriété mass
14
15 if float(mass) < 0:
16 raise ValueError("Mass should be a positive float.")
17
18 self._mass = float(mass)
Les propriétés sont également utilisées pour accéder à des quantités calculées à la volée à partir d’attributs
intrinsèques.
1 class Interval:
2
8 @property
9 def min(self):
10 """La propriété min est simplement _range[0]. Elle n'a pas de setter."""
11
12 return self._range[0]
13
14 @property
15 def max(self):
16 """La propriété max est simplement _range[1]. Elle n'a pas de setter."""
17
18 return self._range[1]
19
20 @property
21 def middle(self):
22 """La propriété middle est calculée à la volée. Elle n'a pas de setter."""
23
Pour des raisons historiques autant que pratiques 3 , ce cours présentait initialement le langage Python
dans sa version 2. Cependant, puisque le développement actuel de Python (et de certaines de ses bi-
bliothèques clés) se fait maintenant uniquement sur la branche 3.x, qui constitue une remise à plat non
rétrocompatible du langage, et que la branche 2.x n’est plus supporté depuis janvier 2020 (PEP 466), le
cours a été porté sur Python 3.
Python 3 apporte quelques changements fondamentaux, notamment :
— print() n’est plus un mot-clé mais une fonction : print(...) ;
— l’opérateur / ne réalise plus la division euclidienne entre les entiers, mais toujours la division
réelle ;
— la plupart des fonctions qui retournaient des itérables en Python 2 (p.ex. range()) retournent
maintenant des itérateurs, plus légers en mémoire ;
— un support complet (mais encore complexe) des chaînes Unicode ;
— un nouveau système de formatage des chaînes de caractères (f-string du PEP 498 à partir de
Python 3.6) ;
— la fonction de comparaison cmp (et la méthode spéciale associée __cmp__) n’existe plus 4 .
Note : La branche 3.x a pris un certain temps pour mûrir, et Python 3 n’est vraiment considéré
fonctionnel (et maintenu) qu’à partir de la version 3.5. Inversement, la dernière version supportée de
Python 2 a été 2.7.
Avertissement : Python 2 n’étant plus supporté, il est dorénavant indispensable d’utiliser exclusi-
vement Python 3.
Si votre code est encore sous Python 2.x, il existe de nombreux outils permettant de faciliter la transition
vers 3.x (mais pas de la repousser ad eternam) :
— L’interpréteur Python 2.7 dispose d’une option -3 mettant en évidence dans un code les parties
qui devront être modifiées pour un passage à Python 3.
— Le script 2to3 permet d’automatiser la conversion du code 2.x en 3.x.
— La bibliothèque standard __future__ permet d’introduire des constructions 3.x dans un code 2.x,
p.ex. :
— La bibliothèque non standard six fournit une couche de compatibilité 2.x-3.x, permettant de
produire de façon transparente un code compatible simultanément avec les deux versions.
Liens
— Py3 Readiness : liste (réduite) des bibliothèques encore non-compatibles avec Python 3
— Porting Python 2 Code to Python 3
— The Conservative Python 3 Porting Guide
— Python 2/3 compatibility
Bibliothèque standard
Python dispose d’une très riche bibliothèque de modules étendant les capacités du langage dans de
nombreux domaines : nouveaux types de données, interactions avec le système, gestion des fichiers et des
processus, protocoles de communication (internet, mail, FTP, etc.), multimédia, etc.
— The Python Standard Library (v3.x)
— Python Module of the Week (v3.x)
Utilisation de sys.argv
Le module sys permet un accès direct aux arguments de la ligne de commande, via la liste sys.argv :
sys.argv[0] contient le nom du script executé, sys.argv[1] le nom du 1er argument (s’il existe), etc.
P.ex. :
39
Analyse scientifique avec Python, Version Janvier 2021
Module argparse
Pour une gestion avancée des arguments et/ou options de la ligne de commande, il est préférable d’utiliser
le module argparse. P.ex. :
1 import argparse
2
3 parser = argparse.ArgumentParser(
4 usage="%(prog)s [-p/--plot] [-i/--input coordfile | x1,y1 x2,y2 x3,y3]",
5 description="Compute the circumscribed circle to 3 points in the plan.")
6 parser.add_argument('coords', nargs='*', type=str, metavar='x,y',
7 help="Coordinates of point")
8 parser.add_argument('-i', '--input', nargs='?', type=argparse.FileType('r'),
9 help="Coordinate file (one 'x,y' per line)")
10 parser.add_argument('-P', '--plot', action="store_true", default=False,
11 help="Draw the circumscribed circle")
12 parser.add_argument('-T', '--tests', action="store_true", default=False,
13 help="Run doc tests")
14 parser.add_argument('--version', action='version', version=__version__)
15
16 args = parser.parse_args()
$ python3 circonscrit.py -h
usage: circonscrit.py [-p/--plot] [-i/--input coordfile | x1,y1 x2,y2 x3,y3]
positional arguments:
x,y Coordinates of point
optional arguments:
-h, --help show this help message and exit
-i [INPUT], --input [INPUT]
Coordinate file (one 'x,y' per line)
-p, --plot Draw the circumscribed circle
-T, --tests Run doc tests
--version show program's version number and exit
Attention : les pickles ne sont pas un format d’échange de données. Ils sont spécifiques à python,
et peuvent dépendre de la machine utilisée. Ils peuvent en outre constituer une faille de sécurité.
— functools est une collection d’outils s’appliquant sur des fonctions (mémorisation, fonction par-
tielle, fonction générique, wrapping, etc.)
— itertools fournit des générateurs de boucle (itérateurs) et de combinatoire :
Bibliothèques externes :
— Numpy
— Tableaux
— Création de tableaux
— Manipulations sur les tableaux
— Opérations de base
— Tableaux évolués
— Entrées/sorties
— Sous-modules
— Performances
— Scipy
— Tour d’horizon
— Quelques exemples complets
— Matplotlib
— pylab vs. pyplot
— Figure et axes
— Sauvegarde et affichage interactif
— Anatomie d’une figure
— Visualisation 3D
6.1 Numpy
numpy est une bibliothèque numérique apportant le support efficace de larges tableaux multidimension-
nels, et de routines mathématiques de haut niveau (fonctions spéciales, algèbre linéaire, statistiques,
etc.).
Note :
— La convention d’import utilisé dans les exemples est « import numpy as N ».
— N’oubliez pas de citer numpy dans vos publications et présentations utilisant ces outils.
43
Analyse scientifique avec Python, Version Janvier 2021
Liens :
6.1.1 Tableaux
Un numpy.ndarray (généralement appelé array) est un tableau multidimensionnel homogène : tous les
éléments doivent avoir le même type, en général numérique. Les différentes dimensions sont appelées des
axes, tandis que le nombre de dimensions – 0 pour un scalaire, 1 pour un vecteur, 2 pour une matrice,
etc. – est appelé le rang.
Création de tableaux
>>> N.zeros((2, 1)) # Shape (2, 1): 2 lignes, 1 colonne, float par défaut
array([[ 0.],
[ 0.]])
>>> N.ones((1, 2), dtype=bool) # Shape (1, 2): 1 ligne, 2 colonnes, type booléen
array([[True, True]], dtype=bool)
>>> N.full((2, 2), N.NaN)
array([[ nan, nan],
[ nan, nan]])
>>> N.arange(10, 30, 5) # De 10 à 30 (exclu) par pas de 5, type entier par défaut
array([10, 15, 20, 25])
>>> N.arange(0.5, 2.1, 0.3) # Accepte des réels en argument, DANGER!
array([ 0.5, 0.8, 1.1, 1.4, 1.7, 2. ])
Index tricks
— numpy.r_ (resp. numpy.c_) est un opérateur puissant avec une notation évoluée (index tricks),
permettant à la fois la génération (équivalent à numpy.arange() et numpy.linspace()) et la
concaténation (numpy.concatenate()) le long du 1e axe (resp. du 2e axe) :
>>> N.concatenate([[0], N.arange(1, 6, 2), N.zeros(2), N.linspace(1, 2, 3)])
array([0. , 1. , 3. , 5. , 0. , 0. , 1. , 1.5, 2. ])
>>> N.r_[0, 1:6:2, [0]*2, 1:2:3j] # Notez les crochets et les slices
array([0. , 1. , 3. , 5. , 0. , 0. , 1. , 1.5, 2. ])
>>> N.c_[1:6:2, 1:2:3j]
array([[1. , 1. ],
[3. , 1.5],
[5. , 2. ]])
— Plus généralement, numpy.mgrid permet de générer des rampes d’indices (entiers) ou de coor-
données (réels) de rang arbitraire avec les index tricks. Équivalent à numpy.linspace() en 1D et
similaire (mais différent) à numpy.meshgrid() en 2D.
>>> N.mgrid[0:4, 1:6:2] # Grille 2D d'indices (entiers)
array([[[0, 0, 0], # 0:4 = [0, 1, 2, 3] selon l'axe 0
[1, 1, 1],
[2, 2, 2],
[3, 3, 3]],
[[1, 3, 5], # 1:6:2 = [1, 3, 5] selon l'axe 1
[1, 3, 5],
[1, 3, 5],
[1, 3, 5]]])
>>> N.mgrid[0:2*N.pi:5j] # Rampe de coordonnées (réels): 5 nb de 0 à 2π (inclus)
array([ 0., 1.57079633, 3.14159265, 4.71238898, 6.28318531])
>>> # 3 points entre 0 et 1 selon l'axe 0, et 5 entre 0 et 2 selon l'axe 1
>>> z = N.mgrid[0:1:3j, 0:2:5j]; z
array([[[ 0. , 0. , 0. , 0. , 0. ], # Axe 0 variable, axe 1 constant
[ 0.5, 0.5, 0.5, 0.5, 0.5],
[ 1. , 1. , 1. , 1. , 1. ]],
[[ 0. , 0.5, 1. , 1.5, 2. ], # Axe 0 constant, axe 1 variable
(suite sur la page suivante)
6.1. Numpy 45
Analyse scientifique avec Python, Version Janvier 2021
— numpy.ogrid est similaire à numpy.mgrid mais permet de générer des rampes d’indices ou de
coordonnées compactes (sparse) :
Attention : à l’ordre de variation des indices dans les tableaux multidimensionnels, et aux
différences entre numpy.meshgrid() et numpy.mgrid/numpy.ogrid.
Tableaux aléatoires
— numpy.random.rand() crée un tableau d’un format donné de réels aléatoires dans [0, 1[ ; numpy.
random.randn() génère un tableau d’un format donné de réels tirés aléatoirement d’une distri-
bution gaussienne (normale) standard 𝒩 (𝜇 = 0, 𝜎 2 = 1).
Les array 1D sont indexables comme les listes standard. En dimension supérieure, chaque axe est in-
déxable indépendamment.
Slicing
Les sous-tableaux de rang < N d’un tableau de rang N sont appelées slices : le (ou les) axe(s) selon
le(s)quel(s) la slice a été découpée, devenu(s) de longueur 1, est (sont) éliminé(s).
Modification de format
numpy.ndarray.reshape() modifie le format d’un tableau sans modifier le nombre total d’éléments :
>>> y = N.arange(6).reshape(2, 3); y # Shape (6,) → (2, 3) (*size* inchangé)
array([[0, 1, 2],
[3, 4, 5]])
>>> y.reshape(2, 4) # Format incompatible (*size* serait modifié)
ValueError: total size of new array must be unchanged
numpy.ndarray.transpose() transpose deux axes, par défaut les derniers (raccourci : numpy.ndarray.
T) :
>>> y.T # Transposition = y.transpose() (voir aussi *rollaxis*)
array([[0, 3],
[1, 4],
[2, 5]])
6.1. Numpy 47
Analyse scientifique avec Python, Version Janvier 2021
Attention : N.resize(arr, shape) (complétion avec des copies de arr) est différent de arr.
resize(shape) (complétion avec des 0).
Exercice :
Inversion de matrice *
Stacking
>>> a = N.arange(5); a
array([0, 1, 2, 3, 4])
>>> N.hstack((a, a)) # Stack horizontal (le long des colonnes) = N.r_[a, a]
array([0, 1, 2, 3, 4, 0, 1, 2, 3, 4])
>>> N.vstack((a, a)) # Stack vertical (le long des lignes) = N.r_['0,2', a, a]
array([[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4]])
>>> N.dstack((a, a)) # Stack en profondeur (le long des plans)
array([[[0, 0],
[1, 1],
[2, 2],
[3, 3],
[4, 4]]])
Broadcasting
L”array broadcasting définit les régles selon lesquelles deux tableaux de formats différents peuvent éven-
tuellement s’apparier.
1. Deux tableaux de même rang sont compatibles (broadcastable) si, pour chaque axe, soit les tailles
sont égales, soit l’une d’elles est exactement égale à 1. P.ex. (5, 3) et (1, 3) sont des formats
broadcastable, (5, 3) et (5, 1) également, mais (5, 3) et (3, 1) ne le sont pas.
2. Si un tableau a un axe de taille 1, le tableau sera dupliqué à la volée autant de fois que nécessaire
selon cet axe pour attendre la taille de l’autre tableau le long de cet axe. P.ex. un tableau (2, 1,
3) pourra être transformé en tableau (2, 5, 3) en le dupliquant 5 fois le long du 2e axe (axis=1).
3. La taille selon chaque axe après broadcast est égale au maximum de toutes les tailles d’entrée le
long de cet axe. P.ex. (5, 3, 1) × (1, 3, 4) → (5, 3, 4).
4. Si un des tableaux a un rang (ndim) inférieur à l’autre, alors son format (shape) est précédé
d’autant de 1 que nécessaire pour atteindre le même rang. P.ex. (5, 3, 1) × (4,) = (5, 3, 1) × (1,
1, 4) → (5, 3, 4).
Indexation évoluée
Opérations de base
6.1. Numpy 49
Analyse scientifique avec Python, Version Janvier 2021
Opérations matricielles
Les opérations de base s’appliquent sur les éléments des tableaux, et n’ont pas une signification matricielle
par défaut :
Il est possible d’utiliser systématiquement les opérations matricielles en manipulant des numpy.matrix
plutôt que de numpy.ndarray :
Le sous-module numpy.linalg fournit des outils spécifiques au calcul matriciel (inverse, déterminant,
valeurs propres, etc.).
Ufuncs
>>> N.all(N.sin(x) == [0, 1, 0, -1, 0]) # Test d'égalité stricte de tous les éléments
False
>>> N.allclose(y, [0, 1, 0, -1, 0]) # Test d'égalité numérique de tous les éléments
True
Exercices :
Types composés
Par définition, tous les éléments d’un tableau homogène doivent être du même type. Cependant, outre les
types scalaires élémentaires – bool, int, float, complex, str, etc. – numpy supporte les types composés,
c.-à-d. incluant plusieurs sous-éléments de types différents :
Les tableaux structurés sont très puissants pour manipuler des données (semi-)hétérogènes, p.ex. les
entrées du catalogue CSV des objets de Messier Messier.csv :
1 # Messier, NGC, Magnitude, Size [arcmin], Distance [pc], RA [h], Dec [deg], Constellation,␣
˓→Season, Name
>>> N.nanmean(messier['Mag'])
7.4927273
6.1. Numpy 51
Analyse scientifique avec Python, Version Janvier 2021
Tableaux masqués
Le sous-module numpy.ma ajoute le support des tableaux masqués (Masked Arrays). Imaginons un tableau
(4, 5) de réels (positifs ou négatifs), sur lequel nous voulons calculer pour chaque colonne la moyenne
des éléments positifs uniquement :
Note : Les tableaux évolués de numpy sont parfois suffisants, mais pour une utilisation avancée, il peut
être plus pertinent d’invoquer les bibliothèques dédiées Pandas et xarray.
6.1.3 Entrées/sorties
numpy peut lire – numpy.loadtxt() – ou sauvegarder – numpy.savetxt() – des tableaux (uni- ou bi-
dimensionnels) dans un simple fichier ASCII :
Attention : numpy.loadtxt() supporte les types composés, mais ne supporte pas les données
manquantes ; utiliser alors la fonction numpy.genfromtxt(), plus robuste mais plus lente.
Le format texte n’est pas optimal pour de gros tableaux (ou de rang > 2) : il peut alors être avantageux
d’utiliser le format binaire .npy, beaucoup plus compact (mais non human readable) :
Il est enfin possible de sérialiser les tableaux à l’aide de la bibliothèque standard pickle.
6.1.4 Sous-modules
numpy fournit en outre quelques fonctionnalités supplémentaires, parmis lesquelles les sous-modules sui-
vants :
— numpy.fft : Discrete Fourier Transform ;
— numpy.random : valeurs aléatoires ;
— numpy.polynomial : manipulation des polynômes (racines, polynômes orthogonaux, etc.).
6.1.5 Performances
Même si numpy apporte un gain significatif en performance par rapport à du Python standard, il peut
être possible d’améliorer la vitesse d’exécution par l’utilisation de bibliothèques externes dédiées, p.ex. :
— numexpr est un évaluateur optimisé d’expressions numériques :
>>> a = N.arange(1e6)
>>> b = N.arange(1e6)
>>> %timeit a*b - 4.1*a > 2.5*b
100 loops, best of 3: 11.4 ms per loop
>>> %timeit numexpr.evaluate("a*b - 4.1*a > 2.5*b")
100 loops, best of 3: 1.97 ms per loop
>>> %timeit N.exp(-a)
10 loops, best of 3: 60.1 ms per loop
>>> timeit numexpr.evaluate("exp(-a)") # Multi-threaded
10 loops, best of 3: 19.3 ms per loop
— bottleneck est une collection de fonctions accélérées, notamment pour des tableaux contenant des
NaN ou pour des statistiques glissantes ;
— theano, pour optimiser l’évaluation des expressions mathématiques sur les tableaux numpy, no-
tamment par l’utilisation des GPU (Graphics Processing Unit) et de code C généré à la volée.
Voir également Profilage et optimisation.
6.2 Scipy
scipy est une bibliothèque numérique 1 d’algorithmes et de fonctions mathématiques, basée sur les
tableaux numpy.ndarray, complétant ou améliorant (en termes de performances) les fonctionnalités de
numpy.
Note : N’oubliez pas de citer scipy & Co. dans vos publications et présentations utilisant ces outils.
1. Python dispose également d’une bibliothèque de calcul formel, sympy, et d’un environnement de calcul mathéma-
tique, sage.
6.2. Scipy 53
Analyse scientifique avec Python, Version Janvier 2021
Liens :
— Scipy Reference
— Scipy Cookbook
Voir également :
Exercices :
6.3 Matplotlib
Matplotlib est une bibliothèque graphique de visualisation 2D (avec un support pour la 3D, l’animation
et l’interactivité), permettant des sorties de haute qualité « prêtes à publier ». C’est à la fois une biblio-
thèque de haut niveau, fournissant des fonctions de visualisation « clés en main » (échelle logarithmique,
histogramme, courbes de niveau, etc., voir la galerie), et de bas niveau, permettant de modifier tous
les éléments graphiques de la figure (titre, axes, couleurs et styles des lignes, etc., voir Anatomie d’une
figure).
import numpy as N
import matplotlib.pyplot as P
Par la suite, nous nous concentrerons sur l’interface OO (Orientée Objet) matplotlib.pyplot, plus
puissante et flexible.
6.3. Matplotlib 55
Analyse scientifique avec Python, Version Janvier 2021
Ainsi, pour générer une figure contenant 2 (vertical) × 3 (horizontal) = 6 systèmes d’axes (numérotés
de 1 à 6) :
Pour un contrôle plus fin de la disposition des systèmes d’axes dans une figure, il est possible de générer
les axes un à un via la méthode matplotlib.figure.Figure.add_subplot() :
fig = P.figure()
for i in range(1, 4):
ax = fig.add_subplot(2, 3, i, xticks=[], yticks=[])
ax.text(0.5, 0.5, f"subplot(2, 3, { i} )",
ha='center', va='center', size='large')
for i in range(3, 5):
ax = fig.add_subplot(2, 2, i, xticks=[], yticks=[])
ax.text(0.5, 0.5, f"subplot(2, 2, { i} )",
ha='center', va='center', size='large')
Pour des mises en page plus complexes, il est possible d’utiliser le kit gridspec, p.ex. :
fig = P.figure()
fig.suptitle("grid = GridSpec(2, 3)", fontsize='x-large')
grid = GridSpec(2, 3)
ax1 = fig.add_subplot(grid[0, :-1], xticks=[], yticks=[])
ax1.text(0.5, 0.5, "grid[0, :-1]", ha='center', va='center', size='large')
ax2 = fig.add_subplot(grid[:, -1], xticks=[], yticks=[])
ax3.text(0.5, 0.5, "grid[:, -1]", ha='center', va='center', size='large')
ax3 = fig.add_subplot(grid[1, 0], xticks=[], yticks=[])
ax3.text(0.5, 0.5, "grid[1, 0]", ha='center', va='center', size='large')
ax4 = fig.add_subplot(grid[1, 1], xticks=[], yticks=[])
ax4.text(0.5, 0.5, "grid[1, 1]", ha='center', va='center', size='large')
Enfin, il est toujours possible de créer soi-même le système d’axes dans les coordonnées relatives à la
figure :
fig = P.figure()
ax0 = fig.add_axes([0, 0, 1, 1], frameon=False,
xticks=N.linspace(0, 1, 11), yticks=N.linspace(0, 1, 11))
ax0.grid(True, ls='--')
(suite sur la page suivante)
6.3. Matplotlib 57
Analyse scientifique avec Python, Version Janvier 2021
Note : Utiliser ipython --pylab pour l’utilisation intéractive des figures dans une session ipython.
Note : N’oubliez pas de citer matplotlib dans vos publications et présentations utilisant cet outil.
Liens :
— User’s Guide
— Gallery
— Tutorial matplotlib
— Tutoriel matplotlib
Voir également :
— Third party packages, une liste de bibliothèques complétant matplotlib : cartographie, visualisa-
tion interactive, implémentation de la Grammar of Graphics, graphiques spécialisés, animations,
interactivité, etc.
— mpld3, un backend matplotlib interactif basé sur la bibliothèque web 3D.js ;
— Seaborn, une surcouche de visualisation statistique à matplotlib et Pandas et xarray ;
— Bokeh, une bibliothèque graphique alternative à matplotlib plus orientée web/temps réel ;
— plotext, termplotlib, pour réaliser des figures directement dans le terminal (voir également le
backend drawilleplot).
6.3. Matplotlib 59
Analyse scientifique avec Python, Version Janvier 2021
Exemples :
figure.py, filtres2ndOrdre.py
Exercices :
6.3.5 Visualisation 3D
Matplotlib fournit d’emblée une interface mplot3d pour des figures 3D assez simples :
Pour des visualisations plus complexes, mayavi.mlab est une bibliothèque graphique de visualisation 3D
s’appuyant sur Mayavi.
Note : N’oubliez pas de citer mayavi dans vos publications et présentations utilisant cet outil.
Voir également :
6.3. Matplotlib 61
Analyse scientifique avec Python, Version Janvier 2021
— Pandas et xarray
— Structures
— Accès aux données
— Manipulation des données
— Regroupement et agrégation de données
— Visualisations
— Xarray
— Autres types de tableaux
— Astropy
— Tour d’horizon (v4.0)
— Démonstration
— Autres bibliothèques scientifiques
pandas est une bibliothèque pour la structuration et l’analyse avancée de données hétérogènes (PANel
DAta). Elle fournit notamment :
— des structures de données relationelles (« labellisées »), avec une indexation simple ou hiérarchique
(c.-à-d. à plusieurs niveaux) ;
— des méthodes d’alignement et d’agrégation des données, avec gestion des données manquantes ;
— un support performant des labels temporels (p.ex. dates, de par son origine dans le domaine de
l’économétrie), et des statistiques « glissantes » ;
— de nombreuses fonctions d’entrée/sortie, d’analyse statistiques et de visualisation.
Les fonctionnalités de pandas sont très riches et couvrent de nombreux aspects (données manquantes,
dates, analyse statistiques, etc.) : il n’est pas question de toutes les aborder ici. Avant de vous lancer dans
une manipulation qui vous semble complexe, bien inspecter la documentation, très complète (p.ex. les
recettes du Cookbook), pour vérifier qu’elle n’est pas déjà implémentée ou documentée, ou pour identifier
l’approche la plus efficace.
63
Analyse scientifique avec Python, Version Janvier 2021
Attention : La bibliothèque pandas est maintenant considérée comme stable (v1.x) mais xarray est
encore en phase de développement. Nous travaillons ici sur les versions :
— pandas 1.1
— xarray 0.16
7.1.1 Structures
Références : Intro to data structures
Pandas dispose de deux grandes structures de données 1 :
Pour mettre en évidence la puissance de Pandas, nous utiliserons le catalogue des objets Messier vu
précédemment. Le fichier peut être importé à l’aide de la function pandas.read_csv(), et le dataframe
résultant est labellisé à la volée par la colonne M (pandas.DataFrame.set_index()) :
1. Les structures pandas.Panel (de rang 3), et pandas.Panel4D (de rang 4) et pandas.PanelND (de rang arbitraire) ont
été respectivement dépréciées aux versions 0.20 et 0.19. Utiliser une indexation hiérarchique ou xarray.
Un dataframe est caractérisé par son indexation pandas.DataFrame.index et ses colonnes pandas.
DataFrame.columns (de type pandas.Index ou pandas.MultiIndex), et les valeurs des données pandas.
DataFrame.values :
Une description statistique sommaire des colonnes numériques est obtenue par pandas.DataFrame.
describe() :
>>> messier.loc['M31'] # Retourne une Series indexée par les noms de colonne
NGC 224
Type Sp
...
Season autumn
Name Andromeda Galaxy
Name: M31, Length: 10, dtype: object
>>> messier.loc['M31', ['Type', 'Name']] # Retourne une Series
Type Sp
Name Andromeda Galaxy
Name: M31, dtype: object
>>> messier.loc[['M31', 'M51'], ['Type', 'Name']] # Retourne un DataFrame
Type Name
M
M31 Sp Andromeda Galaxy
M51 Sp Whirlpool Galaxy
>>> messier.loc['M31':'M33', ['Type', 'Name']] # De M31 à M33 *inclu*
Type Name
M
M31 Sp Andromeda Galaxy
M32 El NaN
M33 Sp Triangulum Galaxy
De façon symétrique, l’accès peut se faire par position (n° de ligne/colonne) via pandas.DataFrame.iloc,
p.ex. :
Noter qu’il existe une façon de filtrer les données sur les colonnes ou les labels :
>>> messier.filter(regex='M.7', axis='index').filter('RA Dec'.split())
RA Dec
M
M17 18.347 -16.183
M27 19.993 22.717
M37 5.873 32.550
M47 7.610 -14.500
M57 18.893 33.033
M67 8.840 11.817
M77 2.712 0.033
M87 12.513 12.400
M97 11.247 55.017
>>> minsize = 80
>>> messier.query("Mag < Mag.mean() and Size > @minsize and Name").Name
M
M24 Sagittarius Star Cloud
M31 Andromeda Galaxy
M44 Beehive Cluster
M45 Pleiades
Name: Name, dtype: object
Attention : par défaut, beaucoup d’opérations retournent une copie de la structure, sauf si l’opé-
ration se fait « sur place » (inplace=True). D’autres opérations d’accès retournent seulement une
nouvelle vue des mêmes données.
Indéxation hiérarchique
Les informations contenues sont toujours les mêmes, mais structurées différemment :
>>> saisons.loc['spring'].head(3) # Utilisation du 1er label
M NGC Mag Size Distance RA Dec Con Name
Type
Gc M3 5272 6.2 16.2 10400.0 13.703 28.383 CVn
Ds M40 Win4 8.4 0.8 156.0 12.373 58.083 UMa Winnecke 4
El M49 4472 8.4 8.2 18400000.0 12.497 8.000 Vir
>>> saisons.loc['spring', 'El'].head(3) # Utilisation des 2 labels
M NGC Mag Size Distance RA Dec Con Name
Season Type
spring El M49 4472 8.4 8.2 18400000.0 12.497 8.00 Vir
El M59 4621 9.6 4.2 18400000.0 12.700 11.65 Vir
El M60 4649 8.8 6.5 18400000.0 12.728 11.55 Vir
La fonction pandas.DataFrame.xs() permet des sélections sur les différents niveaux d’indexation :
>>> saisons.xs('spring', level='Season').head(3) # = saisons.loc['spring']
M NGC Mag Size Distance RA Dec Con Name
Type
Gc M3 5272 6.2 16.2 10400.0 13.703 28.383 CVn
Ds M40 Win4 8.4 0.8 156.0 12.373 58.083 UMa Winnecke 4
El M49 4472 8.4 8.2 18400000.0 12.497 8.000 Vir
>>> saisons.xs('El', level='Type').head(3) # Sélection sur le 2e niveau
M NGC Mag Size Distance RA Dec Con Name
(suite sur la page suivante)
Le (multi-)index n’est pas nécessairement trié à sa création, pandas.sort_index() permet d’y remédier :
>>> saisons[['M', 'NGC', 'Name']].head()
M NGC Name
Season Type
winter Sn M1 1952 Crab Nebula
autumn Gc M2 7089
spring Gc M3 5272
summer Gc M4 6121
Gc M5 5904
>>> saisons[['M', 'NGC', 'Name']].sort_index().head()
M NGC Name
Season Type
autumn El M32 221
El M110 205
Gc M2 7089
Gc M15 7078 Great Pegasus Globular
Gc M30 7099
Note : l’interopérabilité entre pandas et numpy est totale, toutes les fonctions Numpy peuvent prendre
une structure Pandas en entrée, et s’appliquer aux colonnes appropriées :
>>> N.mean(messier, axis=0) # Moyenne sur les lignes → Series indexée sur les colonnes
Mag 7.492727e+00
Size 1.771909e+01
Distance 4.574883e+06
RA 1.303774e+01
Dec 9.281782e+00
dtype: float64
>>> N.random.seed(0)
>>> df = PD.DataFrame(
{'one': PD.Series(N.random.randn(3), index=['a', 'b', 'c']),
'two': PD.Series(N.random.randn(4), index=['a', 'b', 'c', 'd']),
'three': PD.Series(N.random.randn(3), index=['b', 'c', 'd'])})
>>> df
one three two
a 1.764052 NaN 2.240893
b 0.400157 -0.151357 1.867558
c 0.978738 -0.103219 -0.977278
d NaN 0.410599 0.950088
>>> df['four'] = df['one'] + df['two']; df # Création d'une nouvelle colonne
one three two four
a 1.764052 NaN 2.240893 4.004946
b 0.400157 -0.151357 1.867558 2.267715
c 0.978738 -0.103219 -0.977278 0.001460
(suite sur la page suivante)
>>> df.sort_values(by='a', axis=1) # Tri des colonnes selon les valeurs de la ligne 'a'
one two three
a 1.764052 2.240893 NaN
b 0.400157 1.867558 -0.151357
c 0.978738 -0.977278 -0.103219
d NaN 0.950088 0.410599
>>> df.min(axis=1) # Valeur minimale le long des colonnes
a 1.764052
b -0.151357
c -0.977278
d 0.410599
dtype: float64
>>> df.idxmin(axis=1) # Colonne des valeurs minimales le long des colonnes
a one
b three
c two
d three
dtype: object
>>> df.mean(axis=0) # Moyenne sur toutes les lignes (gestion des données manquantes)
one 1.047649
three 0.052007
two 1.020315
dtype: float64
Note : Si les bibliothèques d’optimisation de performances bottleneck et numexpr sont installées, pandas
en bénéficiera de façon transparente.
Histogramme et discrétisation
Partitionner les objets en 3 groupes de magnitude (par valeurs : pandas.cut(), par quantiles : pandas.
qcut()), et les compter :
Group-by
7.1.5 Visualisations
Exemple :
Références :
— Visualization
— Seaborn: statistical data visualization
— Iris Dataset
— Histoire des sujets télévisés
Liens
— Pandas tutorials
— Pandas Cookbook
— Pandas Lessons for New Users
— Practical Data Analysis
Exercices :
Voir également :
— geopandas : extension pour des opérations spatiales sur des formes géométriques
7.1.6 Xarray
xarray est une bibliothèque pour la structuration de données homogènes de dimension arbitraire. Suivant
la philosophie de la bibliothèque Pandas dont elle est issue (et dont elle dépend), elle permet notamment
de nommer les différentes dimensions (axes) des tableaux (p.ex. x.sum(axis='time')), d’indexer les
données (p.ex. x.loc['M31']), de naturellement gérer les opérations de broadcasting, des opérations de
regroupement et d’agrégation (p.ex. x.groupby(time.dayofyear).mean()), une gestion plus facile des
données manquantes et d’alignement des tableaux indexés (p.ex. align(x, y, join='outer')).
pandas excels at working with tabular data. That suffices for many statistical analyses, but
physical scientists rely on N-dimensional arrays – which is where xarray comes in.
xarray fournit deux structures principales, héritées du format netCDF :
— xarray.DataArray, un tableau N-D indexé généralisant le pandas.Series ;
— xarray.Dataset, un dictionnaire regroupant plusieurs DataArray alignés selon une ou plusieurs
dimensions, et similaire au pandas.DataFrame.
>>> N.random.seed(0)
>>> data = X.DataArray(N.arange(3*4, dtype=float).reshape((3, 4)), # Tableau de données
... dims=('x', 'y'), # Nom des dimensions
... coords={'x': list('abc')}, # Indexation des coordonnées en 'x'
... name='mesures', # Nom du tableau
... attrs=dict(author='Y. Copin')) # Métadonnées
>>> data
<xarray.DataArray 'mesures' (x: 3, y: 4)>
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
Coordinates:
* x (x) <U1 'a' 'b' 'c'
Dimensions without coordinates: y
Attributes:
author: Y. Copin
>>> data.to_pandas() # Conversion en DataFrame à indexation simple
y 0 1 2 3
x
a 0.0 1.0 2.0 3.0
b 4.0 5.0 6.0 7.0
c 8.0 9.0 10.0 11.0
>>> data.to_dataframe() # Conversion en DataFrame multi-indexé (hiérarchique)
mesures
(suite sur la page suivante)
>>> data['isSmall'] = data.sum(dim='y') < 10; data # Booléen "Somme sur y < 10"
<xarray.DataArray 'mesures' (x: 3, y: 4)>
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
Coordinates:
* x (x) <U1 'a' 'b' 'c'
isSmall (x) bool True False False
Dimensions without coordinates: y
>>> data.groupby('isSmall').mean(dim='x') # Regroupement et agrégation
<xarray.DataArray 'mesures' (isSmall: 2, y: 4)>
array([[ 6., 7., 8., 9.],
[ 0., 1., 2., 3.]])
Coordinates:
* isSmall (isSmall) object False True
Dimensions without coordinates: y
— Examples
7.2 Astropy
astropy est une bibliothèque astronomique maintenue par la communauté et visant à fédérer les efforts
jusque là disparates. Elle offre en outre une interface unifiée à des bibliothèques affiliées plus spécifiques.
7.2.2 Démonstration
Démonstration Astropy (astropy.ipynb)
Voir également :
— AstroBetter tutorials
Note : N’oubliez pas de citer [Astropy13] ou de mentionner l’utilisation d’astropy dans vos publications
et présentations.
Développer en Python
— Le zen du Python
— Us et coutumes
— Principes de conception logicielle
— Développement piloté par les tests
— Outils de développement
— Integrated Development Environment
— Vérification du code
— Débogage
— Profilage et optimisation
— Documentation
— Python packages
— Système de gestion de versions
— Intégration continue
Le zen du Python (PEP 20) est une série de 20 aphorismes 1 donnant les grands principes du Python :
79
Analyse scientifique avec Python, Version Janvier 2021
8.1.1 Us et coutumes
— Keep it simple, stupid !
— Don’t repeat yourself.
— Fail early, fail often, fail better ! (raise)
— Easier to Ask for Forgiveness than Permission (try ... except)
— We’re all consenting adults here. (attributs privés)
— « Don’t reinvent the wheel, unless you plan on learning more about wheels » (Jeff Atwood) :
cherchez si ce que vous voulez faire n’a pas déjà été fait (éventuellement en mieux. . . ) pour vous
concentrer sur votre valeur ajoutée, réutilisez le code (en citant évidemment vos sources), améliorez
le, et contribuez en retour si possible !
— Écrivez des programmes pour les humains, pas pour les ordinateurs : codez proprement, structurez
vos algorithmes, commentez votre code, utilisez des noms de variable qui ont un sens, soignez le
style et le formatage, etc.
— Code is read far more often than it is written. Ne croyez pas que vous ne relirez jamais votre code
(ou même que personne n’aura jamais à le lire), ou que vous aurez le temps de le refaire mieux
plus tard. . .
— You ain’t gonna need it : se concentrer sur les fonctionnalités nécessaires plutôt que de prévoir
d’emblée l’ensemble des cas.
— « Premature optimization is the root of all evil » (Donald Knuth) : mieux vaut un code lent
mais juste et maintenable qu’un code rapide et faux ou incompréhensible. Dans l’ordre absolu des
priorités :
1. Make it work.
2. Make it right.
3. Make it fast.
— Respectez le zen du python, il vous le rendra.
Voir également :
Le Test Driven Development (TDD, ou en français « développement piloté par les tests ») est une méthode
de programmation qui permet d’éviter des bogues a priori plutôt que de les résoudre a posteriori. Ce n’est
pas une méthode propre à Python, elle est utilisée très largement par les programmeurs professionnels.
Le cycle préconisé par TDD comporte cinq étapes :
1. Écrire un premier test ;
2. Vérifier qu’il échoue (puisque le code qu’il teste n’existe pas encore), afin de s’assurer que le test
est valide et exécuté ;
3. Écrire un code minimal pour passer le test ;
4. Vérifier que le test passe correctement ;
5. Éventuellement « réusiner » le code (refactoring), c’est-à-dire l’améliorer (rapidité, lisibilité) tout
en gardant les mêmes fonctionnalités.
«~Diviser pour mieux régner~» : chaque fonction, classe ou méthode est testée indépendemment. Ainsi,
lorsqu’un nouveau morceau de code ne passe pas les tests qui y sont associés, il est certain que l’erreur
provient de cette nouvelle partie et non des fonctions ou objets que ce morceau de code utilise. On
distingue ainsi hiérarchiquement :
1. Les tests unitaires vérifient individuellement chacune des fonctions, méthodes, etc. ;
2. Les tests d’intégration évaluent les interactions entre différentes unités du programmes ;
3. Les tests système assurent le bon fonctionnement du programme dans sa globalité.
Il est très utile de transformer toutes les vérifications réalisées au cours du développement et du débogage
sous forme de tests, ce qui permet de les réutiliser lorsque l’on veut compléter ou améliorer une partie
du code. Si le nouveau code passe toujours les anciens tests, on est alors sûr de ne pas avoir cassé les
fonctionnalités précédentes (régréssions).
Nous avons déjà vu aux TD précédents plusieurs façons de rédiger des tests unitaires.
— Un doctest est un exemple (assez simple) d’exécution de code inclus dans la docstring d’une classe
ou d’une fonction :
11 Exemples:
12 >>> mean_power([1, 2, 3])
13 2.0
14 >>> mean_power([1, 2, 3], power=2)
15 2.160246899469287
16 """
17
21 return mean
— Les fonctions dont le nom commence par test_ et contenant des assert sont automatiquement
détectées par pytest 2 . Cette méthode permet d’effectuer des tests plus poussés que les doctests,
éventuellement dans un fichier séparé du code à tester. P.ex. :
1 def test_empty_init():
2 with pytest.raises(TypeError):
3 Animal()
4
6 def test_wrong_init():
7 with pytest.raises(ValueError):
8 Animal('Youki', 'lalala')
9
10
11 def test_init():
12 youki = Animal('Youki', 600)
13 assert youki.masse == 600
14 assert youki.vivant
15 assert not youki.empoisonne
8.3.3 Débogage
Les débogueurs permettent de se « plonger » dans un code en cours d’exécution ou juste après une erreur
(analyse post-mortem).
— Module de la bibliothèque standard : pdb
Pour déboguer un script, il est possible de l’exécuter sous le contrôle du débogueur pdb en s’in-
terrompant dès la 1re instruction :
python -m pdb script.py
(Pdb)
Avant toute optimisation, s’assurer extensivement que le code fonctionne et produit les bons résultats
dans tous les cas. S’il reste trop lent ou gourmand en mémoire pour vos besoins, il peut être nécessaire
de l’optimiser.
Le profilage permet de déterminer le temps passé dans chacune des sous-fonctions d’un code (ou ligne
par ligne : line profiler, ou selon l’utilisation de la mémoire : memory profiler), afin d’y identifier les
parties qui gagneront à être optimisées.
— python -O, __debug__, assert
Il existe un mode « optimisé » de python (option -O), qui pour l’instant ne fait pas grand chose
(et n’est donc guère utilisé. . . .) :
— Tutoriel de profilage
Une fois identifiée la partie du code à optimiser, quelques conseils généraux :
— en cas de doute, favoriser la lisibilité aux performances ;
— utiliser des opérations sur les tableaux, plutôt que sur des éléments individuels (vectorization) :
listes en compréhension, tableaux numpy (qui ont eux-mêmes été optimisés) ;
— cython est un langage de programmation compilé très similaire à python. Il permet d’écrire des
extensions en C avec la facilité de python (voir notamment Working with Numpy) ;
— numba permet automagiquement de compiler à la volée (JIT (Just In Time)) du pur code python
via le compilateur LLVM, avec une optimisation selon le CPU (éventuellement le GPU) utilisé,
p.ex. :
from numba import jit # compilation à la volée (seulement au 1e appel)
@jit
def crible(n):
...
ou :
from numba import guvectorize # ufunc numpy compilée
— à l’avenir, l’interpréteur CPython actuel sera éventuellement remplacé par pypy, basé sur une
compilation JIT.
Lien :
Performance tips
8.3.5 Documentation
— Outils de documentation, ou comment transformer automagiquement un code-source bien docu-
menté en une documentation fonctionnelle.
— Sphinx ;
— reStructuredText for Sphinx ;
— Awesome Sphinx ;
— apidoc (documentation automatique).
— Conventions de documentation :
— Docstring convention : PEP 257 ;
— Documenting Your Project Using Sphinx ;
— A Guide to NumPy/SciPy Documentation ;
— Sample doc (matplotlib).
Lien :
Documentation Tools
Références supplémentaires
Voici une liste très partielle de documents Python disponibles en ligne. La majorité des liens sont en
anglais, quelques-uns sont en français.
Note : N’hésitez pas à me faire des retours sur les différents liens (j’avoue ne pas les avoir tous testés. . . )
ou sur d’autres que vous avez trouvé particulièrement utiles.
89
Analyse scientifique avec Python, Version Janvier 2021
ipython
— IPython tutorial
— IPython cookbook
— IPython en ligne
— IPython quick refsheet
Expressions rationnelles
— regex tester
Python 3.x
— Python 3 Patterns, Recipes and Idioms
— 10 awesome features of Python that you can’t use because you refuse to upgrade to Python 3
9.4.1 Python
— 6 formations en ligne ;
— Apprenez à programmer en Python
— Débuter avec Python au lycée
— Présentation de Python
— Introduction à Python pour la programmation scientifique
— Google’s Python Class
— Begginer’s Guide
— DIY python workshop
— Google’s Python Class
— Hack in Science, plusieurs dizaines d’exercices et corrections interactives
— CheckIO, pour apprendre la programmation Python en s’amusant !
— Python Programming (Code Academy)
— Tutoriaux Zeste de savoir : programmation orientée objet, notions avancées,
9.4.2 Scientifique
— Scipy Cookbook (inclut numpy, scipy, matplotlib, etc.)
— Python Scientific Lecture Notes
— Handbook of the Physics Computing Course (Oxford, 2003)
— Practical Scientific Computing in Python
— Computational Physics with Python (avec exercices)
— SciPy tutorials (numpy, scipy, matplotlib, ipython) : 2011, 2012, 2013,
— Advance Scientific Programming in Python
— Lectures on Computational Economics (avec exercices)
— Intro to Python for Data Science (DataCamp avec vidéos et exercices)
— Python for Data Science
— Learning Python For Data Science
— Computational Statistics in Python
— Python Data Science Handbook
— An Introduction To Machine Learning
En français
9.4.3 Snippets
— Python cheatsheets
Exemples
1 #!/usr/bin/env python3
2 # coding: utf-8
3
4 """
5 Exemple de script (shebang, docstring, etc.) permettant une
6 utilisation en module (`import mean_power`) et en exécutable (`python
7 mean_power.py -h`);
8 """
9
10
21 Exemples:
22 >>> mean_power([1, 2, 3])
23 2.0
24 >>> mean_power([1, 2, 3], power=2)
25 2.160246899469287
26 """
27
31 return mean
32
33
34 if __name__ == '__main__':
35
36 # start-argparse
(suite sur la page suivante)
93
Analyse scientifique avec Python, Version Janvier 2021
39 parser = argparse.ArgumentParser()
40 parser.add_argument('list', nargs='*', type=float, metavar='nombres',
41 help="Liste de nombres à moyenner")
42 parser.add_argument('-i', '--input', nargs='?', type=argparse.FileType('r'),
43 help="Fichier contenant les nombres à moyenner")
44 parser.add_argument('-p', '--power', type=float, default=1.,
45 help="'Puissance' de la moyenne (par défaut: %(default)s )")
46
47 args = parser.parse_args()
48 # end-argparse
49
63 # Calcul
64 moyenne = mean_power(args.list, args.power)
65
66 # Affichage du résultat
67 print("La moyenne puissance 1/{0} des {1} nombres à la puissance {0} "
68 " est {2} .".format(args.power, len(args.list), moyenne))
Source : mean_power.py
1 #!/usr/bin/env python3
2 # coding: utf-8
3
4 """
5 Exemple (tragique) de Programmation Orientée Objet.
6 """
7
11 class Animal:
12 """
13 Un animal, défini par sa masse.
14 """
15
24 self.estVivant = True
25
26 self.masse = float(masse)
27 if self.masse < 0:
28 raise ValueError("La masse ne peut pas ^etre négative.")
29
30
31 def __str__(self):
32 """
33 Surcharge de la fonction `str()`.
34
44
45 def meurt(self):
46 """
47 L'animal meurt.
48 """
49
50 self.estVivant = False
51
52
61 self.masse += float(masse)
62
63
66 class AnimalFeroce(Animal):
67 """
68 Un animal féroce est un animal qui peut dévorer d'autres animaux.
69
81 def __str__(self):
82 """
83 Surcharge de la fonction `str()`.
(suite sur la page suivante)
94 * Si other est également un animal féroce, il faut que self soit plus
95 gros que other pour le dévorer. Sinon, other se défend et self meurt.
96 * Si self dévore other, other meurt, self grossit de la masse de other
97 (jusqu'à 10% de sa propre masse) et other maigrit d'autant.
98
115
157 Animal.meurt(self)
158 print(f"Pauvre { self.nom} meurt, paix à son ^ame...")
159
160
183 print(elephant)
184 print(lion)
Exécution du code :
$ ./animal.py
=====================Une tragédie en trois actes======================
--------------------Acte I: la vache prend 10 kg.---------------------
Animal vivant, 510 kg
----------------------Acte II: Dumbo l'éléphant-----------------------
Dumbo, un animal gentil bien vivant, 1000 kg
-----------------------Acte III: le féroce lion-----------------------
Animal féroce bien vivant, 200 kg
-------------Scène tragique: le lion dévore l'éléphant...-------------
Pauvre Dumbo, paix à son ^ame...
Dumbo, un animal gentil mais mort, 980 kg
Animal féroce bien vivant, 220 kg
Source : animal.py
1 #!/usr/bin/env python3
2 # coding: utf-8
3
4 """
5 Calcule le cercle circonscrit à 3 points du plan.
6
32
33 class Point:
34
35 """
36 Classe définissant un `Point` du plan, caractérisé par ses
37 coordonnées `x`,`y`.
38 """
39
58 def __str__(self):
59 """
(suite sur la page suivante)
66 >>> str(Point(1,2))
67 'Point (x=1.0, y=2.0)'
68 """
69
72 def isOrigin(self):
73 """
74 Teste si le point est à l'origine en testant la nullité des deux
75 coordonnées.
76
80 >>> Point(1,2).isOrigin()
81 False
82 >>> Point(0,0).isOrigin()
83 True
84 """
85
86 import sys
87
104
108
111
114 """
115 Un `Vector` hérite de `Point` avec des méthodes additionnelles
116 (p.ex. la négation d'un vecteur, l'addition de deux vecteurs, ou
117 la rotation d'un vecteur).
118 """
119
219 >>> Vector(Point(1, 0), Point(1, 1)).rotate(90, deg=True) == Vector(O, Point(-1, 0))
220 True
221 """
222
233
254 MN = Vector(M, N)
255 NP = Vector(N, P)
256 PM = Vector(P, M)
257
263 d = (m + n + p) * (-m + n + p) * (m - n + p) * (m + n - p)
264 if d > 0:
265 rad = m * n * p / d**0.5
266 else:
267 raise ValueError("Undefined circumscribed circle radius.")
268
283
286 # start-argparse
287 import argparse
288
350 P.show()
$ ./circonscrit.py -h
usage: circonscrit.py [-p/--plot] [-i/--input coordfile | x1,y1 x2,y2 x3,y3]
positional arguments:
x,y Coordinates of point
optional arguments:
-h, --help show this help message and exit
-i [INPUT], --input [INPUT]
Coordinate file (one 'x,y' per line)
-p, --plot Draw the circumscribed circle
--version show program's version number and exit
Source : circonscrit.py
10.4 Matplotlib
1 #!/usr/bin/env python3
2 # Time-stamp: <2018-01-09 15:03:52 ycopin>
3
4 """
5 Exemple un peu plus complexe de figure, incluant 2 axes, légendes, axes, etc.
6 """
7
8 import numpy as N
(suite sur la page suivante)
13 # Signal carré
14 y = N.sign(N.sin(x)) # = ± 1
15
23 # Figure
24 fig = P.figure() # Création de la Figure
25
Source : figure.py
1 #!/usr/bin/env python3
2
3 import numpy as N
4 import matplotlib.pyplot as P
5
13 return 1 / (1 - x ** 2 + x / Q * 1j)
14
15
18 return -x ** 2 / (1 - x ** 2 + x / Q * 1j)
19
(suite sur la page suivante)
23 return 1 / (1 + Q * (x - 1 / x) * 1j)
24
25
28 return (1 - x ** 2) / (1 - x ** 2 + x / Q * 1j)
29
30
37 g = N.abs(f) # Gain
38 if dB: # [dB]
39 g = 20 * N.log10(g)
40 p = N.angle(f) # [rad]
41
42 return g, p
43
44
47 lx = N.log10(x)
48 return N.where(lx < 0, pentes[0] * lx, pentes[1] * lx)
49
50
55
64 fig = P.figure()
65 axg = fig.add_subplot(2, 1, 1, # Axe des gains
66 xscale='log',
67 ylabel='Gain [dB]')
68 axp = fig.add_subplot(2, 1, 2, # Axe des phases
69 sharex=axg,
70 xlabel=r'x = $\omega$/$\omega_0$', xscale='log',
71 ylabel='Phase [rad]')
72
79 # Asymptotes
80 if gAsymp is not None: # Gain
81 axg.plot(x, asympGain(x, gAsymp), 'k:', lw=2, label='_')
82 if pAsymp is not None: # Phase
83 # axp.plot(x, asympPhase(x,pAsymp), 'k:')
84 pass
85
86 axg.legend(loc='best', prop=dict(size='small'))
87
105 fig.subplots_adjust(hspace=0.1)
106 axg.xaxis.set_major_formatter(P.matplotlib.ticker.ScalarFormatter())
107 P.setp(axg.get_xticklabels(), visible=False)
108
109 if title:
110 axg.set_title(title)
111
114
143 P.show()
Source : filtres2ndOrdre.py
Exercices
11.1 Introduction
def sq(x) :
return x**2
Écrire un programme calculant l’intégrale de cette fonction entre a=0 et b=1, en utilisant une subdivision
en n=100 pas dans un premier temps. Quelle est la précision de la méthode, et comment dépend-elle du
nombre de pas ?
109
Analyse scientifique avec Python, Version Janvier 2021
Écrire un programme calculant le PGCD (Plus Grand Commun Dénominateur) de deux nombres (p.ex.
756 et 306) par l”algorithme d’Euclide.
1 x 1 = 1
1 x 2 = 2
...
9 x 9 = 81
Implémenter le Crible_d’Ératosthène pour afficher les nombres premiers compris entre 1 et un entier
fixe, p.ex. :
11 18 25 2 9
10 12 19 21 3
4 6 13 20 22
23 5 7 14 16
17 24 1 8 15
Pour les carrés magiques d’ordre impair, on dispose de l’algorithme suivant – (i,j) désignant la case de
la ligne i, colonne j du carré ; on se place en outre dans une indexation « naturelle » commençant à 1 :
1. la case (n,(n+1)/2) contient 1 ;
2. si la case (i,j) contient la valeur k, alors on place la valeur k+1 dans la case (i+1,j+1) si cette
case est vide, ou dans la case (i-1,j) sinon. On respecte la règle selon laquelle un indice supérieur
à n est ramené à 1.
Programmer cet algorithme pour pouvoir construire un carré magique d’ordre impair quelconque.
11.3 Programmation
Écrire une fonction suite_syracuse(n) retournant la (partie non-triviale de la) suite de Syracuse pour
un entier n. Écrire une fonction temps_syracuse(n, altitude=False) retournant le temps de vol (éven-
tuellement en altitude) correspondant à l’entier n. Tester ces fonctions sur n=15 :
>>> suite_syracuse(15)
[15, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
>>> temps_syracuse(15)
17
>>> temps_syracuse(15, altitude=True)
10
$ py.test animaux.py
Dans un premier temps, les tests échouent, puisque le proto-code (dans la première partie du fichier)
n’est pas encore correct. L’exercice consiste donc à modifier progressivement les classes Animal et Chien
pour qu’elles passent avec succès tous les tests. C’est le principe du Test Driven Development (voir
Développement piloté par les tests).
...#..#.....##.......
.....###.............
#........#...........
.....#...#...........
................##...
.....#.#......##..#..
..............##.##..
..............##.##..
................#....
Astuce : Pour que l’affichage soit agréable à l’oeil, vous marquerez des pauses entre l’affichage de chaque
itération grâce à la fonction time.sleep().
Créer un tableau carré réel r aléatoire (numpy.random.randn()), calculer la matrice hermitienne m = r·r𝑇
(numpy.dot()), l’inverser (numpy.linalg.inv()), et vérifier que m · m−1 = m−1 · m = 1 (numpy.eye())
à la précision numérique près (numpy.allclose()).
Si les erreurs 𝜎𝑖 sont correctes, la distribution du pull est centrée sur 0 avec une déviation standard de
1.
Écrire une fonction pull(x, dx) calculant le pull de tableaux 1D.
Pour chacun des jeux de données, tracer y en fonction de x, ainsi que la droite de régression linéaire.
Écrivez une fonction qui calcule la valeur d’équilibre de la Suite logistique pour un 𝑥0 (nécessairement
compris entre 0 et 1) et un paramètre 𝑟 (parfois noté 𝜇) donné.
Générez l’ensemble de ces points d’équilibre pour des valeurs de 𝑟 comprises entre 0 et 4 :
N.B. Vous utiliserez la bibliothèque Matplotlib pour tracer vos résultats.
Indication : Vous écrirez une classe Simulation qui permet de charger un fichier de dynamique
moléculaire, puis de tracer l’évolution de a température et de la densité, et enfin d’en extraire la valeur
moyenne et les fluctuations. À partir de cette classe, vous construirez les tableaux contenant l’équation
d’état.
— Exercices de base
— Entraînez-vous !
— Learn Python The Hard Way
— Google Code Jam
— CheckIO
=> Final system : Particle with mass 1.00, position (-1.00,-0.00,0.00) and speed (0.00,-1.00,0.
˓→00)
** Electrostatic computation of central-force motion for a Ion with mass 1.00, charge 4,␣
˓→position (0.00,0.00,1.00) and speed (0.00,0.00,-1.00)
=> Final system : Ion with mass 1.00, charge 4, position (0.00,0.00,7.69) and speed (0.00,0.00,
˓→2.82)
Projets
— Projets
— Physique
— Astrophysique
— Relation masse/rayon d’une naine blanche
— Section de Poincaré
— Divers
— Formation de pistes de fourmis sur un pont à 2 branches
— Auto-organisation d’un banc de poisson
— Évacuation d’une salle & déplacement d’une foule dans une rue
— Suivi de particule(s)
— Mini-projets
— Statistiques
— Visualisation
— Divers
— Contribution à un projet Open Source
12.1 Physique
12.2 Astrophysique
𝐺𝑀 (𝑟)
∇𝑃 (𝑟) = −𝜌(𝑟) 𝑒𝑟
𝑟2
119
Analyse scientifique avec Python, Version Janvier 2021
Modélisation
La masse et le rayon d’équilibre de ce système sont entièrement déterminés par l’équation d’état ther-
modynamique 𝑃 = 𝑃 (𝜌) et la densité centrale. En effet on montre facilement que :
(︂ )︂−1
𝑑𝜌 𝑑𝑃 𝐺𝑀
=− 𝜌
𝑑𝑟 𝑑𝜌 𝑟2
𝑑𝑀
= 4𝜋𝑟2 𝜌
𝑑𝑟
Une fois que les réactions thermonucléaires s’arrêtent, la première des forces empêchant l’étoile de s’ef-
fondrer vient de la pression due aux électrons. Le modèle que nous utiliserons sera donc un simple gaz
d’électrons (masse 𝑚𝑒 et de nombre par unité de volume n) plongé dans un gaz de noyaux (on note 𝑌𝑒
le nombre d’électrons par nucléon et 𝑀𝑛 la masse d’un nucléon) d’équation d’état :
𝐸
= 𝑛0 𝑚𝑒 𝑐2 𝑥3 𝜀(𝑥),
𝑉
(︂ )︂ 13
𝜌
avec 𝑥 = ,
𝜌0
𝑚3 𝑐3
𝑛0 = 3𝑒 2 ,
3~ 𝜋
𝑀𝑛 𝑛 0
𝜌0 =
𝑌𝑒
3 [︁ √︀ (︁ √︀ )︁]︁
et 𝜀(𝑥) = 3 𝑥(1 + 2𝑥2 ) 1 + 𝑥2 − ln 𝑥 + 1 + 𝑥2
8𝑥
12
Si tous les noyaux sont du 𝐶, alors 𝑌𝑒 = 1/2.
1. Montrer que le système d’équations à résoudre est
(︃ √ )︃
𝑑𝜌 3𝑀𝑛 𝐺 1 + 𝑥2 𝑀
=− 𝜌
𝑑𝑟 𝑌𝑒 𝑚𝑒 𝑐2 𝑥2 𝑟2
𝑑𝑀
= 4𝜋𝑟2 𝜌
𝑑𝑟
2. En fixant la densité centrale 𝜌(𝑟 = 0) = 𝜌𝑐 tracer 𝜌(𝑟) et en déduire une méthode pour calculer le
rayon R de l’étoile et sa masse M.
3. En faisant varier la densité centrale tracer la relation 𝑀 (𝑅).
4. Discuter la validité numérique et physique des résultats par exemple en changeant la composition
de l’étoile, la définition du rayon de l’étoile, etc.
Les équations du mouvement 1 𝑟(𝑡) = (𝑥(𝑡), 𝑦(𝑡)) d’une particule de masse 𝑚 plongée dans un potentiel
Φ(𝑥, 𝑦) s’écrivent :
𝑟 = −∇Φ.
𝑚¨
1. On se place dans toute la suite du problème dans un espace à deux dimensions.
En coordonnées polaires :
1 𝜕Φ
𝑎𝑟 = 𝑟¨ − 𝑟𝜃˙2 = −
𝑚 𝜕𝑟
1 𝜕Φ
𝑎𝜃 = 2𝑟˙ 𝜃˙ + 𝑟𝜃¨ = −
𝑚𝑟 𝜕𝜃
Le système peut donc s’écrire :
1 𝜕Φ
𝑟¨ = 𝑟𝜃˙2 −
𝑚 𝜕𝑟
2 1 𝜕Φ
𝜃¨ = − 𝑟˙ 𝜃˙ −
𝑟 𝑚𝑟2 𝜕𝜃
ou en posant 𝑟𝑝 = 𝑟˙ et 𝜃𝑝 = 𝜃˙ :
𝑟˙ = 𝑟𝑝
𝜃˙ = 𝜃𝑝
1 𝜕Φ
𝑟˙𝑝 = 𝑟𝜃𝑝2 −
𝑚 𝜕𝑟
2 1 𝜕Φ
𝜃˙𝑝 = − 𝑟𝑝 𝜃𝑝 −
𝑟 𝑚𝑟2 𝜕𝜃
˙ =
L’intégration – analytique ou numérique – de ces équations pour des conditions initiales (𝑟(𝑡 = 0), 𝑟(𝑡
0)) particulières caractérise une orbite. Le tracé de l’ensemble des points d’intersection de différentes
orbites de même énergie avec le plan, p.ex., (𝑥, 𝑥)˙ (avec 𝑦 = 0 et 𝑦˙ > 0) constitue une section de
Poincaré.
Nous étudierons plus particulièrement le cas particulier 𝑚 = 1 et les deux potentiels :
1. le potentiel intégrable de Sridhar & Touma (1997 ; MNRAS, 287, L1) 2 , qui s’exprime naturelle-
ment dans les coordonnées polaires (𝑟, 𝜃) :
Objectif
12.3 Divers
Fig. 12.1 – Figure : 1) la première fourmi trouve la source de nourriture (F), via un chemin quelconque
(a), puis revient au nid (N) en laissant derrière elle une piste de phéromone (b). 2) les fourmis empruntent
indifféremment les 4 chemins possibles, mais le renforcement de la piste rend plus attractif le chemin le
plus court. 3) les fourmis empruntent le chemin le plus court, les portions longues des autres chemins
voient la piste de phéromones s’évaporer. Source : Johann Dréo via Wikimedia Commons.
Fig. 12.2 – Figure : Environnement proche du poisson : zones dans lesquelles le positionnement d’un
voisin provoque une réponse de la part de l’individu au centre
L’environnement proche d’un poisson est modélisé par des sphères imbriquées qui présentent une zone
aveugle (voir figure).
Par ailleurs, si un individu n’a pas de voisins dans son environnement proche il adopte un comportment de
recherche. Il explore aléatoirement les alentours jusqu’à ce qu’il repère le banc de poissons et finalement
s’en rapproche.
Ce projet vise à :
— Coder le comportement des poissons et à les faire évoluer dans un environnement 2D.
— On essaiera d’obtenir un comportement collectif cohérent (similaire à un banc de poisson) et
d’établir les conditions nécessaires à ce comportement.
— On étudiera notamment l’influence du nombre d’individus pris en compte. Est-ce que le position-
nement par rapport au plus proche voisin (k = 1) est suffisant ?
— On pourra se servir de la visualisation pour rendre compte de la cohérence du comportment et
éventuellement inventer des mesures pour rendre compte de manière quantifier de cette cohérence.
Liens :
12.3.3 Évacuation d’une salle & déplacement d’une foule dans une rue
Le comportement d’une foule est un problème aux applications multiples : évacuation d’une salle, couloir
du métro aux heures de pointes, manifestations. . . On peut en imaginer des modèles simples. P. ex., on
peut décrire chaque individu par sa position, sa vitesse, et comme étant soumis à des « forces » :
— Une force qui spécifie la direction dans laquelle l’individu veut se déplacer, f𝑑𝑖𝑟 = (v0 − v(𝑡))/𝜏 ,
où v0 est la direction et la vitesse que la personne veut atteindre, v sa vitesse actuelle, et 𝜏 un
temps caractéristique d’ajustement.
— Une force qui l’oblige à éviter des obstacles qui peuvent être fixes (un mur, un massif de fleurs, . . . ),
ou qui peuvent être les autres individus eux-mêmes. On pourra essayer 𝑓𝑜𝑏𝑠 (𝑑) = 𝑎 exp(−𝑑/𝑑0 ),
où 𝑑 est la distance entre le piéton et l’obstacle, 𝑑0 la « portée » de la force, et 𝑎 son amplitude.
On pourra varier les différents paramètres apparaissant ci-dessus, tout en leur donnant une interpréta-
tion physique réelle, et étudier leur influence dans des situations concrètes. P. ex., à quelle vitesse, en
fonction de v0 et de la densité de piétons, se déplace une foule contrainte à avancer dans un couloir si
chaque individu veut maintenir une vitesse v0 ? Comment s’organise l’évacuation d’une salle initialement
uniformément peuplée, avec une ou plusieurs sorties, et en la présence éventuels d’obstacles ?
Il est également possible d’essayer d’autres expressions pour les forces.
Il existe une littérature conséquente sur le sujet, que l’on pourra explorer si besoin (p. ex : Décrypter le
mouvement des piétons dans une foule).
Fig. 12.4 – Figure : Exemple d’image test où on voudra localiser les particules.
12.4 Mini-projets
Ces mini-projets demande un investissement de développement et d’analyse moindre, et peuvent être
traités (ou au moins initiés) en quelques heures comme des exercices ouverts.
12.4.1 Statistiques
— Tests statistiques du NIST/SEMATECH e-Handbook of Statistical Methods, p.ex. Comparisons
based on data from two processes
— Statistiques robustes, p.ex. Beers et al. (1990)
12.4.2 Visualisation
L’objectif premier de ces projets est de développer des outils de visualisation sous Python/Matplotlib.
— Coordonnées parallèles
— Sources éventuelles d’inspiration : Parallel Coordinates plot in Matplotlib, XDAT
— Exemples de jeu de données multi-variables : Iris flower data set, Cars (source)
— Andrew Curves (voir également Rip’s Applied Mathematics Blog). À appliquer sur les mêmes
jeux de données que pour les coordonnées parallèles.
— Stacked graphs (source éventuelle d’inspiration : Python recipe)
— Hertzsprung-Russel diagram : l’objectif est de développer une classe permettant de tracer des dia-
grammes HR à partir de diverses quantités observationnelles (magnitudes apparentes ou absolues,
couleurs) ou théoriques (luminosité, températures effectives), ainsi que des isochrones.
— Source éventuelle d’inspiration : Stellar evolutionary tracks
— Données photométriques : p.ex. M55 (source : BVI photometry in M55)
— Données théoriques : p.ex. CMD
— Treemap (source éventuelle d’inspiration : Treemaps under pylab)
— De façon plus générale, l’ensemble des visualisations proposées sous :
— Flare
— D3
— Periodic Table of Vizualisation Methods
12.4.3 Divers
— Numération_romaine *
— Jeu du Taquin **
— Jeu du Mastermind **
— Abelian_sandpile_model ***
— Jeu 2048 (voir également Make your own 2048 in python) ***
Annales d’examen
127
Analyse scientifique avec Python, Version Janvier 2021
Démonstration Astropy
# Interactive figures
# %matplotlib notebook
# Static figures
%matplotlib inline
Le format FITS (Flexible Image Transport System) constitue le format de données historique (et encore
très utilisé) de la communauté astronomique. Il permet le stockage simultané de données – sous forme
de tableaux numériques multidimensionnels (spectre 1D, image 2D, cube 3D, etc.) ou de tables de
données structurées (texte ou binaires) – et des métadonnées associées – sous la forme d’un entête ASCII
nommé header. Il autorise en outre de combiner au sein d’un même fichier différents segments de données
(extensions, p.ex. le signal et la variance associée) sous la forme de HDU (Header-Data Units).
Le fichier FITS de test est disponible ici : image.fits (données Herschel Space Observatory)
129
Analyse scientifique avec Python, Version Janvier 2021
filename = "image.fits"
hdulist = F.open(filename)
hdulist est un objet HDUList de type liste regroupant les différents HDU du fichier :
[3]: hdulist.info()
Filename: image.fits
No. Name Ver Type Cards Dimensions Format
0 PRIMARY 1 PrimaryHDU 151 ()
1 image 1 ImageHDU 52 (273, 296) float64
2 error 1 ImageHDU 20 (273, 296) float64
3 coverage 1 ImageHDU 20 (273, 296) float64
4 History 1 ImageHDU 23 ()
5 HistoryScript 1 BinTableHDU 39 105R x 1C [300A]
6 HistoryTasks 1 BinTableHDU 46 77R x 4C [1K, 27A, 1K, 9A]
7 HistoryParameters 1 BinTableHDU 74 614R x 10C [1K, 20A, 7A, 46A, 1L, 1K, 1L,␣
˓→74A, 11A, 41A]
Il est également possible de lire directement les données et les métadonnées de l’extension image :
[5]: ima, hdr = F.getdata(filename, 'image', header=True)
print(type(ima), type(hdr))
<class 'numpy.ndarray'> <class 'astropy.io.fits.header.Header'>
L’entête hdr est un objet de type Header similaire à un OrderedDict (dictionnaire ordonné).
[7]: hdr[:5] # Les 5 premières clés de l'ent^ete
[7]: XTENSION= 'IMAGE ' / Java FITS: Wed Aug 14 11:37:21 CEST 2013
BITPIX = -64
NAXIS = 2 / Dimensionality
NAXIS1 = 273
NAXIS2 = 296
Attention : les axes des tableaux FITS et NumPy arrays sont inversés !
ax.coords['ra'].set_ticks(color='r')
ax.coords['dec'].set_ticks(color='r')
overlay['l'].set_axislabel('Galactic Longitude')
overlay['b'].set_axislabel('Galactic Latitude')
overlay['l'].set_ticks(color='g')
overlay['b'].set_ticks(color='g')
14.2 Tables
Outre les tables FITS, astropy permet lire et écrire des Tables dans de nombreux formats ASCII usuels
en astronomie (LaTeX, HTML, CDS, SExtractor, etc.).
Le fichier ASCII de test est disponible ici : sources.dat
catalog = ascii.read('sources.dat')
catalog.info()
<Table length=167>
name dtype
------------ -------
ra float64
dec float64
x float64
y float64
raPlusErr float64
decPlusErr float64
raMinusErr float64
decMinusErr float64
xPlusErr float64
yPlusErr float64
(suite sur la page suivante)
#catalog.show_in_notebook(display_length=5)
catalog[:5] # Les cinq sources les plus brillantes du catalogue
[14]: <Table length=5>
ra dec x ... bgMinusErr quality
float64 float64 float64 ... float64 float64
------------- -------------- ------------- ... ---------------- -------------
30.0736543481 -24.8389847181 133.076596062 ... 0.00280563968109 24.0841967062
30.0997563127 -25.030193106 118.886083699 ... 0.00310958937187 16.5084425251
30.2726211379 -25.0771584874 24.9485847511 ... 0.00603800958334 6.67900541976
29.8763509609 -24.7518860739 240.583543382 ... 0.00466051306854 9.08251222505
29.8668948822 -24.8539846811 245.642862323 ... 0.00330155226713 8.43689223988
fig, ax = P.subplots()
VIZ.hist(catalog['flux'], bins='freedman', ax=ax, histtype='stepfilled')
ax.set(xlabel="Flux", title="%d sources" % len(catalog));
Après conversion des coordonnées RA-Dec de la table en coordonnées, on peut superposer la position
des 5 sources les plus brillantes du catalogue à l’image précédente :
[16]: fig, ax = P.subplots(figsize=(6, 6))
ax.imshow(ima, cmap='gray', origin='lower', interpolation='None', vmin=-2e-2, vmax=5e-2)
ax.set(xlabel="x [px]", ylabel="y [px]", title=filename)
x, y = wcs.wcs_world2pix(catalog['ra'][:5], catalog['dec'][:5], 0)
(suite sur la page suivante)
s = 1 * U.mJy
print ("{} = {} à {} ".format(s, s.to('erg/(cm**2 * s * angstrom)',
equivalencies=U.spectral_density(1 * U.micron)), 1 * U.
˓→micron))
ax1.xaxis.set_minor_locator(P.matplotlib.ticker.LogLocator(subs=range(2,10)))
ax1.yaxis.set_minor_locator(P.matplotlib.ticker.LogLocator(subs=range(2,10)))
ax1.grid(which='minor', color='w', linewidth=0.5)
ax2.xaxis.set_minor_locator(P.matplotlib.ticker.LogLocator(subs=range(2,10)))
ax2.yaxis.set_minor_locator(P.matplotlib.ticker.LogLocator(subs=range(2,10)))
ax2.grid(which='minor', color='w', linewidth=0.5)
fig.subplots_adjust(wspace=0.3)
fig.suptitle(cosmo.name, fontsize='x-large');
Voici un exemple d’utilisation des libraries Pandas (manipulation de données hétérogène) et Seaborn
(visualisations statistiques), sur le Pokémon dataset d’Alberto Barradas.
Références :
— Visualizing Pokémon Stats with Seaborn
— Pokemon Stats Analysis And Visualizations
%matplotlib inline
139
Analyse scientifique avec Python, Version Janvier 2021
Legendary
Name
Bulbasaur False
Ivysaur False
Venusaur False
Mega Venusaur False
Charmander False
— par une sélection booléenne, p.ex. tous les pokémons légendaires de type herbe :
[7]: df[df['Legendary'] & (df['Type 1'] == 'Grass')]
[7]: Type 1 Type 2 Total HP Attack Defense Speed \
Name
ShayminLand Forme Grass 600 100 100 100 100
ShayminSky Forme Grass Flying 600 100 103 75 127
Virizion Grass Fighting 580 91 90 72 108
Generation Legendary
Name
ShayminLand Forme 4 True
ShayminSky Forme 4 True
Virizion 5 True
Generation Legendary
Name
DeoxysSpeed Forme 3 True
Ninjask 3 False
DeoxysNormal Forme 3 True
15.4 Visualisation
Pandas intègre de nombreuses fonctions de visualisation interfacées à matplotlib.
fig.tight_layout()
Il est également possible d’utiliser la librairie seaborn, qui s’interface naturellement avec Pandas.
ax.figure.set_size_inches((14, 6))
1 #!/usr/bin/env python3
2 # Time-stamp: <2018-07-16 18:07:22 ycopin>
3
4 """
5 Calcul de l'intégrale de x**2 entre 0 et 1 par la méthode des rectangles
6 (subdivision en 100 pas)
7 """
8
9 def sq(x):
10 "Définition de la fonction sq: x → x**2."
11
12 return x**2
13
14 a, b = 0, 1 # Bornes d'intégration
15 n = 100 # Nombre de pas
16
$ python3 integ.py
Source : integ.py
149
Analyse scientifique avec Python, Version Janvier 2021
Fizz Buzz
1 #!/usr/bin/env python3
2 # Time-stamp: <2018-07-26 16:46 [email protected]>
3
4 """
5 Jeu du Fizz Buzz
6 """
7
10
$ python3 fizz.py
Source : fizz.py
151
Analyse scientifique avec Python, Version Janvier 2021
Algorithme d’Euclide
1 #!/usr/bin/env python3
2 # coding: utf-8
3
6 """
7 Calcul du PGCD de deux entiers 0 < b < a.
8 """
9
19 # On boucle jusqu'à ce que le reste soit nul, d'où la boucle while. Il faut
20 # ^etre s^ur que l'algorithme converge dans tous les cas!
21 while True:
22 r = a % b
23 if r == 0: # Reste de la division euclidienne
24 break # en sortie, PGCD = b
25 else:
26 a, b = b, r # Itération
27
$ python3 pgcd.py
153
Analyse scientifique avec Python, Version Janvier 2021
Source : pgcd.py
Crible d’Ératosthène
1 #!/usr/bin/env python3
2 # coding: utf-8
3
4 """
5 Crible d'Ératosthène.
6
7 Source: http://fr.wikibooks.org/wiki/Exemples_de_scripts_Python#Implémentation_du_crible_d
˓→'Ératosthène
8 """
9
12 # start-sys
13 # Gestion simplifiée d'un argument entier sur la ligne de commande
14 import sys
15
30 i = 2 # Entier à tester
31 while i**2 <= n: # Inutile de tester jusqu'à n
32 if l[i] != 0: # Si i n'est pas étiqueté (=0)...
33 # ...étiqueter tous les multiples de i: de 2 * i à n (inclu) par pas de i
34 for j in range(2 * i, n + 1, i):
35 l[j] = 0
36 # Les 2 lignes précédentes peuvent ^etre fusionnées:
37 # l[2 * i::i] = [0] * len(l[2 * i::i])
38 i += 1 # Passer à l'entier à tester suivant
(suite sur la page suivante)
155
Analyse scientifique avec Python, Version Janvier 2021
$ python3 crible.py
Source : crible.py
Carré magique
1 #!/usr/bin/env python3
2 # coding: utf-8
3
6 """
7 Création et affichage d'un carré magique d'ordre impair.
8 """
9
21 # Initialisation de l'algorithme
22 i, j = n, (n + 1) // 2 # Indices de l'algo (1-indexation)
23 array[i - 1][j - 1] = 1 # Attention: python utilise une 0-indexation
24
157
Analyse scientifique avec Python, Version Janvier 2021
$ python3 carre.py
Source : carre.py
Suite de Syracuse
1 #!/usr/bin/env python3
2 # Time-stamp: <2018-07-26 16:57 [email protected]>
3
7 def suite_syracuse(n):
8 """
9 Retourne la suite de Syracuse pour l'entier n.
10
11 >>> suite_syracuse(15)
12 [15, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
13 """
14
22 return seq
23
24
30 >>> temps_syracuse(15)
31 17
32 >>> temps_syracuse(15, altitude=True)
33 10
34 """
35
36 seq = suite_syracuse(n)
37 if not altitude: # Temps de vol total
38 return len(seq) - 1
39 else: # Temps de vol en altitude
(suite sur la page suivante)
159
Analyse scientifique avec Python, Version Janvier 2021
49 if __name__ == '__main__':
50
51 n = 15
52 print("Suite de Syracuse pour n =", n)
53 print(suite_syracuse(n))
54 print("Temps de vol total: ", temps_syracuse(n))
55 print("Temps de vol en altitude:", temps_syracuse(n, altitude=True))
Source : syracuse.py
Flocon de Koch
1 #!/usr/bin/env python3
2 # coding: utf-8
3
6 """
7 Tracé (via 'turtle') d'un flocon de Koch d'ordre arbitraire.
8
15 Voir également:
16
17 - L-système: http://fr.wikipedia.org/wiki/L-système
18 - Autres exemples: http://natesoares.com/tutorials/python-fractals/
19 """
20
21 import turtle as T
22
26
161
Analyse scientifique avec Python, Version Janvier 2021
55 if __name__ == '__main__':
56
57 # start-argparse
58 # Exemple d'utilisation de la bibliothèque de gestion d'arguments 'argparse'
59 import argparse
60
91 if args.turbo:
92 T.hideturtle()
93 T.speed(0)
94
102 T.exitonclick()
Source : koch.py
163
Analyse scientifique avec Python, Version Janvier 2021
1 #!/usr/bin/env python3
2 # coding: utf-8
3
4 import random
5
19 except ValueError:
20 print(f"Votre proposition { proposition!r} "
21 f"n'est pas un entier compris entre { nmin} et { nmax} .")
22 continue # Nouvel essai
23
Source : pm.py
165
Analyse scientifique avec Python, Version Janvier 2021
Animaux
1 #!/usr/bin/env python3
2 # coding: utf-8
3
4 """
5 Exercice: programmation orientée objet, développement dirigé par les tests.
6 """
7
8 class Animal:
9 """
10 Classe définissant un `Animal`, caractérisé par son nom et son
11 poids.
12 """
13
19 # Ici, convertir les paramètres pour ^etre s^ur qu'il ont le bon
20 # type. On utilisera `str` et `float`
21 self.nom = str(nom)
22 self.masse = float(masse)
23
27 def __str__(self):
28 """
29 Surcharge de l'opérateur `str`: l'affichage *informel* de l'objet
30 dans l'interpréteur, p.ex. `print self` sera résolu comme
31 `self.__str__()`
32
38 def estVivant(self):
39 """Méthode booléenne, vraie si l'animal est vivant."""
(suite sur la page suivante)
167
Analyse scientifique avec Python, Version Janvier 2021
41 return self.vivant
42
43 def mourir(self):
44 """Change l'état interne de l'objet (ne retourne rien)."""
45
46 self.vivant = False
47
71 other.mourir()
72 poids = min(other.masse, self.masse * 0.1)
73 self.masse += poids
74 other.masse -= poids
75 if other.empoisonne:
76 self.mourir()
77
78
79 class Chien(Animal):
80 """
81 Un `Chien` hérite de `Animal` avec des méthodes additionnelles
82 (p.ex. l'aboiement et l'odorat).
83 """
84
94 def __str__(self):
95
98 def aboyer(self):
99 """Une méthode bien spécifique aux chiens."""
100
108 if vivant:
109 self.aboyer()
110
113 #########################################################
114 # Il est *INTERDIT* de modifier les tests ci-dessous!!! #
115 #########################################################
116
119 # start-tests
120 def test_empty_init():
121 with pytest.raises(TypeError):
122 Animal()
123
124
129
137
142
149
157
169
Analyse scientifique avec Python, Version Janvier 2021
173
animauxSol.py ........
Source : animauxSol.py
Particules
1 #!/usr/bin/env python3
2 # Time-stamp: <2018-07-19 10:34 [email protected]>
3
8 """
9 Définition d'une classe point matériel, avec sa masse, sa position et sa
10 vitesse, et des méthodes pour le déplacer. Le main test applique cela à un
11 problème à force centrale gravitationnel ou électrostatique.
12
18
22
23 #############################################################################
24 ### Définition de la classe Vector, utile pour la position et la vitesse. ###
25 #############################################################################
26
27 class Vector:
28 """
29 Une classe-structure simple contenant 3 coordonnées.
30 Une méthode est disponible pour en calculer la norme et
31 une surcharge des opérateurs ==, !=, +, - et * est proposée.
32 """
33
39 Args:
(suite sur la page suivante)
171
Analyse scientifique avec Python, Version Janvier 2021
42 Raises:
43 TypeError en cas de composantes non réelles
44 """
45 try:
46 self.x = float(x)
47 self.y = float(y)
48 self.z = float(z)
49 except (ValueError, TypeError, AttributeError):
50 raise TypeError("The given coordinates must be numbers")
51
52 def __str__(self):
53 """
54 Surcharge de l'opérateur `str`.
55
56 Returns :
57 "(x,y,z)" avec 2 décimales
58 """
59 return "({:.2f} ,{:.2f} ,{:.2f} )".format(self.x, self.y, self.z)
60
66 Args :
67 other(Vector): Un autre vecteur
68
69 Raises :
70 TypeError si other n'est pas un objet Vector
71 """
72 try:
73 return abs(self.x - other.x) < tolerance and \
74 abs(self.y - other.y) < tolerance and \
75 abs(self.z - other.z) < tolerance
76 except (ValueError, TypeError, AttributeError):
77 raise TypeError("Tried to compare Vector and non-Vector objects")
78
84 Args :
85 other(Vector): Un autre vecteur
86
87 Raises :
88 TypeError si other n'est pas un objet Vector
89 """
90 return not self == other
91
96 Args :
97 other(Vector): Un autre vecteur
98
99 Raises :
100 TypeError si other n'est pas un objet Vector
(suite sur la page suivante)
111 Args :
112 other(Vector): Un autre vecteur
113
114 Raises :
115 TypeError si other n'est pas un objet Vector
116 """
117 try:
118 return Vector(self.x - other.x, self.y - other.y, self.z - other.z)
119 except (ValueError, TypeError, AttributeError):
120 raise TypeError("Tried to substract Vector and non-Vector objects")
121
127 Args :
128 number(float): Un nombre à multiplier par le Vector.
129
130 Raises :
131 TypeError si other n'est pas un nombre
132 """
133 try:
134 return Vector(number * self.x, number * self.y, number * self.z)
135 except (ValueError, TypeError, AttributeError):
136 raise TypeError("Tried to multiply Vector and non-number objects")
137
144 Returns :
145 sqrt(x**2 + y**2 + z**2)
146 """
147 return (self.x ** 2 + self.y ** 2 + self.z ** 2) ** (1 / 2)
148
155
156 ###############################################
157 ##### Quelques test pour la classe Vector #####
158 ###############################################
159
173
Analyse scientifique avec Python, Version Janvier 2021
170
175
186
192
199
206
212
220
221 ############################################################
222 ##### Une classe point matériel qui se gère en interne #####
(suite sur la page suivante)
227 """
228 La classe Particle représente un point matériel doté d'une masse,
229 d'une position et d'une vitesse. Elle possède également une méthode
230 pour calculer la force gravitationnelle exercée par une autre particule.
231 Enfin, la méthode update lui permet de mettre à jour sa position et
232 sa vitesse en fonction des forces subies.
233 """
234
240 Args :
241 mass(float): La masse de la particule (doit ^etre
242 strictement positive)
243 position(Vector): La position initiale de la particule
244 speed(Vector): La vitesse initiale de la particule
245
246 Raises :
247 TypeError si la masse n'est pas un nombre, ou si la position ou
248 la vitesse ne sont pas des Vector
249 ValueError si la masse est négative ou nulle
250 """
251 try:
252 self.mass = float(mass)
253 self.position = position.clone()
254 self.speed = speed.clone()
255 except (ValueError, TypeError, AttributeError):
256 raise TypeError("The mass must be a positive float number. "
257 "The position and speed must Vector objects.")
258 try:
259 assert mass > 0 # une masse négative ou nulle pose des problèmes
260 except AssertionError:
261 raise ValueError("The mass must be strictly positive")
262 self.force = Vector()
263
268 Returns :
269 "Particle with mass m, position (x,y,z) and speed (vx,vy,vz)"
270 with 2 decimals
271 """
272 return "Particle with mass {:.2f} , position {} " \
273 "and speed {} ".format(self.mass, self.position, self.speed)
274
280 Args :
281 other(Particle): Une autre particule, source de l'interaction
282
283 Raises :
(suite sur la page suivante)
175
Analyse scientifique avec Python, Version Janvier 2021
297 Args :
298 dt(float): Pas de temps d'intégration.
299 """
300 try:
301 d = float(dt)
302 except (ValueError, TypeError, AttributeError):
303 raise TypeError("The integration timestep must be a number")
304 self.speed += self.force * dt * (1 / self.mass)
305 self.position += self.speed * dt
306
307
308 #############################################
309 ##### Des tests pour la classe Particle #####
310 #############################################
311
323
329
340
349
350 #######################################################
351 ##### Une classe Ion qui hérite de point matériel #####
352 #######################################################
353
367 Args :
368 mass(float): La masse de l'ion (doit ^etre strictement positive)
369 charge(float): La charge de l'ion (doit ^etre entière et
370 strictement positive)
371 position(Vector): La position initiale de la particule
372 speed(Vector): La vitesse initiale de la particule
373
374 Raises :
375 ValueError si charge < 0
376 TypeError si la masse n'est pas un réel,
377 si la charge n'est pas un entier,
378 si position ou speed ne sont pas des Vector
379 """
380 Particle.__init__(self, mass, position, speed)
381 try:
382 self.charge = int(charge)
383 except (ValueError, AttributeError, TypeError):
384 raise TypeError("The charge must be an integer.")
385 try:
386 assert self.charge > 0
387 except AssertionError:
388 raise ValueError("The charge must be positive.")
389
394 Returns :
395 "Ion with mass m, charge q, position (x,y,z)
396 and speed (vx,vy,vz)" avec q entier et le reste à 2 décimales
397 """
398 return "Ion with mass {:.2f} , charge {:d} , position {} " \
399 "and speed {} ".format(self.mass, self.charge,
400 self.position, self.speed)
401
177
Analyse scientifique avec Python, Version Janvier 2021
407 Args :
408 other(Ion): Un autre Ion, source de l'interaction.
409 Raises :
410 TypeError si other n'est pas un objet Ion
411 """
412 try:
413 r = self.position - other.position
414 self.force = self.charge * other.charge / r.norm() ** 3 * r
415 except (AttributeError, TypeError, ValueError):
416 raise TypeError("Tried to compute the force created by "
417 "a non-Ion object")
418
419
420 #######################################
421 ##### Des test pour la classe Ion #####
422 #######################################
423
436
442
452
453 ###########################
454 ##### Un main de test #####
455 ###########################
456
Source : particleSol.py
179
Analyse scientifique avec Python, Version Janvier 2021
Jeu de la vie
1 #!/usr/bin/env python3
2 # Time-stamp: <2018-07-26 16:54 [email protected]>
3
4 """
5 Jeu de la vie (programmation orientée objet).
6 """
7
8 import random
9
10
11 class Life:
12
13 cells = {False: ".", True: "#"} # Dead and living cell representations
14
22 self.h = int(h)
23 self.w = int(w)
24 assert self.h > 0 and self.w > 0
25 # Random initialization of a h×w world
26 self.world = [[random.choice([True, False])
27 for j in range(self.w)]
28 for i in range(self.h)] # h rows of w elements
29 self.periodic = periodic
30
37 if self.periodic:
38 return self.world[i % self.h][j % self.w] # Periodic conditions
39 else:
(suite sur la page suivante)
181
Analyse scientifique avec Python, Version Janvier 2021
45 def __str__(self):
46 """
47 Convert the grid to a visually handy string.
48 """
49
66 if count == 3:
67 # A cell w/ 3 neighbors will either stay alive or resuscitate
68 future = True
69 elif count < 2 or count > 3:
70 # A cell w/ too few or too many neighbors will die
71 future = False
72 else:
73 # A cell w/ 2 or 3 neighbors will stay as it is (dead or alive)
74 future = alive # Current status
75
76 return future
77
78 def evolve(self):
79 """
80 Evolve the game grid by one step.
81 """
82
88 if __name__ == "__main__":
89
90 import time
91
97 generation = 0
98 while True: # Infinite loop! (Ctrl-C to break)
99 print(generation)
100 print(life) # Print current world
(suite sur la page suivante)
Source : life.py
183
Analyse scientifique avec Python, Version Janvier 2021
1 #!/usr/bin/env python3
2
3 import numpy as N
4
11 # Median along given axis, but *keeping* the reduced axis so that
12 # result can still broadcast against a.
13 med = N.median(a, axis=axis, keepdims=True)
14 mad = N.median(N.absolute(a - med), axis=axis) # MAD along given axis
15
16 return mad
17
18 if __name__ == '__main__':
19
20 x = N.arange(4 * 5, dtype=float).reshape(4, 5)
21
22 print("x =\n", x)
23 print("MAD(x, axis=None) =", mad(x))
24 print("MAD(x, axis=0) =", mad(x, axis=0))
25 print("MAD(x, axis=1) =", mad(x, axis=1))
26 print("MAD(x, axis=(0, 1)) =", mad(x, axis=(0, 1)))
Source : mad.py
185
Analyse scientifique avec Python, Version Janvier 2021
Distribution du pull
1 #!/usr/bin/env python3
2 # coding: utf-8
3
4 import numpy as N
5
24 n = len(x)
25
31 v = dx ** 2 # Variance
32 w = 1 / v # Variance (optimal) weighting
33
39 return p
(suite sur la page suivante)
187
Analyse scientifique avec Python, Version Janvier 2021
41 if __name__ == '__main__':
42
43 import matplotlib.pyplot as P
44 import scipy.stats as SS
45
46 n = 1000
47 mu = 1.
48 sig = 2.
49
56 m, s = p.mean(), p.std(ddof=1)
57 print(f"Pull ({ n} entries): mean={ m: .2f} , std={ s: .2f} ")
58
59 fig, ax = P.subplots()
60 _, bins, _ = ax.hist(p, bins='auto', normed=True,
61 histtype='stepfilled',
62 label=f"#={ n} , µ={ m: .3f} , σ={ s: .3f} ")
63 y = N.linspace(-3, 3)
64 ax.plot(y, SS.norm.pdf(y), label=r"$\mathcal{N} $(µ=0, σ2 =1)")
65 ax.set(title='Pull distribution', xlabel='Pull')
66 ax.legend(loc='upper left')
67
68 P.show()
Source : pull.py
Quadrature
Calcul de l’intégrale
∞
𝑥3 𝜋4
∫︁
d𝑥 =
0 𝑒𝑥 − 1 15
This is separate from the ipykernel package so we can avoid doing imports until
189
Analyse scientifique avec Python, Version Janvier 2021
Il faut d’abord déterminer un intervalle contenant la solution, c.-à-d. le zéro de func. Puisque 𝑓 (0+ ) =
−4 < 0 et 𝑓 (10) ≃ 5 > 0, il est intéressant de tracer l’allure de la courbe sur ce domaine :
191
Analyse scientifique avec Python, Version Janvier 2021
Solution: 4.965114231744277
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exercices/numerique.ipynb ends here.
Quartet d’Anscombe
1 #!/usr/bin/env python3
2 # coding: utf-8
3
4 import numpy as N
5 import scipy.stats as SS
6 import matplotlib.pyplot as P
7
24
35 # Data + corrcoeff
36 ax.plot(x, y, 'bo', label=f"r = { r: .2f} ")
37
38 # Linear regression
39 xx = N.array([0, 20])
(suite sur la page suivante)
193
Analyse scientifique avec Python, Version Janvier 2021
52 m = N.mean(y)
53 s = N.std(y, ddof=1)
54 ax.axhline(m, color='g', ls='--', label='_') # Mean
55 ax.axhspan(m - s, m + s, color='g', alpha=0.2) # Std-dev
56
65
66 if __name__ == '__main__':
67
70 fig = P.figure()
71
82 P.show()
$ python3 anscombe.py
Source : anscombe.py
195
Analyse scientifique avec Python, Version Janvier 2021
Suite logistique
1 #!/usr/bin/env python3
2 # Time-stamp: <2018-07-19 10:42 [email protected]>
3
4 import numpy as np
5 import random
6 import matplotlib.pyplot as plt
7
11 x = random.uniform(0, 1)
12 i = 0
13 while i < niter and x < 1:
14 x = r * x * (1 - x)
15 i += 1
16
19
25 + le premier élément est la liste des valeurs prises par le paramètre *r*
26 + le second est la liste des points d'équilibre correspondants
27 """
28
29 r_v = []
30 x_v = []
31 for rr in r:
32 j = 0
33 while j < ntrials:
34 xx = iteration(rr)
35 if xx > 0: # Convergence: il s'agit d'une valeur d'équilibre
36 r_v.append(rr)
37 x_v.append(xx)
38 j += 1 # Nouvel essai
39
197
Analyse scientifique avec Python, Version Janvier 2021
42 r = np.linspace(0, 4, 1000)
43 x, y = generate_diagram(r)
44
45 plt.plot(x, y, 'r,')
46 plt.xlabel('r')
47 plt.ylabel('x')
48 plt.show()
Source : logistique.py
Ensemble de Julia
1 #!/usr/bin/env python3
2 # Time-stamp: <2018-07-19 10:43 [email protected]>
3
4 """
5 Visualisation de l'`ensemble de julia
6 <http://fr.wikipedia.org/wiki/Ensemble_de_Julia>`_.
7
11 import numpy as np
12 import matplotlib.pyplot as plt
13
26 # Visualisation
27 plt.imshow(np.abs(z), extent=[-xlim, xlim, -xlim, xlim], aspect='equal')
28 plt.title(c)
29 plt.show()
Source : julia.py
199
Analyse scientifique avec Python, Version Janvier 2021
Nous allons intégrer les équations du mouvement pour un boulet de canon soumis à des forces de frot-
tement « turbulentes » (non-linéaires) :
𝛼
r̈ = g − 𝑣 × v.
𝑚
Cette équation différentielle non linéaire du 2d ordre doit être réécrite sous la forme de deux équations
différentielles couplées du 1er ordre :
{︃
ṙ = v
𝛼
v̇ = g − 𝑚 𝑣 × v.
Il s’agit donc de résoudre une seule équation différentielle du 1er ordre en z = (r, v).
[1]: %matplotlib inline
import numpy as N
import scipy.integrate as SI
import matplotlib.pyplot as P
Conditions initiales :
[3]: v0 = 450. # Vitesse initiale [m/s]
alt = 45. # Inclinaison du canon [deg]
alt *= N.pi / 180. # Inclinaison [rad]
z0 = (0., 0., v0 * N.cos(alt), v0 * N.sin(alt)) # (x0, y0, vx0, vy0)
201
Analyse scientifique avec Python, Version Janvier 2021
√︁
𝑚
Temps caractéristique du système : 𝑡 = 𝑔𝛼 (durée du régime transitoire). L’intégration des équations
se fera sur un temps caractéristique, avec des pas de temps significativement plus petits.
x, y, vx, vy = z
alphav = alpha * N.hypot(vx, vy)
Le tableau zs contient les valeurs de 𝑧 à chaque instant 𝑡 : il est donc de taille (len(t),4).
1 #!/usr/bin/env python3
2 # Time-stamp: <2018-07-19 10:38 [email protected]>
3
5 import numpy as N
6 import matplotlib.pyplot as P
7
10 """
11 Construction d'un système d'extraction et d'analyse de fichiers de sortie de
12 dynamique moléculaire afin d'extraire les grandeurs thermodynamiques.
13 On affichera les ensuite isothermes.
14 """
15
18
21
22 ##############################
23 ##### A Simulation class #####
24 ##############################
25
26 class Simulation:
27 """
28 La classe Simulation représente une simulation de dynamique
29 moléculaire, donc un point de l'équation d'état. Son constructeur
30 doit impérativement ^etre appelé avec le chemin du fichier output
31 correspondant. Elle possède des méthodes pour extraire les grandeurs
32 thermodynamiques et afficher la run, en pouvant enlever certains pas
33 de temps en début de simulation.
34 """
35
203
Analyse scientifique avec Python, Version Janvier 2021
42 Args :
43 temp,dens(float): La température et la densité de la simulation
44 path(string): Le chemin vers le fichier décrivant la simulation
45
46 Raises :
47 TypeError si temp ou dens ne sont pas des réels
48 IOError si le fichier n'existe pas
49 """
50 self.temp = float(temp)
51 self.dens = float(dens)
52 tmp = N.loadtxt(path, skiprows=1).T
53 self.pot = tmp[0]
54 self.kin = tmp[1]
55 self.tot = self.pot + self.kin
56 self.press = tmp[2]
57
58 def __str__(self):
59 """
60 Surcharge de l'opérateur str.
61 """
62 return "Simulation at {:.0f} g/cc and {:.0f} K ; {:d} timesteps". \
63 format(self.dens, self.temp, len(self.pot))
64
70 Args:
71 skipSteps(int): Nb de pas à enlever en début de simulation.
72
73 Returns:
74 {'T':temperature, 'rho':density,
75 'E':energy, 'P':pressure,
76 'dE':dEnergy, 'dP':dPressure}
77 """
78 return {'T': self.temp,
79 'rho': self.dens,
80 'E': self.tot[skipSteps:].mean(),
81 'P': self.press[skipSteps:].mean(),
82 'dE': self.tot[skipSteps:].std(),
83 'dP': self.press[skipSteps:].std()}
84
90 Args:
91 skipSteps(int): Pas de temps à enelevr en début de simulation.
92
93 Raises:
94 TypeError si skipSteps n'est pas un entier.
95 """
96 fig, (axen, axpress) = P.subplots(2, sharex=True)
97 axen.plot(list(range(skipSteps, len(self.tot))), self.tot[skipSteps:],
98 'rd--')
99 axen.set_title("Internal energy (Ha)")
100 axpress.plot(list(range(skipSteps, len(self.press))), self.press[skipSteps:],
(suite sur la page suivante)
105 P.show()
106
109
120
128
134
145 ###################
146 ### Main script ###
147 ###################
148
205
Analyse scientifique avec Python, Version Janvier 2021
170 ### Extract the EOS out of the source files ###
171 for t, rho in [(t, rho) for t in temps for rho in denss]:
172 filenm = "outputs/{} K_{:0>2d} gcc.out".format(t, rho)
173 s = Simulation(t, rho, filenm)
174 for key in keys:
175 eos[key] = N.append(eos[key], s.thermo(nsteps)[key])
176
Source : equationEtatSol.py
207
Analyse scientifique avec Python, Version Janvier 2021
L’objectif de ce TD est de se familiariser avec les principales librairies numériques de Python, à savoir
numpy, scipy et matplotlib.
Vous importerez ces bibliothèques avec les instructions suivantes :
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats
Les parties notées « Pour aller plus loin » sont à traiter chez vous ou à la fin du TP si le temps le
permet.
IMPORTANT : n’oubliez pas d’ajouter des commentaires à votre code.
— fig est un objet de type Figure contenant un (ou plusieurs) système(s) d’axes, et pouvant être
affiché ou sauvegardé (p.ex. au format PDF ou PNG) ;
— ax est un objet de type Axes disposant de nombreuses méthodes de visualisation (plot, scatter,
imshow, hist, etc.), et de personalisation (xlabel, xscale, title, legend, grid, etc.).
209
Analyse scientifique avec Python, Version Janvier 2021
1. Modifier le code pour prendre en compte une largeur de fente et une longueur d’onde précise.
2. En choisissant différentes valeurs de 𝑎 et 𝜆, tracer sur une même figure l’évolution de la figure de
diffraction :
— pour des largeurs de fentes différentes, à une seule longueur d’onde,
— pour différentes longueurs d’onde, avec une fente de largeur fixée.
r3 = habenicht(3, theta)
r5 = habenicht(5, theta)
r7 = habenicht(7, theta)
Rappel
Pour tracer une courbe en coordonnées polaires, il faut un système d’axes adapté :
— Tracer dans un même système d’axes les différentes courbes polaires 𝑟𝑛=3,5,7 (𝜃).
[7]: z = 1 + 2j
print("z:", z)
print("Parties réelle et imaginaire:", np.real(z), np.imag(z))
print("Module et argument:", np.abs(z), np.angle(z))
z: (1+2j)
Parties réelle et imaginaire: 1.0 2.0
Module et argument: 2.23606797749979 1.1071487177940904
𝑃 𝐻2 −𝑥2
𝐻𝑄 (𝑥) =
1 − 𝑥2 + 𝑗𝑥/𝑄
x: pulsation réduite
Q: facteur de qualité
"""
return 20 * np.log10(np.abs(H))
# Gain [dB]
ax1.plot(x, gain_dB(H_PH2(x, Q=0.2)), label='Q=1/5')
ax1.plot(x, gain_dB(H_PH2(x, Q=1)), label='Q=1')
ax1.plot(x, gain_dB(H_PH2(x, Q=5)), label='Q=5')
# Phase [rad]
ax2.plot(x, np.angle(H_PH2(x, Q=0.2)))
ax2.plot(x, np.angle(H_PH2(x, Q=1)))
ax2.plot(x, np.angle(H_PH2(x, Q=5)))
[11]: # lecture des données à partir d'un fichier "classique" (séparation par des espaces,␣
˓→commentaires '#')
data = np.loadtxt('etalonnageMonochromateur.dat')
print(data.shape)
(6, 3)
fig, ax = plt.subplots(1, 1)
ax.scatter(position, wavelength, marker='+', s=50, label=u'Données')
ax.set(title=u'Étalonnage monochromateur',
xlabel='Position [u.a.]', ylabel = "Longueur d'onde [nm]")
ax.legend();
Étalonner le monochromateur consiste à déterminer une relation analytique approchant «au mieux» la
relation entre les valeurs (𝑝𝑖 , 𝜆𝑖 ) observée avec la lampe d’étalonnage. Une fois cette relation établie, elle
permet de prédire la longueur d’onde correspondant à une position 𝑝 quelconque (dans le domaine de
validité de la relation d’étalonnage).
Compte tenu de la répartition linéaire des points (𝜆𝑖 , 𝑝𝑖 ), la relation d’étalonnage sera obtenue en ajustant
les données avec une régression linéaire 𝜆 = 𝑎𝑝 + 𝑏 (utiliser scipy.stats.linregress) :
Le coefficient de corrélation linéaire 𝑟 (r_value) indique le degré de corrélation linéaire entre les deux
variables (−1 ≤ 𝑟 ≤ +1, on regarde en général le coeff. de détermination 𝑟2 ). (Les deux autres paramètres
p_value et std_err ne sont pas considérés ici.)
— Déterminer et afficher l’équation de la droite d’étalonnage 𝜆 = 𝑎𝑝 + 𝑏, ainsi que son coefficient de
détermination 𝑟2 .
ax.legend();
ax.set(xlim=(400, 700))
fig
[14]:
Pour aller plus loin : Ajouter sur la figure les barres d’erreur 𝛿𝑝 sur la position 𝑝. En toute rigueur,
il faudrait en tenir compte dans l’ajustement de la loi d’étalonnage (moindres carrés pondérés).
Pour la lisibilité, vous devrez multiplier les barres d’erreur 𝛿𝑝 par un facteur multiplicatif pour les
visualiser distinctement sur la figure. Ajouter cette information sur la figure.
ax.legend()
fig
[15]:
Nous allons charger et étudier 4 jeux de données (𝑥, 𝑦), d’abord en calculant des statistiques descriptives
(moyennes, écarts type, etc.) puis en les visualisant.
— Utiliser la fonction numpy.loadtxt pour charger les données du fichier anscombe.dat disponible
depuis Claroline. Quel format (shape) a le tableau de retour ?
— Extraire du tableau précédent 4 jeux de données j1 (les 2 premières colonnes), j2 (les 2 suivantes),
j3 et j4, chacun de format (11, 2).
print(j1.shape)
(11, 2)
— Pour chacun des 4 jeux de données x, y = j.T, calculer et afficher (avec 2 chiffres après la virgule)
les statistiques suivantes :
— les moyennes de 𝑥 et 𝑦 (numpy.mean),
— les écarts-type de 𝑥 et 𝑦 (numpy.std),
— le coefficient de corrélation entre 𝑥 et 𝑦 (scipy.stats.pearsonr),
— l’équation de la droite de régression linéaire 𝑦 = 𝑎𝑥 + 𝑏 (scipy.stats.linregress).
Que constatez-vous ? Que pouvez-vous en déduire ?
— Au sein d’une même figure (plt.subplots(2, 2)), tracer les 4 jeux de données (𝑥𝑖 , 𝑦𝑖 ) (p.ex.
plot(x, y, 'bo')), en y ajoutant à chaque fois la droite de régression linéaire (p.ex. plot(x,
a*x+b, 'r-')).
Que conclure ?
[19]: fig, axs = plt.subplots(2, 2, figsize=(8, 8), sharex=True, sharey=True) # retourne un tableau␣
˓→axs de format (2, 2)
%matplotlib inline
221
Analyse scientifique avec Python, Version Janvier 2021
38.2 Isopotentiels
if ax is None:
fig, ax = P.subplots()
if x is None:
x = N.linspace(-2, 2, 61)
if y is None:
y = N.linspace(-2, 2, 61)
xx, yy = N.meshgrid(x, y)
return ax
[6]: ax = plot_isopotentiels(potentiel_ST)
ax.set(title="Potentiel de Sridhar & Touma", aspect='equal')
ax.figure.set_size_inches((8, 6))
alpha = 0.5
r, theta, rdot, thetadot = z
if N.ndim(zs) == 2:
rs, thetas, rdots, thetadots = zs.T
else:
rs, thetas, rdots, thetadots = zs
[13]: ax = plot_isopotentiels(potentiel_ST)
ax.scatter([xs[0]], [ys[0]], marker='o', color='r') # Position initiale
ax.plot(xs, ys, color='0.2')
ax.set(title="Potentiel de Sridhar & Touma", aspect='equal')
ax.figure.set_size_inches((8, 6))
fig, ax = P.subplots()
ax.plot(t, es/E0 - 1)
ax.set(xlabel='Temps', ylabel=u"Fluctuation d'énergie")
ax.ticklabel_format(style='sci', scilimits=(-3, 3), axis='y');
Consignes :
— Vous avez accès à tout l’internet « statique » (hors mail, tchat, forum, etc.), y compris donc au
cours en ligne.
— Ne soumettez pas de codes non-fonctionnels (i.e. provoquant une exception à l’interprétation,
avant même l’exécution) : les erreurs de syntaxe seront lourdement sanctionnées.
— Respectez scrupuleusement les directives de l’énoncé (nom des variables, des méthodes, des fichiers,
etc.), en particulier concernant le nom des fichiers à renvoyer aux correcteurs.
39.1 Exercice
Un appareil de vélocimétrie a mesuré une vitesse à intervalle de temps régulier puis à sorti le fichier texte
velocimetrie.dat (attention à l’entête). Vous écrirez un script python « exo_nom_prénom.py » (sans
accent) utilisant matplotlib qui générera, affichera et sauvegardera sous le nom « exo_nom_prénom.
pdf » une figure composée de trois sous-figures, l’une au dessus de l’autre :
1. la vitesse en mm/s mesurée en fonction du temps,
2. le déplacement en mètres en fonction du temps. On utilisera volontairement une intégration naïve
à partir de zéro via la fonction numpy.cumsum(),
3. l’accélération en m/s2 en fonction du temps. On utilisera volontairement une dérivation naïve à
deux points :
𝑓 (𝑥 + ℎ) − 𝑓 (𝑥 − ℎ)
𝑓 ′ (𝑥) ≈
2ℎ
via la fonction numpy.diff(). Attention, si l’entrée de cette fonction est un tableau de taille N,
sa sortie est un tableau de taille N-1.
Le script doit lire le fichier velocimetrie.dat stocké dans le répertoire courant. On prendra soin des
noms des axes et des unités physiques. Si les trois axes des abscisses sont identiques, seul celui de la
troisième sous-figure peut être nommé.
227
Analyse scientifique avec Python, Version Janvier 2021
39.2.1 Introduction
Le problème du voyageur de commerce est un problème d’optimisation consistant à déterminer le plus
court chemin reliant un ensemble de destinations. Il n’existe pas d’algorithme donnant la solution opti-
male en un temps raisonnable (problème NP-complet), mais l’on peut chercher à déterminer des solutions
approchées.
On va se placer ici dans le cas d’un livreur devant desservir une seule fois chacune des n destinations
d’une ville américaine où les rues sont agencées en réseau carré (Figure). On utilise la « distance de
Manhattan » (norme L1) entre deux points 𝐴(𝑥𝐴 , 𝑦𝐴 ) et 𝐵(𝑥𝐵 , 𝑦𝐵 ) :
En outre, on se place dans le cas où les coordonnées des destinations sont entières, comprises entre 0 (in-
clus) et TAILLE = 50 (exclus). Deux destinations peuvent éventuellement avoir les mêmes coordonnées.
Les instructions suivantes doivent permettre de définir les classes nécessaires (Ville et Trajet) et de dé-
velopper deux algorithmes approchés (heuristiques) : l’algorithme du plus proche voisin, et l’optimisation
2-opt. Seules la librairie standard et la librairie numpy sont utilisables si nécessaire.
Un squelette du code, définissant l’interface de programmation et incluant des tests unitaires (à utiliser
avec py.test), vous est fourni : exam_1501.py. Après l’avoir renommé « pb_nom_prénom.py » (sans
accent), l’objectif est donc de compléter ce code progressivement, en suivant les instructions suivantes.
Une ville-test de 20 destinations est fournie : ville.dat (Fig.), sur laquelle des tests de lecture et
d’optimisation seront réalisés.
L=774
L=288
20 destinations L=276
50 12 5 16
87
17 3
40 14
10
4 1 19
30
18
20 15 9
11 2
10 6
13 0
0
0 10 20 30 40 50
Fig. 39.1 – Figure : Ville-test, avec 20 destinations et trois trajets de longueurs différentes : un trajet
aléatoire (L=774 ), un trajet plus proche voisins (L=288 ), et un trajet après optimisation opt-2 (L=276 ).
39.3 Correction
Corrigé
231
Analyse scientifique avec Python, Version Janvier 2021
232 Bibliographie
Index
Symboles broadcasting
* numpy, 48
dépaquetage, 28
** C
dépaquetage, 28 c_
**kwargs, 28 numpy, 45
*args, 28 class, 22
@ méthode de classe, 32
décorateur, 29 méthode statique, 32
numpy, 50 variable, 31
$PATH, 5 classmethod, 32
columns
A pandas, 65
agg complex
pandas, 72 type numérique, 11
all continue, 13
numpy, 50 cut
allclose pandas, 71
numpy, 50
any D
numpy, 50 décorateur
arange @, 29
numpy, 44 dépaquetage
argparse *, 28
module, 40 **, 28
args, 18 DataArray
array xarray, 74
numpy, 44 DataFrame
assert pandas, 64
exceptions, 22 Dataset
astropy xarray, 74
module, 76 def, 18
at dict
pandas, 66 itérables, 12
Axes dir, 15
matplotlib, 56 dot
axis numpy, 50
numpy, 49 drop
pandas, 68
B dropna
bool pandas, 68
type numérique, 11, 13 dstack
break, 13 numpy, 48
dtype
233
Analyse scientifique avec Python, Version Janvier 2021
numpy, 51 ipython, 7
python, 6
E isinstance, 12
exceptions itérables, 16
assert, 22 dict, 12
raise, 21 len, 14
try ... except, 21 list, 12
expand_dims set, 12
numpy, 47 slice, 14
str, 11, 14
F tuple, 12
Figure
matplotlib, 56 K
file, 26 kwargs, 18
fillna
pandas, 68 L
filter lambda, 31
pandas, 66 len
float itérables, 14
type numérique, 11 linspace
for ... in, 13 numpy, 44
full list
numpy, 44 itérables, 12
loc
G pandas, 66
genfromtxt xarray, 74
numpy, 51, 52 logspace
GridSpec numpy, 44
matplotlib, 57
groupby M
pandas, 72 méthode de classe
class, 32
H méthode statique
hstack class, 32
numpy, 48 matplotlib
Axes, 56
I Figure, 56
iat GridSpec, 57
pandas, 66 module, 54
identity mplot3d, 60
numpy, 50 pylab, 55
idxmin pyplot, 55
pandas, 71 savefig, 58
if ... elif ... else, 13 show, 58
iloc subplots, 56
pandas, 66 matrix
import, 19 numpy, 50
Index mayavi/mlab
pandas, 65 module, 60
index meshgrid
pandas, 65 numpy, 45
input, 26 mgrid
int numpy, 45
type numérique, 11 module
interfaces argparse, 40
jupyter, 7 astropy, 76
interpréteur matplotlib, 54
mayavi/mlab, 60
234 Index
Analyse scientifique avec Python, Version Janvier 2021
numpy, 43 rollaxis, 47
numpy.fft, 53 save/load, 52
numpy.linalg, 50 savetxt/loadtxt, 52
numpy.ma, 52 slicing, 46
numpy.polynomial, 53 squeeze, 47
numpy.random, 46, 53 transpose, 47
pandas, 63 ufuncs, 50
pickle, 40 vstack, 48
scipy, 53 where, 49
seaborn, 73 zeros, 44
sys, 39 numpy.fft
turtle, 111 module, 53
xarray, 74 numpy.linalg
mplot3d module, 50
matplotlib, 60 numpy.ma
module, 52
N numpy.polynomial
ndarray module, 53
numpy, 44 numpy.random
newaxis module, 46, 53
numpy, 47, 48
None, 11 O
nonzero ogrid
numpy, 49 numpy, 45
numpy ones
@, 50 numpy, 44
all, 50 opérateur ternaire (... if ... else ...), 13
allclose, 50 open, 26
any, 50
arange, 44 P
array, 44 pandas
axis, 49 agg, 72
broadcasting, 48 at, 66
c_, 45 columns, 65
dot, 50 cut, 71
dstack, 48 DataFrame, 64
dtype, 51 drop, 68
expand_dims, 47 dropna, 68
full, 44 fillna, 68
genfromtxt, 51, 52 filter, 66
hstack, 48 groupby, 72
identity, 50 iat, 66
linspace, 44 idxmin, 71
logspace, 44 iloc, 66
matrix, 50 Index, 65
meshgrid, 45 index, 65
mgrid, 45 loc, 66
module, 43 module, 63
ndarray, 44 pivot_table, 73
newaxis, 47, 48 qcut, 71
nonzero, 49 query, 66
ogrid, 45 reset_index, 69
ones, 44 Series, 64
r_, 45 set_index, 69
ravel, 47 sort_index, 69, 71
recarray, 51 sort_values, 71
reshape, 47 value_counts, 71
resize, 47 values, 65
Index 235
Analyse scientifique avec Python, Version Janvier 2021
xs, 69 seaborn
pickle module, 73
module, 40 sel
pivot_table xarray, 74
pandas, 73 Series
print, 15, 26 pandas, 64
property, 34 set
pylab itérables, 12
matplotlib, 55 set_index
pyplot pandas, 69
matplotlib, 55 show
Python Enhancement Proposals matplotlib, 58
PEP 20, 79 slice
PEP 257, 20, 86 itérables, 14
PEP 308, 13 slicing
PEP 3132, 28 numpy, 46
PEP 343, 36 sort_index
PEP 448, 28 pandas, 69, 71
PEP 466, 36 sort_values
PEP 484, 36 pandas, 71
PEP 498, 36 squeeze
PEP 526, 36 numpy, 47
PEP 8, 34, 81 staticmethod, 32
str
Q itérables, 11, 14
qcut subplots
pandas, 71 matplotlib, 56
query sys
pandas, 66 module, 39
R T
r_ transpose
numpy, 45 numpy, 47
raise try ... except
exceptions, 21 exceptions, 21
range, 12 tuple
ravel itérables, 12
numpy, 47 turtle
recarray module, 111
numpy, 51 type, 12
reset_index type numérique
pandas, 69 bool, 11, 13
reshape complex, 11
numpy, 47 float, 11
resize int, 11
numpy, 47
rollaxis U
numpy, 47 ufuncs
numpy, 50
S
save/load V
numpy, 52 value_counts
savefig pandas, 71
matplotlib, 58 values
savetxt/loadtxt pandas, 65
numpy, 52 variable
scipy class, 31
module, 53 variable d'environnement
236 Index
Analyse scientifique avec Python, Version Janvier 2021
$PATH, 5
vstack
numpy, 48
W
where
numpy, 49
while, 13
X
xarray
DataArray, 74
Dataset, 74
loc, 74
module, 74
sel, 74
xs
pandas, 69
Z
Zen du Python, 79
zeros
numpy, 44
Index 237