Cours Python
Cours Python
Le but de ce cours est de comprendre ce qu’est python, ce que python permet de réaliser et d’explorer ses
différentes fonctionnalités.
Sommaire :
1. Introduction
a) Présentation de python
b) Caractéristiques du langage
c) Fonctionnement d’un code source
d) Compilation/interprétation
e) Bibliothèque pour le calcul scientifique
f) Console et éditeur spider
2. Les différents types de variables
a) Scalaire et chaines
b) Le type list
c) Le type tuple
d) L’affectation multiple et permutation
e) Le type array de numpy
3. Les structure itératives et conditionnelles
a) La boucle “for”
b) La boucle “while”
c) Les conditions avec if, elif et else
4. Les fonctions
a) Syntaxe de base
b) Application
c) Exercice d’application
d) Fonction retournant
5. Affichage graphique mat plot lib
a) L’affichage 2D : plot
b) Le découpage de la fenêtre graphique : subplot
c) L’affichage 3D avec plot 3D
6. Les calculs avec scipy
a) Ajustement de données avec lingères
b) Ajustement de données avec poliplet
c) Ajustement de données avec curvefit
d) Absoudre des équations différentielles avec odeint
e) Absoudre un système d’équation avec fsolve
Introduction
1 Un peu d’histoire
Python est un langage de programmation objet interprété. Son origine est le langage de script
du système d’exploitation Amoeba (1990). Il a été développé par Guido Von Rossum au CWI,
à l’Université d’Amsterdam et nommé par rapport au Monthy Python’s Flying Circus.
Depuis, Python est devenu un langage de programmation généraliste (comp.lang.python est
créé en 1994). Il offre un environnement complet de développement comprenant un interpréteur
performant et de nombreux modules. Un atout indéniable est sa disponibilité sur la grande
majorité des plates-formes courantes (BeOS, Mac OS X, Unix, Windows).
Python est un langage open source supporté, développé et utilisé par une large communauté :
300 000 utilisateurs et plus de 500 000 téléchargements par an.
Comme tout langage de programmation Python n’est pas la solution ultime à tous les besoins.
Toutefois, on retrouve son utilisation dans différents contextes [successtory] comme Google, la
NASA, YouTube, Industrial Light & Magic, le projet One Laptop Per Child, l’environnement
Opie sur pocket PC, etc. Python est donc adapté pour réaliser de «grosses applications».
Quand Python ? Python est un très bon langage pour le développement agile cite{agile} et
itératif. Il permet d’être réactif et évite le cycle lourd programmer / compiler / tester. De
plus, le langage permet de vérifier très rapidement des hypothèses de mise en oeuvre. Une
fois la solution réalisée en Python, il est alors toujours possible de l’optimiser davantage
en re-développant certain de ses composants en C++ par exemple.
Python permet de produire des applications facilement extensibles non seulement par le
développeur mais aussi dans certains cas par l’usager (de nombreuses librairies scienti-
fiques sont disponibles).
Enfin, Python est un bon candidat pour l’automatisation de tâches systèmes. Le système
d’administration de la distribution Gentoo Linux portage est développé en Python. Une
partie au moins du support de l’intégration continue chez Google est fait en Python.
Quand pas python ?
Dynamicité est rarement compatible avec haute performance, Python n’est donc
certainement pas le meilleur langage pour faire du traitement intensif (calcul nu-
mérique, extraction de données, etc.). Mais il reste très pertinent pour piloter ce
type d’applications.
Dans certain cas, les usagers préfèrent limiter l’utilisation de Python à l’aspect
intégration de composants afin de produire des systèmes performants. La version
Python fournit une preuve de concept ou une version fonctionnelle avant de re-
développer les points faibles. Toutefois, les performances de Python sont souvent
suffisante pour des applications à forte charge (cf. YouTube)
Une dernière remarque pour terminer, Python permet d’identifier rapidement une mauvaise
idée...
Premiers pas en Python
1 Utilisation de Python
Python, comme la majorité des langages dit de script, peut être utilisé aussi bien en mode
interactif qu’en mode script / programme.
Dans le premier cas, il y a un dialogue entre l’utilisateur et l’interprète : les commandes entrées
par l’utilisateur sont évaluées au fur et à mesure. Cette première solution est pratique pour
réaliser des prototypes, ainsi que pour tester tout ou partie d’un programme ou plus simplement
pour interagir aisément et rapidement avec des structures de données complexes. Le listing
suivant présente comment lancer l’interprète (simplement en tapant python à l’invite du shell 1 )
et comment en sortir (en tapant Ctrl-D).
$ python
Python 2.7.2 (v2.7.2:8527427914a2, Jun 11 2011, 15:22:34)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print ’hello world!’
hello world!
>>> ^D
$
Pour une utilisation en mode script les instructions à évaluer par l’interprète sont sauvegardées,
comme n’importe quel programme informatique, dans un fichier. Dans ce second cas, l’utilisa-
teur doit saisir l’intégralité des instructions qu’il souhaite voir évaluer à l’aide de son éditeur
de texte favori, puis demander leur exécution à l’interprète. Les fichiers Python sont identifiés
par l’extension .py. Le listing suivant, l’interprète est appelé avec en paramètre le nom du
programme à évaluer (le traditionnel hello world’). Dans le cas d’un système Unix, la première
ligne du fichier ‘‘hello.py‘ précise quel interprète utiliser pour évaluer le fichier si ce dernier
est exécutable. Il suffit dans ce cas de taper le nom du fichier à l’invite du shell. :
$ cat hello.py
#! /usr/bin/env python
2 Structures de base
2.1 Commentaires
Comme dans la majorité des langages de script, les commentaires Python sont définis à l’aide
du caractère #. Qu’il soit utilisé comme premier caractère ou non, le # introduit un commen-
taire jusqu’à la fin de la ligne. Comme toujours, les commentaires sont à utiliser abondamment
avec parcimonie. Il ne faut pas hésiter à commenter le code, sans pour autant mettre des com-
mentaires qui n’apportent rien. Le listing suivant présente une ligne de commentaire en Python.
Les commentaires seront de nouveaux abordés en [sub :chap1 :doc] pour l’auto-documentation.
Les commentaires introduits par # devraient être réservés au remarques sur le code en sa mise
en oeuvre.
En Python, tout est objet. Quelque soit les données que l’on manipule, ces données sont des ob-
jets dont les classes sont définies par l’usager, ou par l’environnement Python pour les types de
base. Une conséquence est que l’utilisateur manipule les données au travers de références (qui
donnent accès aux fonctionnalités des instances). Cette approche permet de rendre homogène
les données manipulées par l’utilisateur (comme c’était le cas en SmallTalk). Ainsi, toutes les
données suivantes suivant sont des objets : 1, [2, 3, 4], 5.6, ‘toto’, une instance de Foo.
Python est un langage à typage dynamique. Ceci ne veut pas dire que les données que l’on
manipule ne sont pas typées, mais que leur type est «calculé» lors de leur utilisation 2 . Dans ce
contexte, le type des variables n’est pas défini explicitement par l’utilisateur. Ainsi, une même
variable peut référencer dans un programme des objets de types différents 3 .
Python permet d’exprimer très simplement des opérations arithmétiques. Dans le cas où tous
les opérandes sont des entiers, alors les résultats seront aussi des entiers. Lorsqu’au moins un
des opérandes est de type réel, alors tous les opérandes sont automatiquement convertis en
réels.
>>> x = 1 + 2
>>> y = 5 * 2
>>> y / x
3
>>> y % x
1
>>> y = 5.5 * 2
>>> y
11.0
>>> x = 12.0 / 3
>>> x
4.0
Dans le contexte de l’arithmétique, les affections peuvent prendre deux formes. Ces deux
formes ont un sens différent. Le listing suivant présente deux affectations qui pourraient être
comprises de manière identique. Toutefois, la première forme (ligne 2) a pour conséquence de
créer une nouvelle instance d’entier pour contenir l’ajout de 2 à la valeur de x. La seconde
forme (ligne 3) ajoute 2 à la valeur de x sans créer de nouvelle instance. La manière d’écrire
une opération a donc un impact sur son évaluation.
>>> x = 4
>>> x = x + 2
>>> x += 2
Les chaînes de caractères se définissent de plusieurs manières en Python. Il est possible d’uti-
liser indifféremment des guillemets simples ou des guillemets doubles. Le choix est souvent
imposé par le contenu de la chaîne : une chaîne contenant des guillemets simples sera décla-
rée avec des guillemets doubles et réciproquement. Pour les autres cas, c’est indifférent. Enfin,
comme tout est objet en Python une chaîne est donc un objet. Le listing suivant déclarer deux
chaînes référencées par x et y. Enfin, la chaîne référencée par z est une chaîne de caractères
multi-lignes (utilisation de trois quotes / guillemets simples ou doubles).
>>> x = ’hello ’
>>> y = "world!"
>>> z = ’’’hello
... world’’’
2.5 Concaténation
La concaténation de ces chaînes de caractères peut prendre deux formes. Dans les deux cas,
l’opérateur + est utilisé pour exprimer la concaténation. La forme de la ligne 4 est un raccourci
d’écriture.
>>> z = x + y
>>> z
’hello world!’
>>> x += y
>>> x
’hello world!’
2.6 Affichage
2.7 Manipulations
Python offre une méthode simple pour accéder aux caractères contenus dans une chaîne : une
chaîne est manipulée comme une séquence indexée de caractères. Ainsi, chaque caractère est
accessible directement par son index (le premier étant indexé 0) en utilisation des crochets.
En plus de cet accès unitaire aux caractères, il est possible d’accéder à des sous-chaînes en
précisant la tranche souhaitée l’index de début (qui est inclus) étant séparé de l’index de fin
(qui est exclu) par le caractère :. Dans le cas des sous-chaînes, la valeur fournie est une copie
et non un accès à une partie de la chaîne d’origine.
Le listing suivant donne quelques exemples d’accès à un caractère (ligne 2), ou à une sous-
chaîne pour le reste. La colonne de gauche présente des accès à partir du début de la chaînes
(les index sont positifs). La ligne 6 signifie que l’on souhaite le contenu de x du quatrième
caractère à la fin. Enfin, la dernière ligne réalise une copie de la chaîne x.
Enfin, un index négatifs précise que le calcul s’effectue depuis la fin de la chaîne.
>>> x[-3:]
’ld!’
>>> x[1:-1]
’ello world’
2.8 Listes
Les listes Python sont des ensemble ordonnés et dynamique d’éléments. Ces ensemble peuvent
contenir des éléments de différents types, leur seul point commun est que ce sont des objets. Et
comme tout est objet, les listes sont elles mêmes des objets (instances de la classe list).
L’exemple suivant crée tout d’abord deux listes vides avec les deux manières possibles.
>>> mylist = []
>>> mylist2 = list()
Ensuite, après avoir défini x, une liste contenant la chaîne ‘bar’, l’entier 12345 et l’objet réfé-
rencé par la variable x est créée.
>>> x = True
>>> foo = [’bar’, 12345, x]
>>> foo
[’bar’, 12345, True]
Les listes et les chaînes ont pour point commun le fait d’être des ensembles ordonnés. L’accès
à un élément d’une liste se fait donc aussi par indexation (ligne 1). Il est possible de prendre
une partie de liste (ligne 3) et d’obtenir une copie de liste (ligne 5). Une copie de liste crée une
nouvelle liste, mais partage les éléments contenus dans la liste d’origine.
Cette dernière manipulation est très importante lors de parcours de listes (voir section [sub-
Chap1LoopPbl]) pour ne pas modifier la liste qui est parcourue et se retrouver dans une boucle
sans fin.
>>> foo[2]
True
>>> foo[1:]
[12345, True]
>>> bar = foo[:]
Enfin, le contenu de la liste peut être changé par simple affection en utilisant l’index cible.
Comme la liste référencée par bar est une copie de la liste référencée par foo, le dernier
élément de bar n’est pas modifié par l’affectation du dernier élément de foo.
>>> foo[2] = 1
>>> foo
[’bar’, 12345, 1]
>>> bar[-1]
True
L’ajout d’éléments dans une liste se fait à l’aide des méthodes append pour un ajout en fin de
liste, et insert, pour un ajout à un index donné. Enfin, la méthode extend ajoute le contenu
d’une liste passé en paramètre à la fin de la liste.
>>> foo.append(’new’)
>>> foo
[’bar’, 12345, 1, ’new’]
>>> foo.insert(2, ’new’)
>>> foo
[’bar’, 12345, ’new’, 1, ’new’]
>>> foo.extend([67, 89])
>>> foo
[’bar’, 12345, ’new’, 1, ’new’, 67, 89]
La méthode index permet de connaître l’index de la première occurrence d’un élément dans
une liste. Dans le cas où l’élément fournit en paramètre n’est pas présent, la méthode lève
l’exception ValueError. L’utilisation de la construction in retourne quant à elle True si
l’élément est présent dans la liste et False sinon.
>>> foo.index(’new’)
2
>>> foo.index(34)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: list.index(x): x not in list
>>> 34 in foo
False
Les listes peuvent être fusionnées par concaténation. Ces concaténations peuvent aussi bien se
faire par copie que par ajout. La ligne 1 représente une concaténation par copie des deux listes
existantes. La ligne 4 présente une concaténation par ajout d’éléments à une liste existante.
Enfin, la ligne 7 présente une manière simple de créer une liste par répétition d’un motif qui
doit être lui même une liste.
Attention Dans le cas d’une création de liste par répétition, les éléments ne sont pas dupliqués,
mais ce sont leurs références qui sont répétées (et donc présentes plusieurs fois dans la liste
finale). Utiliser l’opérateur * pour la construction de matrice est donc une mauvaise idée car
toutes les lignes seraient alors la même liste référencée plusieurs fois.
De manière symétrique à l’ajout d’éléments dans une liste, il est possible d’en supprimer. La
méthode remove permet de supprimer la première occurrence d’un élément d’une liste en le
désignant.
>>> foo.remove(’new’)
>>> foo
[’bar’, 12345, 1, ’new’, 67, 89]
Si l’élément fourni en paramètre n’existe pas dans la liste, l’exception ValueError est levée.
>>> foo.remove(34)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: list.remove(x): x not in list
L’opérateur del (delete) permet de détruire une référence à un objet Python, ou à une partie
d’une liste 4 .
D’autres formes plus avancées de manipulation de listes sont présentées dans la section
[sub :chap1 :listes].
Les listes et les chaînes de caractères sont similaire dans leur structure et dans leur manipula-
tion. Certaines méthodes disponibles sur les chaînes manipulent les deux structures de données.
La méthode join disponible sur les chaîne permet de construire une chaîne de caractère depuis
une liste de chaînes. La chaîne sur laquelle est invoquée la méthode join est alors utilisé
comme séparateur des différents éléments de la liste.
4. Lorsque la dernière référence à un objet est détruite alors l’objet et lui même effectivement détruit.
De manière symétrique, la méthode split, disponible sur les chaînes, permet de décomposer
une chaîne de caractères en une liste de sous chaînes. Ce découpage se fait par rapport à un ou
plusieurs caractères. Dans le cas où un entier est passé comme second argument, il précise le
nombre maximum de découpage.
2.10 Tuples
Les tuples sont des ensemble ordonnés et immuables d’éléments. Comme les listes, les
tuples peuvent contenir des données de différents types. La première ligne présente une décla-
ration classique (avec des parenthèses) alors que la secone ligne présente la notation abrégée.
La virgule est importante pour préciser que l’on parle d’un tuple à un élément et non de la
valeur 12. Cette remarque serait valable dans la cas d’une déclaration parenthésée d’un tuple à
un élément.
Comme les listes, les tuples sont accessibles par indexation, et la construction in permet de
tester la présence d’un élément dans le tuple. Cependant, une fois créé le contenu d’un tuple ne
peut être modifié.
>>> foo[1]
12345
>>> foo[:-1]
(’bar’, 12345)
>>> ’bar’ in foo
True
>>> foo[1] = 12
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: ’tuple’ object does not support item assignment
2.11 Dictionnaires
Les dictionnaires, parfois appelés tableaux associatifs, sont des ensembles non ordonnés d’élé-
ments indexés par des clés. Une clé doit obligatoirement être immuable (chaîne, entier ou
tuple). D’autre part, une clé est toujours unique.
Un dictionnaire est déclaré par deux accolades ou en utilisant le type dict. Il peut être créé
vide ou avec des valeurs initiales.
>>> dict1 = {}
>>> dict2 = {’foo’: 456, 123: ’bar’}
L’ajout et l’accès aux éléments d’un dictionnaire se fait par indexation sur la clé associé à
l’élément.
>>> dict1[123]
’bar’
>>> dict1
{123: ’bar’, ’foo’: 456}
>>> dict4
{’foo’: 456, ’bar’: 123}
L’utilisation de la classe dict pour créer un dictionnaire non vide, ne peut se faire qu’avec des
chaines (utilisation d’arguments nommés).
>>> dict4
{’foo’: 456, ’bar’: 123}
Les dictionnaires offrent des méthodes pour manipuler les clés. La méthode keys retourne une
liste de toutes les clés du dictionnaire et la méthode has_key retourne True si la clé donné en
paramètre est présente dans le dictionnaire et False dans le cas contraire. Il est aussi possible
d’utiliser l’opérateur in
>>> dict1.keys()
[123, ’foo’]
>>> dict1.has_key(’bar’)
False
La méthode values donne accès à une liste des valeurs contenues dans le dictionnaire et la
méthode items donne une liste de tuples, contenant chacun une paire clé, valeur.
>>> dict1.values()
[’bar’, 456]
>>> dict1.items()
[(123, ’bar’), (’foo’, 456)]
La modification d’une valeur associée à une clé se fait simplement en affectant de nouveau la
valeur indexée dans le dictionnaire par la clé en question. Enfin, l’opérateur del permet de
supprimer une association du dictionnaire.
A l’aides des méthodes de manipulation des clé et des valeurs, il est possible de parcourir un
dictionnaire de plusieurs manières. Les quelques lignes suivantes donnent deux exemples de
parcours.
La structuration d’un programme Python est définie par son indentation. Le début d’un bloc
est défini par un ‘:‘, la première ligne pouvant être considérée comme un en-tête (test, boucle,
définition, etc.). Le corps du bloc est alors indenté de manière plus importante (mais régulière)
que l’en-tête. Enfin, la fin du bloc est délimité par le retour à l’indentation de l’en-tête. La
convention en Python est d’utiliser quatre espaces pour chaque niveau d’indentation. Les bloc
peuvent être imbriqués.
<en-tete>:
<instructions>
Dans le cas de bloc de taille réduite, par exemple une seule instruction, un bloc peut être défini
sur une seule ligne. Le caractère : sert toujours à délimiter l’en-tête du corps. Cependant, cette
utilisation n’est pas vraiment bénéfique quant à la lisibilité du code, si ce n’est pour faire tenir
du code sur un transparent. Elle est donc à éviter, l’époque où les caractères étaient comptés
dans un fichier est bien révolue.
<en-tete>: <instruction>
Cette structuration est utilisée aussi bien pour définir des boucles, des tests, des fonctions, des
classes ou encore des méthodes.
3.2 Tests
Conditions booléennes En Python, tout ce qui n’est pas faux est vrai. Les conditions boo-
léennes fausses se résument au données «vide» en plus du faux :
Expression booléennes Python propose les opérateurs de comparaison et les opérateurs boo-
léen suivants :
>>> l1 = [1, 2, 3]
>>> l2 = [1, 2, 3]
>>> l1 == l2
True
>>> l1 is l2
False
>>> l3 = l1
>>> l3 is l1
True
Python offre une seule construction pour réaliser des tests : le if then else. Une particu-
larité de cette mise en oeuvre est la possibilité d’enchaîner les tests avec la construction elif.
Le else et le elif sont bien sûr optionnels. Enfin, a clause elif peut être utilisée plusieurs
fois dans un même bloc.
if x == ’hello’:
print ’hello too!’
elif x == ’bonjour’:
print ’bonjour aussi!’
else:
print ’moi pas comprendre’
Depuis la version 2.5, il est aussi possible d’utiliser une expression conditionnelle (par opposi-
tion à la construction if du paragraphe précédent)
L’absence du switch se justifie par le fait que Python est un langage objet. Dans cette ap-
proche, l’utilisation du polymorphisme replace naturellement le switch. Lorsque l’on ne dé-
veloppe pas avec une approche objet, le switch se remplace simplement par l’utilisation d’un
dictionnaire. Ce dictionnaire contient, par exemple, des fonctions associées à des clés (pas né-
cessairement des chaînes) pour choisir la bonne fonction en fonction du contexte
traitements = {
’en’: traiter_anglais,
’de’: traiter_allemand,
’fr’: traiter_francais,
# etc.
}
Les comparaisons entre chaînes de caractères se fait selon l’ordre lexicographique. Les compa-
raisons entre listes et tuples se fait éléments par éléments de la gauche vers la droite.
Deux types de boucles sont disponibles : les boucles énumérées (for) et les boucles basées sur
un test de fin (while). Ces deux constructions suivent le même schéma : un en-tête qui décrit
l’évolution de la boucle, un ensemble d’instructions qui sont évaluées à chaque tour de boucle
et une partie optionnel qui est évaluée en sortie de boucle (introduite par le mot-clé else).
Enfin, comme en C les boucles peuvent contenir les branchements continue pour passer à
l’itération suivante et break pour sortir de la boucle (dans ce cas la clause else n’est pas
évaluée).
Boucles énumérées
Une boucle for définit une variable qui prend successivement toutes les valeurs de la séquence
(liste ou tuple) parcourue (ligne 1). La clause else est évalué lorsque la séquence est épuisée
et s’il n’y a pas eu de sortie de boucle avec un break (ligne 3).
La fonction range produit une liste de tous les entiers entre une borne inférieur et une borne
supérieur. Cette construction est utile lorsqu’une boucle for repose sur l’utilisation d’une sé-
quence d’entiers. La fonction range est utilisable de trois manières :
– un seul paramètre spécifiant le nombre d’éléments (ligne 1),
– deux paramètres spécifiant la borne inférieure (inclue) et supérieure (exclue) (ligne 3),
– trois paramètres spécifiant les bornes et le saut (incrément entre deux éléments de la sé-
quence) (ligne 5).
>>> range(6)
[0, 1, 2, 3, 4, 5]
>>> range(3, 7)
[3, 4, 5, 6]
>>> range(0, 10, 3)
[0, 3, 6, 9]
Boucle parcourant une liste de valeurs pour accéder aux éléments d’une liste.
S’il est nécessaire de parcourir une séquence tout en connaissant l’index de l’élément courant,
la fonction enumerate est une solution plus «pythonesque».
Enfin, une chaîne étant une séquence de lettre, elle peut être parcourue comme une séquence.
Une boucle while définit une condition booléenne avec les même règles que pour les tests
(ligne 1). Tant que cette condition est respectée, les instruction du bloc associé au while sont
évaluées. La clause else est évalué lorsque la condition est fausse et qu’il n’y a pas eu de
sortie de boucle avec un break (ligne 3).
while <condition>:
<instructions>
else:
<instructions, condition fausse>
La boucle suivante représente la boucle minimale définissable avec un while. Certes, elle est
stupide mais elle permet d’illustrer l’utilisation du Ctrl-C pour interrompre un traitement en
cours d’exécution.
>>> while 1:
... pass
...
KeyboardInterrupt
3.4 Fonctions
Il n’y a que des fonctions en Python (et non une distinction entre fonction et procédures comme
dans certains langages). Une fonction est définie avec le mot clé def. Une fonction retourne
toujours une valeur. Si une fonction ne contient pas de clause return, la valeur None est
alors retournée.
>>> def fib(n): # suite de fibonacci jusque n
... a, b = 0, 1
... while b < n:
... print b,
... a, b = b, a + b
...
>>> fib(100)
1 1 2 3 5 8 13 21 34 55 89
Les paramètres d’une fonction peuvent être définis avec une valeur par défaut. A l’utilisation
de la fonction, ces paramètres sont alors optionnels. Dans le cas d’une fonction avec des para-
mètres optionnels, l’utilisation des paramètres doit être ordonnée, les paramètres sont affectés
dans l’ordre de leur définition, ou nommée.
Python permet de définir des fonctions anonymes, en utilisant la forme lambda 5 . Une telle
fonction est réduite à une simple expression. Cette construction est utile pour passer des fonc-
tions en paramètre d’autre fonctions pour les configurer ou dans le cas des list mappings (voir
section [subChap1List]). Nous verrons un peu plus tard que ce n’est pas la seule possibilité.
>>> compare(1, 2)
True
>>> compare(1, 2, func=(lambda x,y: x > y))
False
3.5 Documenter
La documentation fait partie intégrante du code. En plus de la possibilité de mettre des com-
mentaires avec l’utilisation de #, Python offre la possibilité d’écrire du code auto-documenté.
Ceci est valable pour les fonctions, les classes et les modules. Une telle documentation d’un
élément est accessible vie l’attribut __doc__ de ces différents éléments. Cette technique est
utilisable automatiquement par les environnements intégrés de développement ou par l’utilisa-
tion de la fonction help en mode interactif.
5. Ici encore, cela sent la programmation fonctionnelle type Lisp à plein nez...
Introduction à Python
Affichage graphique
on
1. matplotlib.pyplot, le module qu’il nous faut
Commençons par le début, présentons matplotlib. Il s’agit sûrement de l’une des bibliothèques
python les plus utilisées pour représenter des graphiques en 2D. Elle permet de produire une
grande variété de graphiques et ils sont de grande qualité.
th
Le module pyplot de matplotlib est l’un de ses principaux modules. Il regroupe un grand
nombre de fonctions qui servent à créer des graphiques et les personnaliser (travailler sur les
axes, le type de graphique, sa forme et même rajouter du texte). Avec lui, nous avons déjà de
quoi faire de belles choses.
Py
i
Le fonctionnement de matplotlib est très semblable à celui de matlab. Le fonctionne-
ment, et même les noms des fonctions par exemple, sont quasiment toujours les mêmes.
à
Il est maintenant temps de l’installer.
Utiliser le gestionnaire de paquets est la méthode la plus simple mais nous pouvons également
utiliser le programme pip (qui est souvent installé par défaut) en entrant cette commande dans
un terminal. Nous commençons par le mettre à jour avec la première ligne, avant d’installer
matplotlib avec la seconde.
tro
Sous Windows, nous pouvons également utiliser pip pour installer matplotlib. Il nous suffit
donc d’ouvrir un terminal et d’entrer ces deux commandes. La première commande permet de
mettre à jour pip et la seconde installe matplotlib.
Tout d’abord, importons le module pyplot. La plupart des gens ont l’habitude de l’importer
en tant que plt et nous ne dérogerons pas à la règle. On place donc cette ligne au début de
notre fichier.
La première commande que nous allons voir dans ce module s’appelle show. Elle permet tout
simplement d’afficher un graphique dans une fenêtre. Par défaut, celui-ci est vide. Nous devrons
utiliser d’autres commandes pour définir ce que nous voulons afficher.
La seconde commande, close sert tout simplement à fermer la fenêtre qui s’est ouverte avec show.
Lorsque nous appuyons sur la croix de notre fenêtre, celle-ci se ferme également. Néanmoins, il
vaut mieux toujours utiliser close.
Finalement, voici notre premier code.
plt.show()
plt.close()
?
Mais, il fait rien le code. Qu’est-ce qui se passe?
En fait, la commande show sert bien à ouvrir la fenêtre, à «show» donc montrer ce que l’on
a fait précédemment. Mais nous n’avons rien fait. Nous devons alors introduire une troisième
commande, la commande plot. Finalement, voici notre vrai premier code.
import matplotlib.pyplot as plt
plt.plot()
plt.show()
plt.close()
Voilà, notre fenêtre s’ouvre bien devant nos yeux émerveillés. Nous pouvons regarder les options
offertes par la fenêtre dans le menu horizontal (zoom, déplacement, enregistrement en tant
qu’image…).
!
La fonction show est bloquante. Tant que la fenêtre n’a pas été fermée, le reste du code
ne s’exécute pas.
Pour tracer des lignes, nous devons utiliser la commande plot du module pyplot. Elle peut
ne prendre aucun argument comme nous venons de le voir, mais c’est bien avec des arguments
qu’elle est utile. En effet, si nous lui passons une liste [a, b, c] en argument, elle reliera le
points A(0, a) au point B(1, b) et ce point B au point C(2, c). En fait, nous fournissons les
ordonnées dans une liste, et les abscisses, elles, sont automatiquement générées et vont de 0 à
len(liste) - 1. Ainsi, le code suivant…
plt.plot([0, 1, 2])
plt.show()
plt.close()
… Permet d’obtenir la droite passant par les points A(0, 0), B(1, 1) et C(2, 2).
Au contraire, le code…
import matplotlib.pyplot as plt
plt.plot([1, 0, 2])
plt.show()
plt.close()
… Permet d’obtenir la droite passant par les points A(0, 1), B(1, 0) et C(2, 2).
Notons l’existence de la fonction savefig qui permet de sauvegarder le graphique dans un fichier.
Elle prend tout simplement en paramètre le chemin (relatif ou absolu) où il faut enregistrer
le fichier. Plusieurs formats sont supportés, notamment le PDF et le PNG. Pour enregistrer le
graphique, nous pouvons alors écrire ce code.
plt.plot([1, 0, 2])
plt.savefig("graphique.png")
Cependant, nous pouvons aussi passer deux listes en arguments à plot. La première liste
correspondra à la liste des abscisses des points que nous voulons relier et la seconde à la liste de
leurs ordonnées. Ainsi, notre code précédent pourrait être le suivant.
x = [0, 1, 2]
y = [1, 0, 2]
plt.plot(x, y)
plt.show()
plt.close()
Ceci nous permet alors de revenir au point de départ et de relier le dernier point au premier.
Grâce à cela, nous pouvons dessiner des figures géométriques très facilement.
Essayons donc de dessiner le triangle ABC avec A(0, 0), B(1, 1) et C(-1, 1).
x = [0, 1, -1]
y = [0, 1, 1]
plt.plot(x, y)
plt.show()
plt.close()
Pas très fermé comme triangle, hein! En fait, c’est tout bête. Chaque point est relié au point le
précédent; donc, avec ce que nous avons écrit, nous ne relions pas le dernier point au premier. Il
nous faut donc faire comme ça.
x = [0, 1, -1, 0]
y = [0, 1, 1, 0]
plt.plot(x, y)
plt.show()
plt.close()
Nous pouvons également passer en paramètre à plot plusieurs listes pour avoir plusieurs tracés.
Par exemple avec ce code…
import matplotlib.pyplot as plt
x = [0, 1, 0]
y = [0, 1, 2]
x1 = [0, 2, 0]
y1 = [2, 1, 0]
x2 = [0, 1, 2]
y2 = [0, 1, 2]
… Nous obtenons trois figures en une. Nous aurions également pu obtenir ce résultat en utilisant
trois fois la commande plot avant d’utiliser la commande show.
x = [0, 1, 0]
y = [0, 1, 2]
x1 = [0, 2, 0]
y1 = [2, 1, 0]
x2 = [0, 1, 2]
y2 = [0, 1, 2]
plt.plot(x, y)
plt.plot(x1, y1)
plt.plot(x2, y2)
plt.show()
plt.close()
Amusons-nous un moment, dessinons des carrés, des losanges, des triangles… Et habituons-nous
aux commandes.
4. Paramètres supplémentaires
C’est bien beau et tout ce qu’on a fait, mais nous avons parlé de personnalisation des graphiques.
Il serait temps d’en parler. Et oui, c’est l’heure de voir quelques moyens de personnalisation.
Tout d’abord, pour rajouter un titre au graphique, il suffit d’utiliser la commande title en lui
envoyant comme paramètre une chaîne de caractères.
Ensuite, la commande plot possède plusieurs autres paramètres. Voyons quelques-uns d’entre
eux rapidement.
Dans le même genre que le titre, le paramètre label permet de légender un graphique, c’est-
à-dire d’attribuer un nom à une courbe. Il suffit alors de choisir d’afficher la légende avec la
commande legend qui est à placer juste avant show. Essayons-la et observons le rendu. La
légende est affichée en haut à droite.
i
Notons qu’il est possible d’utiliser LaTeX pour écrire des mathématiques dans nos
textes avec matplotlib. Par exemple, nous pouvons avoir le titre title = '$\alpha =
f(\beta)$'I. (Pour en savoir plus sur l’utilisation des mathématiques avec LaTeX, nous
pouvons lire ce tutoriel ).
4.1. color
Le paramètre color permet de changer la couleur du tracé. Cette couleur peut être donnée
sous plusieurs formes.
— Sous forme de chaîne de caractères représentant les noms (ou abréviations) pour les
couleurs primaires, le noir et le blanc: b ou blue, g ou green, r ou red, c ou cyan, m
ou magenta, y ou yellow, k ou black, w ou white. C’est quand même assez explicite,
il suffit d’écrire les noms en anglais.
— Sous la forme d’un tuple correspondant aux valeurs RGB de la couleur. Cependant, ce
tuple doit contenir des valeurs entre 0 et 1 (il suffit alors de diviser les valeurs RGB par
255.0). Ainsi, ce sera color = (255 / 255.0, 0, 0) pour obtenir du rouge. Notons
que nous pouvons ajouter une valeur (toujours entre 0 et 1) à ce tuple pour représenter
la transparence alpha.
— Sous la forme de chaîne de caractères représentant la couleur en notation hexadécimale.
On aura donc color = '#00FF00' pour obtenir du vert.
— Et les adeptes des nuances de gris pourront donner en paramètre une chaîne de caractères
correspondant à l’intensité en gris. Par exemple color = '0.8' permet d’obtenir un gris
pâle.
Nous avons donc plusieurs méthodes juste pour choisir une couleur.
4.2. Le style de ligne
Nous pouvons également changer le style des lignes en passant à la commande plot une chaîne
de caractères. Les caractères acceptés et leur signification sont disponibles sur la documentation
de la commande plot. Tous ces styles ne relient pas les points entre eux, certains ne font
qu’afficher le signe à l’endroit où se situe le point. Présentons quelques caractères:
— - est le style par défaut, il correspond à une ligne pleine;
— -- correspond à une ligne en pointillés;
— : correspond à une ligne formée de points;
— -. correspond à une ligne formée d’une suite de points et de tirets.
Ces caractères correspondent au paramètre linestyle. Nous pouvons aussi ajouter des mar-
queurs avec le paramètre marker qui rajoute alors un marqueur pour chaque point de votre
graphique. Ce paramètre est aussi une chaîne de caractères. Voici quelques marqueurs: *, +,
o.
!
Les chaînes de caractères représentant les marqueurs fonctionnent aussi avec le paramètre
linestyle. Le contraire n’est pas vrai.
Nous pouvons également changer l’épaisseur du trait (ou des points) avec le paramètre lw
(linewidth).
4.3. La grille
Nous pouvons ajouter une grille avec la fonction grid qui affiche un quadrillage en pointillés.
Nous pouvons bien sûr changer le style de ce quadrillage.
— Le paramètre axis nous permet de choisir quels axes doivent être quadrillés. Il peut
prendre les valeurs both (les deux), x ou y.
— Le paramètre color nous permet de choisir la couleur de l’axe. Il fonctionne de la même
manière que le paramètre color de la fonction plot.
— Le paramètre linewidth permet de choisir l’épaisseur des traits.
— Le paramètre linestyle permet de choisir le style de quadrillage. Il peut prendre comme
valeur (tout comme plot) -, --, :, -..
On a maintenant de quoi faire plusieurs styles de grilles.
Nous pouvons effectuer plusieurs opérations sur les axes. Tout d’abord les légender:
— xlabel permet de donner un nom à l’axe des abscisses;
— ylabel permet de donner un nom à l’axe des ordonnées.
Ces deux commandes prennent en paramètres le nom que l’on veut donner à l’axe.
On peut également «cadrer» les axes avec la commande axis qu’on utilise ainsi: plt.axis([xmin,
xmax, ymin, ymax]).
4.5. Une question d’échelle
Lorsque nous dessinerons des graphiques, nous aurons affaire à un problème particulier: la
déformation.
Pour l’introduire, essayons de dessiner un carré centré autour de l’origine du repère. Traçons le
carré ABCD avec A(1, 0), B(0, 1), C(-1, 0) et D(0, -1). Le code logique pour tracer ce carré est
le suivant.
x = [1, 0, -1, 0, 1]
y = [0, 1, 0, -1, 0]
plt.plot(x, y)
plt.show()
plt.close()
Pourtant, en exécutant ce code, on se rend compte que… Notre carré n’est pas carré.
Cela est dû au fait que le repère choisi n’est pas orthonormal1 . Pour rendre le repère orthonormal,
nous pouvons utiliser la fonction axis en lui passant comme paramètre la chaîne de caractère
'equal'. Finalement, voici le code obtenu.
x = [1, 0, -1, 0, 1]
y = [0, 1, 0, -1, 0]
plt.plot(x, y)
plt.axis('equal')
plt.show()
plt.close()
Voilà. C’est la fin de cette partie. Il nous faut maintenant pratiquer tout ce que nous avons vu,
nous en aurons besoin pour la partie suivante.
Voici un code qui utilise ce qu’on a vu pour afficher un beau sapin vert.
x = [0.25, 0.25, 1.25, 0.5, 1, 0.25, 0.6, 0, -0.6, -0.25, -1, -0.5,
-1.25, -0.25, -0.25, 0.25]
y = [0, 0.5, 0.5, 1, 1, 1.5, 1.5, 2, 1.5 , 1.5, 1, 1, 0.5, 0.5, 0,
0]
plt.plot(x, y, '-.', color = "green", lw = 2)
plt.title("Mon beau sapin")
plt.axis('equal')
plt.xlabel("C'est Noel")
plt.ylabel("Vive le vent")
plt.show()
plt.close()
1. La méthode secrète
Vous vous demandez sûrement comment nous allons faire pour tracer des fonctions. L’astuce est
très simple. Nous allons relier des points dont nous savons qu’ils appartiennent à la courbe. Par
exemple, pour tracer la fonction cosinus sur l’intervalle [0, 2], nous allons relier les point A(0,
cos(0)) et B(2, cos(2)).
1. Un repère orthonormal (ou orthonormé) est un repère où tous les axes sont perpendiculaires entre eux et
où l’unité est la même (il y a toujours la même distance entre le 0 et le 1).
?
Mais c’est nul ta méthode. On a essayé et on obtient juste une droite. Comment tu fais,
toi, pour avoir une courbe?
Oui, nous obtenons juste une droite, et c’est bien normal, nous avons juste relié deux points.
Pour obtenir la courbe (en tout cas l’approcher), nous allons relier des points très proche qui
appartiennent à la courbe. Par exemple, nous pouvons couper l’intervalle en 100 et donc relier
101 points plutôt que deux points. On appelle cela, subdiviser l’intervalle.
Notre subdivision sera à pas constant (le pas δ est l’espace entre deux points de la subdivision).
Le pas est donc égal à la longueur de l’intervalle divisé par le nombre n de points de la
subdivision.
$$ \delta = \frac{x{max} - x{min}}{n}. $$
Ainsi pour dessiner la fonction cosinus:
— on coupe l’intervalle en n tranches (on choisit par exemple n = 100);
— le pas est donc δ = 100
2π
;
— on relie les points de la subdivision.
i
Nous pouvons remarquer qu’en divisant notre intervalle en 100 tranches, nous obtenons
une subdivision de 101 points (les deux bornes de l’intervalle sont dans la subdivision).
x = []
y = [] # On a créé deux listes vides pour contenir les abscisses et
les ordonnées
pas = 2 * pi / 100
abscisse = 0 # L'abscisse de départ est 0
for k in range(0, 101): # On veut les points de 0 à 100
x.append(abscisse)
y.append(cos(abscisse)) # On rajoute l'abscisse et son image
par la fonction cos aux listes
abscisse += pas # on augmente abscisse de pas pour passer au
point suivant
plt.plot(x, y)
plt.show()
plt.close()
Cependant, la méthode précédente peut présenter quelques problèmes dans des cas particuliers.
Pour bien le voir, essayons de dessiner la fonction inverse (x 7→ x1 ) sur l’intervalle [-1, 1].
a = -1
b = 1
x = []
y = []
pas = (b - a) / 200
abscisse = a
for k in range(0, 201):
x.append(abscisse)
y.append(1 / abscisse)
abscisse += pas
plt.axis([-1, 1, -10, 10])
plt.plot(x, y)
plt.show()
plt.close()
On obtient ce graphe.
/opt/zds/data/contents-public/introduction-aux
13
3. Tracer des fonctions
?
C’est quoi cette droite verticale au milieu de l’écran?
La fonction inverse n’est pas définie en 0. Normalement, nous aurions dû obtenir une erreur lorsque
nous demandions 1 / abscisse et qu’abscisse valait 0. Cependant, les erreurs d’approximation
de python font que l’on ne passe pas par 0 mais par un point proche.
Néanmoins, il ne reste pas moins vrai que la fonction inverse est fortement divergente au
voisinage de 0. Elle tend vers −∞ lorsque x tend vers 0 par valeur négative mais tend au
contraire vers +∞ lorsque x tend vers 0 en étant positif.
La droite que nous observons est donc une conséquence de cela. On relie un point qui a une
ordonnée très négative (le premier point avant 0) à un autre point qui a une ordonnée très
positive (le premier point après 0).
Pour éviter cela, on est obligé de dessiner la fonction en deux fois. Une fois avant 0 et une fois
après. Finalement, notre code est le suivant.
/opt/zds/data/contents-public/introduction-aux
14
4. Notre fonction
Nous pouvons bien entendu le modifier pour par exemple avoir les deux bouts de courbes de la
même couleur, ou changer les axes.
Pour ceux qui connaissent le module numpy2 , sachez que plot accepte aussi ses modules, ce qui
permet de faire ce que nous venons de faire plus simplement.
Pour la fonction cosinus, on peut alors écrire ce code.
x = np.arange(0.01, 1, 0.01)
plt.plot(x, inverse(x), -x, inverse(-x))
plt.show()
# Tracé du résultat en 3D
fig = plt.figure()
ax = fig.gca(projection='3d') # Affichage en 3D
ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0) # Tracé d'une surface
plt.title("Tracé d'une surface")
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.tight_layout()
plt.show()
Comme pour tous les modules Python une première méthode consiste à taper
Comme il devient très vite lassant de taper numpy.X il est courant d’importer Numpy sous
une abréviation, comme par exemple
Ainsi toute commande spécificique de Numpy devient np.X. Il n’est pas recommandé de
charger entièrement Numpy par la commande
Numpy ajoute le type array qui est similaire à une liste (list) avec la condition supplémen-
taire que tous les éléments sont du même type. Nous concernant ce sera donc un tableau
d’entiers, de flottants voire de booléens.
Une première méthode consiste à convertir une liste en un tableau via la commande array.
Le deuxième argument est optionnel et spécifie le type des éléments du tableau.
>>> a = np.array([1, 4, 5, 8], float)
>>> a
array([ 1., 4., 5., 8.])
>>> type(a)
<type ’numpy.ndarray’>
>>> a=np.array([[[1,2],[1,2]],[[1,2],[1,2]]])
>>> a
array([[[1, 2],
[1, 2]],
[[1, 2],
[1, 2]]])
>>> type(a)
<type ’numpy.ndarray’>
>>> a[1,1,1]
2
Nous limiterons notre étude aux tableaux {uni,bi}-dimensionnels. En Python modifier une
donnée d’une extraction d’un tableau entraîne aussi une modification du tableau initial ! Si
nécessaire la fonction np.copy(a) ou a.copy() permet de faire une copie d’un tableau a.
>>> a = np.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> a[2:9:3] # [début:fin:pas]
array([2, 5, 8])
>>> a[2:8:3] # le dernier élément n’est pas inclus
array([2, 5])
>>> a[:5] # le dernier élément n’est pas inclus
array([0, 1, 2, 3, 4])
Dans l’instruction [début:fin:pas] deux des arguments peuvent être omis : par défaut
l’indice de début vaut 0 (le 1er élément), l’indice de fin est celui du dernier élément et le pas
vaut 1. Il faut tout de même noter une différence entre [2:9] et [2:]
>>> a[2:9]
array([2, 3, 4, 5, 6, 7, 8])
>>> a[2:]
array([2, 3, 4, 5, 6, 7, 8, 9])
Un pas négatif inversera l’ordre du tableau et les tableaux étant considérés cycliques
la commande a[-2::1] extrait l’avant dernier et dernier élément. Pour un tableau bi-
dimensionnel on peut bien sûr jouer avec les deux indices.
>>> a=np.eye(5,5)
>>> print a
[[ 1. 0. 0. 0. 0.]
[ 0. 1. 0. 0. 0.]
[ 0. 0. 1. 0. 0.]
[ 0. 0. 0. 1. 0.]
[ 0. 0. 0. 0. 1.]]
>>> a[:3,:4]=4
>>> a[::3,::4]=5
>>> a[:,3]=6
>>> print a
[[ 5. 4. 4. 6. 5.]
[ 4. 4. 4. 6. 0.]
[ 4. 4. 4. 6. 0.]
[ 5. 0. 0. 6. 5.]
[ 0. 0. 0. 6. 1.]]
Une autre méthode d’extraction consiste à écrire a[b] où b est un tableau d’entiers qui cor-
respondra aux indices à extraire et a un vecteur. Pour les matrices c’est a[b,c] et on prend
les indices de ligne dans b, les indices de colonnes dans c, successivement.
>>> a=np.array([2,4,6,8],float)
>>> b=np.array([0,0,1,3,2,1],int)
>>> a[b]
array([ 2., 2., 4., 8., 6., 4.])
>>> a=a.reshape(2,2)
>>> b=np.array([0,0,1,1,0],int)
>>> c=np.array([0,1,1,1,1],int)
>>> a[b,c]
array([ 2., 4., 8., 8., 4.])
>>>
Numpy/Scipy proposent aussi le type mat comme matrice, exclusivement un tableau bi-
dimensionnel. Comme les fonctions telles que ones, eye retournent un objet de type array
nous n’utiliserons pas dans la suite le type mat. Il permet cependant de saisir les matrices
à-la-Matlab et de faire le produit matriciel par le simple symbole *
>>> a=np.mat(’[1 2 4 ; 3 4 5.]’)
>>> b=np.mat(’[2. ; 4 ; 6]’)
>>> print a
[[ 1. 2. 4.]
[ 3. 4. 5.]]
>>> print b
[[ 2.]
[ 4.]
[ 6.]]
>>> print a*b
[[ 34.]
[ 52.]]
Tout objet array est convertible en type mat et réciproquement (sous la condition que le
tableau (array) soit {uni,bi}-dimensionnel)
2.2 Construire
Nous avons déjà vu comment accéder aux éléments d’un tableau soit par a[i,j] soit par le
slicing. Pour définir certaines matrices (au sens array {uni,bi}-dimensionnel) il est possible
d’utiliser les boucles, des matrices prédéfinies de Numpy (Scipy) et les opérations élémen-
taires (multiplication par un scalaire, produit matriciel, transposition, produit de Kronecker,
jeu avec les booléens, etc).
Numpy propose arange équivalent de range mais de type array, ce qui évite une conver-
sion.
>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.arange(2, 10, dtype=np.float)
array([ 2., 3., 4., 5., 6., 7., 8., 9.])
>>> np.arange(2, 3, 0.1)
array([ 2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9])
>>> np.arange(3)
array([0, 1, 2])
>>> np.arange(3.0)
array([ 0., 1., 2.])
Comme il y quelques subtilités dans la fonction range et arange quant au dernier élé-
ment, Pour éviter tout désagrément la fonction linspace(premier,dernier,n) renvoie
un array commençant par premier, se terminant par dernier avec n éléments régulière-
ment espacés.
>>> a=np.arange(16)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
>>> a.reshape(4,4)
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
>>> a.reshape(2,8)
array([[ 0, 1, 2, 3, 4, 5, 6, 7],
[ 8, 9, 10, 11, 12, 13, 14, 15]])
>>> b=np.ones((4,4),float)+np.eye(4,4)
>>> print b
[[ 2. 1. 1. 1.]
[ 1. 2. 1. 1.]
[ 1. 1. 2. 1.]
[ 1. 1. 1. 2.]]
>>> np.reshape(b,16)
array([ 2., 1., 1., 1., 1., 2., 1., 1., 1., 1., 2., 1., 1.,
1., 1., 2.])
>>> np.reshape(b,(2,8))
array([[ 2., 1., 1., 1., 1., 2., 1., 1.],
[ 1., 1., 2., 1., 1., 1., 1., 2.]])
>>> b.flatten()
array([ 2., 1., 1., 1., 1., 2., 1., 1., 1., 1., 2., 1., 1.,
1., 1., 2.])
Numpy permet d’assembler les vecteurs et les matrices, de les concaténer. Par défaut l’as-
semblage se fait selon la 1ère dimension (les lignes, donc assemblage vertical). L’option
axis=1 assemble “horizontalement”.
>>> a=np.arange(4).reshape(2,2)
>>> b=4+np.arange(4).reshape(2,2)
>>> b
array([[4, 5],
[6, 7]])
>>> c=(np.arange(4)+6)[newaxis,:]
>>> np.concatenate((a,b))
array([[0, 1],
[2, 3],
[4, 5],
[6, 7]])
>>> np.concatenate((a,b),axis=0)
array([[0, 1],
[2, 3],
[4, 5],
[6, 7]])
>>> np.concatenate((a,b),axis=1)
array([[0, 1, 4, 5],
[2, 3, 6, 7]])
>>> np.concatenate((c,c,np.concatenate((a,b),axis=1)))
array([[6, 7, 8, 9],
[6, 7, 8, 9],
[0, 1, 4, 5],
[2, 3, 6, 7]])
Pour concaténer un vecteur et une matrice il est nécessaire de convertir le vecteur en ma-
trice à 1 ligne (ou 1 colonne). Par rapport à Matlab/Scilab c’est bien dommage. Au pas-
sage la commande np.newaxis permet d’ajouter une dimension à un tableau (si b est un
tableau unidimensionnel b[np.newaxis,:] retournera une matrice à 1 ligne tandis que
b[:,np.newaxis] retournera une matrice à 1 colonne).
>>> c=(np.arange(4)+6)[np.newaxis,:]
>>> d=(np.arange(4)+6)
>>> np.concatenate((c,d))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: arrays must have same number of dimensions
>>> np.concatenate((c,d),axis=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: arrays must have same number of dimensions
>>> np.concatenate((c,d[np.newaxis,:]),axis=0)
array([[6, 7, 8, 9],
[6, 7, 8, 9]])
>>> np.concatenate((c.transpose(),d[:,np.newaxis]),axis=0)
array([[6],
[7],
[8],
[9],
[6],
[7],
[8],
[9]])
Le “slicing” permet d’extraire des éléments d’un vecteur ou matrice. Il existe aussi la com-
mande np.take qui extrait lignes/colonnes d’un array. L’option axis détermine une ex-
traction selon les lignes/colonnes. La syntaxe se comprend aisément.
>>> a=np.arange(16).reshape(4,4)
>>> a.take([0,3],axis=1)
array([[ 0, 3],
[ 4, 7],
[ 8, 11],
[12, 15]])
>>> a.take([0,3])
array([0, 3])
>>> a.take([0,3],axis=0)
array([[ 0, 1, 2, 3],
[12, 13, 14, 15]])
>>> a.take([0,3,2,2,2],axis=0)
array([[ 0, 1, 2, 3],
[12, 13, 14, 15],
[ 8, 9, 10, 11],
[ 8, 9, 10, 11],
[ 8, 9, 10, 11]])
Comme deux positions seulement ont été données, seules la valeurs 0 et 3 ont été rempla-
cées.
2.4 Opérations
Dans le même ordre a**3 renvoie le vecteur/matrice de même dimension dont tous les
éléments sont ceux de a élevés à la puissance 3. Devinez ce que fait 1./a ?
Pour le produit matriciel (le vrai) la commande np.dot est là
>>> a=np.arange(4).reshape(2,2)
>>> v=np.array([-3,2])
>>> np.dot(a,v)
array([2, 0])
>>> np.dot(v,a)
array([4, 3])
>>> w=np.concatenate((v,v))
>>> w
array([-3, 2, -3, 2])
>>> dot(a,w)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: objects are not aligned
>>> a=np.arange(4,dtype=float).reshape(2,2)
>>> b=np.arange(5,dtype=float)
>>> print a,b
[[ 0. 1.]
[ 2. 3.]] [ 0. 1. 2. 3. 4.]
>>> np.kron(a,b)
array([[ 0., 0., 0., 0., 0., 0., 1., 2., 3., 4.],
[ 0., 2., 4., 6., 8., 0., 3., 6., 9., 12.]])
>>> np.kron(b,a)
array([[ 0., 0., 0., 1., 0., 2., 0., 3., 0., 4.],
[ 0., 0., 2., 3., 4., 6., 6., 9., 8., 12.]])
>>> a=np.arange(5,dtype=float)
>>> b=np.ones((1,5))
>>> np.kron(a,b.transpose())
array([[ 0., 1., 2., 3., 4.],
[ 0., 1., 2., 3., 4.],
[ 0., 1., 2., 3., 4.],
[ 0., 1., 2., 3., 4.],
[ 0., 1., 2., 3., 4.]])
2.5 Booléens
>>> b=6*np.eye(4,4)-np.diag(4+np.arange(3),1)+2*np.ones((4,4))
>>> a=np.arange(16).reshape(4,4)
>>> (a<b)
array([[ True, False, False, False],
[False, True, False, False],
[False, False, False, False],
[False, False, False, False]], dtype=bool)
>>> (a==b)
array([[False, False, True, False],
[False, False, False, False],
[False, False, False, False],
[False, False, False, False]], dtype=bool)
Si les deux arguments sont de tailles différentes, Python vous insulte. Mais si a ou b est de
type float ou int, la commande a<2 renvoie le tableau de taille égale à celle de a corres-
pondant à comparaison des éléments de a avec 2.
>>> (a<2.)
array([[ True, True, False, False],
[False, False, False, False],
[False, False, False, False],
[False, False, False, False]], dtype=bool)
>>> (2<b)
array([[ True, False, False, False],
[False, True, False, False],
[False, False, True, False],
[False, False, False, True]], dtype=bool)
Encore plus formidable, si d est un tableau de booléens de taille égale à celle de a, a[d]
extrait les éléments de a qui correspondent à la valeur True dans d. La commande b[b<4]
renvoie les éléments de b strictement inférieurs à 4.
>>> b=6*np.eye(4,4)-np.diag(4+np.arange(3),1)+2*np.ones((4,4))
>>> b
array([[ 8., -2., 2., 2.],
[ 2., 8., -3., 2.],
[ 2., 2., 8., -4.],
[ 2., 2., 2., 8.]])
>>> b[b<4]
array([-2., 2., 2., 2., -3., 2., 2., 2., -4., 2., 2., 2.])
(à faire : np.all() np.any(), np.where‘)
Numpy permet de “vectoriser”, i.e. appliquer une fonction à un vecteur/matrice et éviter les
boucles. Comme nous avons choisi d’utiliser Numpy à travers import numpy as np, il faut
choisir les fonctions usuelles définies dans Numpy
>>> a = np.arange(4).reshape((2,2))
>>> a
array([[0, 1],
[2, 3]])
>>> np.amin(a) # élément minimum
0
>>> np.amin(a, axis=0) # minimum selon ligne
array([0, 1])
>>> np.amin(a, axis=1) # minimum selon colonne
array([0, 2])
>>> b=6*np.eye(4,4)-np.diag(4+np.arange(3),1)+2*np.ones((4,4))
>>> b
array([[ 8., -2., 2., 2.],
[ 2., 8., -3., 2.],
[ 2., 2., 8., -4.],
[ 2., 2., 2., 8.]])
>>> np.argmin(b)
11
>>> np.argmax(b)
0
>>> np.argmin(b,axis=1)
array([1, 2, 3, 0])
>>> np.argmin(b,axis=0)
array([1, 0, 1, 2])
Les sommes, produits de tous les éléments ou selon les lignes/colonnes se font aisément
avec np.sum et np.prod. À vous de vérifier que les résultats sont justes.
>>> b=6*np.eye(4,4)-np.diag(4+np.arange(3),1)+2*np.ones((4,4))
>>> np.sum(b)
41.0
>>> np.prod(b)
-50331648.0
>>> np.sum(b,axis=0)
array([ 14., 10., 9., 8.])
>>> np.sum(b,axis=1)
array([ 10., 9., 8., 14.])
>>> np.prod(b,axis=1)
array([ -64., -96., -128., 64.])
Pour faire les moyennes de tous les éléments ou selon les lignes/colonnes, np.mean a le
fonctionnement attendu.
Très rapidement, listons les commandes. Certaines commandes étant spécialisées (tout le
monde n’a pas besoin d’inverser les matrices) il faut ajouter linalg : np.linalg.X
◦ rang d’une matrice : np.rank(a)
>>> a=np.arange(15).reshape(3,5)
>>> a
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> np.rank(a)
2
>>> np.rank(a.transpose())
2
◦ inverse d’une matrice : np.linalg.inv(a) inverse d’une matrice carrée
>>> a=np.array([2,4,6,8],float).reshape(2,2)
>>> np.linalg.inv(a)
array([[-1. , 0.5 ],
[ 0.75, -0.25]])
Comme d’habitude avec les logiciels de calcul scientifique, il faut d’abord savoir si la ma-
trice est inversible pour l’inverser, ou encore rester critique vis à vis du résultat retourné.
L’exemple suivant est caractéristique.
>>> a=np.arange(16).reshape(4,4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
>>> np.rank(a) # la matrice n’est pas inversible
2
>>> np.linalg.inv(a)
array([[ 9.00719925e+14, -4.50359963e+14, -1.80143985e+15,
1.35107989e+15],
[ -2.40191980e+15, 2.70215978e+15, 1.80143985e+15,
-2.10167983e+15],
[ 2.10167983e+15, -4.05323966e+15, 1.80143985e+15,
1.50119988e+14],
[ -6.00479950e+14, 1.80143985e+15, -1.80143985e+15,
6.00479950e+14]])
Les valeurs très grandes laissent tout de même planer un certain soupçon.
◦ résolution d’un système linéaire : np.linalg.solve(a,b) où a est une matrice carrée et
b un vecteur ou une matrice (avec condition de compatibilité)
>>> a=np.array([2,4,6,8],float).reshape(2,2)
>>> b=np.array([1, 4],float)
>>> np.linalg.solve(a,b)
array([ 1. , -0.25])
>>> b=np.array([[1, 1],[4, -4]],float)
>>> np.linalg.solve(a,b)
array([[ 1. , -3. ],
[-0.25, 1.75]])
Il en est de même que pour l’inversion de matrice : il faut savoir rester critique.
>>> a=np.arange(16,dtype=float).reshape(4,4)
>>> b=np.arange(4,dtype=float)
>>> b[1]=5
>>> x=np.linalg.solve(a,b)
>>> np.dot(a,x)
array([ -2., 2., 6., 10.])
>>> b
array([ 0., 5., 2., 3.])
◦ calcul du déterminant : np.linalg.det(a) bien sûr !
◦ calcul des valeurs propres et vecteurs propres : np.linalg.eig(a)
>>> a=np.array([2,4,6,8],float).reshape(2,2)
>>> np.linalg.eig(a)
(array([ -0.74456265, 10.74456265]), array([[-0.82456484, -0.41597356],
[ 0.56576746, -0.90937671]]))
Le premier argument représente les valeurs propres, tandis que le deuxième retourne la
matrice des vecteurs propres en colonne (une colonne est un vecteur propre).
◦ calcul de norme vectorielle/matricielle : np.linalg.norm(x[, ord]) où ord est pour
une matrice
• Fro , norme de Frobenius
• inf : max(sum(abs(x), axis=1))
• -inf : min(sum(abs(x), axis=1))
• 1 : max(sum(abs(x), axis=0))