Antisèches Python pour Biologiste Pressé¶
Voici un looooong notebook qui peut vous servir de référence sur Python. Ce n'est en aucun cas un substitut à un cours complet, mais la vraie maîtrise des outils de programmation ne s’acquiert que par la pratique régulière. N'hésitez pas à garder ceci sous le coude quand vous essayez de résoudre les problèmes que vous rencontrez.
Les Parties 1-6 sont dérivées de https://learnxinyminutes.com/ (CC BY-SA 3.0)
Jupyter Notebook¶
Jupyter est un logiciel écrit en python permettant de travailler avec des notebook dans différents languages (dont Julia, Python et R d'où le nom du logiciel).
Vous pouvez le lancer sur les machines de la salle info en tapant jupyter notebook
dans un terminal
Pointez ensuite votre navigateur favori sur l'adresse indiquée (en général https://localhost:8888 )
Le Notebook est organisé en cellules qui peuvent contenir soit du code soit du texte formaté en Markdown et des équations formatées en $\LaTeX$.
Si vous avez chargé le notebook (fichier .ipynb) dans jupyter, n'hésitez pas à editer ce texte en double cliquant dessus.
Les notebook peuvent aussi être exportés dans divers formats dont des pages web (.html) en utilisant le menu idoine (File->Download as
)
Raccourcis clavier utiles
Ctrl-M a
etCtrl-M b
: Créer une nouvelle cellule au-dessus/au-dessous de la cellule sélectionnée.Ctrl-M c
,Ctrl-M v
,Ctrl-M x
: Copier, coller et couper (resp.) des cellules.Shift+Entrée
: Exécuter le contenu de la cellule couranteCtrl-M m
,Ctrl-M y
: Passer la cellule en mode texte ou en mode code.Shift+Tab
sur le nom d'une fonction : Afficher l'aide, C'est à dire sa docstring.
Types de données primaires et opérateurs¶
Entiers, flottants¶
# Le croisillon (#) indique que le reste de la ligne est un commentaire.
# On a des nombres entiers
3 # => 3
type(3) #=> int
# Les calculs sont ce à quoi on s'attend (+, -, *)
1 + 1 # => 2
8 - 1 # => 7
10 * 2 # => 20
# On peut forcer la priorité de calcul avec des parenthèses
(1 + 3) * 2 # => 8
# La division retourne un nombre à virgule flottante (float)
35 / 5 # => 7.0
type(35/5) #=> float
# Quand on utilise un float, le résultat est un float
3 * 2.0 # => 6.0
# La Division Euclidienne se fait avec une double barre oblique.
5 // 3 # => 1
type(5//3) #=> int
5.0 // 3.0 # => 1.0 # Ça marche pour les floats egalement.
-5 // 3 # => -2
-5.0 // 3.0 # => -2.0
# Modulo (reste de la division)
7 % 3 # => 1
# Exponentiation (x**y, x élevé à la puissance y)
2**4; # => 16
Booléens¶
# Les valeurs booléennes sont de type "bool"
True
False
type(True) #=> bool
# Négation avec not
not True # => False
not False # => True
# Opérateurs booléens
# On note que "and" et "or" sont sensibles à la casse
True and False #=> False
False or True #=> True
# On vérifie une égalité avec ==, cela renvoie un bool.
1 == 1 # => True
2 == 1 # => False
type(1==1) #=> bool
# On vérifie une inégalité avec !=
1 != 1 # => False
2 != 1 # => True
# Autres opérateurs de comparaison
1 < 10 # => True
1 > 10 # => False
2 <= 2 # => True
2 >= 2 # => True
# On peut enchaîner les comparaisons
1 < 2 < 3 # => True
2 < 3 < 2 # => False
# Utilisation des opérations booléennes avec des entiers :
# Tout entier non null est "True"
0 == False #=> True
2 == True #=> False
1 == True #=> True
0 and 2 #=> 0
-5 or 0 #=> -5
# (is vs. ==) is vérifie si deux variables pointent sur le même objet, mais == vérifie
# si les objets ont la même valeur.
a = [1, 2, 3, 4] # a pointe sur une nouvelle liste, [1, 2, 3, 4]
b = a # b pointe sur a
b is a # => True, a et b pointent sur le même objet
b == a # => True, les objets a et b sont égaux
b = [1, 2, 3, 4] # b pointe sur une nouvelle liste, [1, 2, 3, 4]
b is a # => False, a et b ne pointent pas sur le même objet
b == a; # => True, les objets a et b ne pointent pas sur le même objet
Chaînes de caractères¶
# Les chaînes (ou strings) sont créées avec " ou '
"Ceci est une chaine"
'Ceci est une chaine aussi.'
""" Les chaînes de caractères peuvent aussi être écrites
avec 3 guillemets doubles ("), et sont souvent
utilisées comme des commentaires.
"""
# Additionner des chaînes les concatène.
"Hello " + "world!" # => "Hello world!"
# Deux chaînes juxtaposées sont aussi concaténées.
"Hello " "world!" # => "Hello world!"
("Combiné aux parenthèses, cela permet de représenter une "
"longue chaîne (qui ne contient pas de retour à la ligne)"
"sur plusieurs ligne.")
# On peut traîter une chaîne comme une liste de caractères
"This is a string"[0] # => 'T'
# .format peut être utilisé pour formatter des chaînes, comme ceci:
"{} peuvent etre {}".format("Les chaînes", "interpolées")
# La méthode format a un mini langage complet pour formatter les chaînes de caractères
# (https://docs.python.org/3/library/string.html#formatspec)
# On peut aussi réutiliser le même argument pour gagner du temps.
"{0} be nimble, {0} be quick, {0} jump over the {1}".format("Jack", "candle stick")
# On peut aussi utiliser des mots clés pour éviter de devoir compter.
"{name} wants to eat {food}".format(name="Bob", food="lasagna")
# Python a une fonction print pour afficher du texte
print("I'm Python. Nice to meet you!")
# Par défaut, la fonction print affiche aussi une nouvelle ligne à la fin.
# Utilisez l'argument optionnel end pour changer ce caractère de fin.
print("Hello, World", end="!") # => Hello, World!
liste = [1,2,3]
str(liste)[1:-1]
# La méthode Split permet de découper une chaîne par rapport à un séparateur
"human_genome_200.txt".split("_") #=> ['human', 'genome', '200.txt']
# La méthode join permet de recoller une liste de chaîne à partir d'un séparateur
print("\n".join(['human', 'genome', '200.txt'])) #=> 'human_genome_200.txt'
Un type spécial, None¶
# None est un objet
None # => None
# N'utilisez pas "==" pour comparer des objets à None
# Utilisez plutôt "is". Cela permet de vérifier l'égalité de l'identité des objets.
"etc" is None # => False
None is None # => True
Variables¶
# Les variables en Python sont des référence nommées à des objets.
# La convention est de nommer ses variables avec des minuscules_et_underscores
some_var = 5 # Créer une variable qui pointe vers l'objet "5" de type int.
some_var
# Tenter d'accéder à une variable non définie lève une exception.
# Voir Structures de contrôle pour en apprendre plus sur le traitement des exceptions.
une_variable_inconnue # Lève une NameError
Structures de données¶
Listes (ordonné, modifiable)¶
# Les listes permettent de stocker des séquences
li = []
# On peut initialiser une liste pré-remplie
other_li = [4, 5, 6]
# On ajoute des objets à la fin d'une liste avec .append
li.append(1) # li vaut maintenant [1]
li.append(2) # li vaut maintenant [1, 2]
li.append(4) # li vaut maintenant [1, 2, 4]
li.append(3) # li vaut maintenant [1, 2, 4, 3]
# On enlève le dernier élément avec .pop
li.pop() # => 3 et li vaut maintenant [1, 2, 4]
# Et on le remet
li.append(3) # li vaut de nouveau [1, 2, 4, 3]
# Accès à un élément d'une liste :
li[0] # => 1
# Accès au dernier élément :
li[-1]; # => 3
# Accéder à un élément en dehors des limites lève une IndexError
li[4] # Lève une IndexError
# On peut accéder à un intervalle avec la syntaxe "slice"
li = [1,2,3,4]
li[1:3] # => [2, 4]
# Omettre les deux premiers éléments
li[2:] # => [4, 3]
# Prendre les trois premiers
li[:3] # => [1, 2, 4]
# Sélectionner un élément sur deux
li[::2] # =>[1, 4]
# Avoir une copie de la liste à l'envers
li[::-1] # => [3, 4, 2, 1]
# La syntaxe est: debut:fin:pas
# Le premier element est inclusif (en partant de 0)
# Le dernier element est exclusif
# Le pas est optionnel
# Faire une copie d'une profondeur de un avec les "slices"
li2 = li[:] # => li2 = [1, 2, 4, 3] mais (li2 is li) vaut False.
# Enlever des éléments arbitrairement d'une liste
del li[2] # li is now [1, 2, 3]
# On peut additionner des listes pour les concaterner
# Note: les valeurs de li et other_li ne sont pas modifiées.
li + other_li # => [1, 2, 3, 4, 5, 6]
# Vérifier la présence d'un objet dans une liste avec "in"
1 in li # => True
# Examiner la longueur avec "len()"
len(li); # => 6
# Trier les listes se fait avec la méthode sort pour un tri "sur place"
a = [24, 324, 2, 8]
a.sort() #=> a is now [2, 8, 24, 324]
# ou la fonction sorted pour un qui retourne une nouvelle liste sans changer la première
a = [24, 324, 2, 8]
b = sorted(a) #=> [2, 8, 24, 324]
a,b
# Par défaut les listes sont triées dans l'ordre croissant.
# On peut changer la fonction de comparaison utilisée pour le tri avec l'argument key.
# Ex: tri dans l'ordre décroissant.
a = [24, 324, 2, 8]
sorted(a) #=> [2, 8, 24, 324]
def ordre(x):
return -x
sorted(a, key=ordre) #=> [324, 24, 8, 2]
# Ex: tri dans l'ordre du second elément d'une paire
a = [(1,2),(3,4),(5,0)]
sorted(a) #=> [(1, 2), (3, 4), (5, 0)]
def ordre(x):
return x[1]
sorted(a, key=ordre) #=> [(5, 0), (1, 2), (3, 4)]
N-uplets ou Tuples (ordonné, immuable)¶
# Les tuples sont comme des listes mais sont immuables
tup = (1, 2, 3)
tup[0] # => 1
tup[0] = 3 # Lève une TypeError
# Note : un tuple de taille un doit avoir une virgule après le dernier élément,
# mais ce n'est pas le cas des tuples d'autres tailles, même zéro.
type((1)) # => <class 'int'>
type((1,)) # => <class 'tuple'>
type(()) # => <class 'tuple'>
# On peut utiliser la plupart des opérations des listes sur des tuples.
len(tup) # => 3
tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6)
tup[:2] # => (1, 2)
2 in tup # => True
# Vous pouvez décomposer des tuples (ou des listes) dans des variables
a, b, c = (1, 2, 3) # a vaut 1, b vaut 2 et c vaut 3
# Les tuples sont créés par défaut sans parenthèses
d, e, f = 4, 5, 6
# Voyez comme il est facile d'intervertir deux valeurs :
e, d = d, e # d vaut maintenant 5 et e vaut maintenant 4
Dictionnaires (clé/valeur, non-ordonné, modifiable)¶
# Créer un dictionnaire :
empty_dict = {}
# Un dictionnaire pré-rempli :
filled_dict = {"one": 1, "two": 2, "three": 3}
# Note : les clés des dictionnaires doivent être de types immuables.
# Elles doivent être convertibles en une valeur constante pour une recherche rapide.
# Les types immuables incluent les ints, floats, strings et tuples.
invalid_dict = {[1,2,3]: "123"} # => Lève une TypeError: unhashable type: 'list'
valid_dict = {(1,2,3):[1,2,3]} # Par contre, les valeurs peuvent être de tout type.
# On trouve une valeur avec []
filled_dict["one"] # => 1
# On obtient toutes les clés sous forme d'un itérable avec "keys()" Il faut l'entourer
# de list() pour avoir une liste Note: l'ordre n'est pas garanti.
list(filled_dict.keys()) # => ["three", "two", "one"]
# On obtient toutes les valeurs sous forme d'un itérable avec "values()".
# Là aussi, il faut utiliser list() pour avoir une liste.
# Note : l'ordre n'est toujours pas garanti.
list(filled_dict.values()) # => [3, 2, 1]
# On vérifie la présence d'une clé dans un dictionnaire avec "in"
"one" in filled_dict # => True
1 in filled_dict; # => False
# L'accès à une clé non-existente lève une KeyError
filled_dict["four"] # KeyError
# On utilise "get()" pour éviter la KeyError
filled_dict.get("one") # => 1
filled_dict.get("four") # => None
# La méthode get accepte une valeur de retour par défaut en cas de valeur non-existante.
filled_dict.get("one", 4) # => 1
filled_dict.get("four", 4) # => 4
# "setdefault()" insère une valeur dans un dictionnaire si la clé n'est pas présente.
filled_dict.setdefault("five", 5) # filled_dict["five"] devient 5
filled_dict.setdefault("five", 6) # filled_dict["five"] est toujours 5
# Ajouter à un dictionnaire
filled_dict.update({"four":4}) #=> {"one": 1, "two": 2, "three": 3, "four": 4}
#filled_dict["four"] = 4 # une autre méthode
# Enlever des clés d'un dictionnaire avec del
del filled_dict["one"] # Enlever la clé "one" de filled_dict.
Ensembles (non-ordonné, modifiable)¶
# Les sets stockent des ensembles
empty_set = set()
# Initialiser un set avec des valeurs.
some_set = {1, 1, 2, 2, 3, 4} # some_set est maintenant {1, 2, 3, 4}
# Comme les clés d'un dictionnaire, les éléments d'un set doivent être immuables.
valid_set = {(1,), 1}
invalid_set = {[1], 1} # => Lève une TypeError: unhashable type: 'list'
# On peut changer un set :
filled_set = some_set
# Ajouter un objet au set :
filled_set.add(5) # filled_set vaut maintenant {1, 2, 3, 4, 5}
# Chercher les intersections de deux sets avec &
other_set = {3, 4, 5, 6}
filled_set & other_set # => {3, 4, 5}
# On fait l'union de sets avec |
filled_set | other_set # => {1, 2, 3, 4, 5, 6}
# On fait la différence de deux sets avec -
{1, 2, 3, 4} - {2, 3, 5} # => {1, 4}
# On vérifie la présence d'un objet dans un set avec in
2 in filled_set # => True
10 in filled_set # => False
# None, 0, et les strings/lists/dicts (chaînes/listes/dictionnaires) vides valent False
# lorsqu'ils sont convertis en booléens.
# Toutes les autres valeurs valent True
bool(0) # => False
bool("") # => False
bool([]) #=> False
bool({}) #=> False
Structures de contrôle et Itérables¶
# On crée juste une variable
some_var = 5
# Voici une condition "si". L'indentation est significative en Python!
# Affiche: "some_var is smaller than 10"
if some_var > 10:
print("some_var is totally bigger than 10.")
elif some_var < 10: # La clause elif ("sinon si") est optionelle
print("some_var is smaller than 10.")
else: # La clause else ("sinon") l'est aussi.
print("some_var is indeed 10.")
x = 2
# Python a aussi un opérateur conditionnel ternaire, qui peut être utilisé en une seule ligne.
'pair' if x%2==0 else 'impair' #=> pair
#Les boucles "for" itèrent sur une liste
for animal in ["chien", "chat", "souris"]:
# On peut utiliser format() pour interpoler des chaînes formattées
print("{} est un mammifère".format(animal))
# "range(nombre)" retourne un itérable de nombres de zéro au nombre donné
for i in range(4):
print(i)
#"range(debut, fin)" retourne un itérable de nombre de debut à fin.
for i in range(4, 8):
print(i)
#"range(debut, fin, pas)" retourne un itérable de nombres
#de début à fin en incrémentant de pas.
#Si le pas n'est pas indiqué, la valeur par défaut est 1.
for i in range(4, 8, 2):
print(i)
#Les boucles "while" bouclent jusqu'à ce que la condition devienne fausse.
x = 0
while x < 4:
print(x)
x += 1 # Raccourci pour x = x + 1
# On gère les exceptions avec un bloc try/except
try:
# On utilise "raise" pour lever une erreur
raise IndexError("Ceci est une erreur d'index")
except IndexError as e:
pass # Pass signifie simplement "ne rien faire". Généralement, on gère l'erreur ici.
except (TypeError, NameError):
pass # Si besoin, on peut aussi gérer plusieurs erreurs en même temps.
else: # Clause optionelle des blocs try/except. Doit être après tous les except.
print("Tout va bien!") # Uniquement si aucune exception n'est levée.
finally: # Éxécuté dans toutes les circonstances.
print("On nettoie les ressources ici")
# Python offre une abstraction fondamentale : l'Iterable.
# Un itérable est un objet pouvant être traîté comme une séquence.
# L'objet retourné par la fonction range() est un itérable.
filled_dict = {"one": 1, "two": 2, "three": 3}
our_iterable = filled_dict.keys()
print(our_iterable) #=> range(1,10). C'est un objet qui implémente l'interface Iterable
# On peut boucler dessus
for i in our_iterable:
print(i) # Affiche one, two, three
# Cependant, on ne peut pas accéder aux éléments par leur adresse.
our_iterable[1] # Lève une TypeError
# Un itérable est un objet qui sait créer un itérateur.
our_iterator = iter(our_iterable)
# Notre itérateur est un objet qui se rappelle de notre position quand on le traverse.
# On passe à l'élément suivant avec "next()".
next(our_iterator) #=> "one"
# Il garde son état quand on itère.
next(our_iterator) #=> "two"
next(our_iterator) #=> "three"
# Après que l'itérateur a retourné toutes ses données, il lève une exception StopIterator
next(our_iterator) # Lève une StopIteration
# On peut mettre tous les éléments d'un itérateur dans une liste avec list()
list(filled_dict.keys()) #=> Returns ["one", "two", "three"]
Fonctions¶
# On utilise "def" pour créer des fonctions
def add(x, y):
print("x est {} et y est {}".format(x, y))
return x + y # On retourne une valeur avec return
# Appel d'une fonction avec des arguments :
add(5, 6) # => affiche "x est 5 et y est 6" et retourne 11
# Une autre manière d'appeler une fonction : avec des arguments nommés.
add(x=5, y=6)
# Les arguments nommés peuvent être indiqués dans n'importe quel ordre
#(mais doivent toujours être indiqués après les arguments non nommés).
add(y=6, x=5)
# Un argument est optionnel s'il a une valeur par défaut dans la déclaration de la fonction
def add(x,y=1):
return x+y
add(3)
# Définir une fonction qui prend un nombre variable d'arguments
def varargs(*args):
return args
# La variable args fait référence à un tuple des valeurs des arguments.
varargs(1, 2, 3) # => (1, 2, 3)
# On peut aussi définir une fonction qui prend un nombre variable d'arguments nommés.
def keyword_args(**kwargs):
return kwargs
# La variable kwargs fait référence à un dictionnaire des noms et valeurs des arguments.
keyword_args(big="foot", loch="ness"); # => {"big": "foot", "loch": "ness"}
# On peut aussi faire les deux à la fois :
def all_the_args(*args, **kwargs):
print(args)
print(kwargs)
all_the_args(1, 2, a=3, b=4)
# En appelant des fonctions, on peut aussi faire l'inverse :
# utiliser * pour étendre un tuple de paramètres
# et ** pour étendre un dictionnaire d'arguments.
# On parle d'opérateur de dépaquetage.
args = (1, 2, 3, 4)
kwargs = {"a": 3, "b": 4}
all_the_args(*args) # équivalent à foo(1, 2, 3, 4)
all_the_args(**kwargs) # équivalent à foo(a=3, b=4)
all_the_args(*args, **kwargs) # équivalent à foo(1, 2, 3, 4, a=3, b=4)
# On peut retourner plusieurs valeurs (avec un tuple)
def swap(x, y):
return y, x # Retourne plusieurs valeurs avec un tuple sans parenthèses.
# (Note: on peut aussi utiliser des parenthèses)
x = 1
y = 2
x, y = swap(x, y) # => x = 2, y = 1
# (x, y) = swap(x,y) # Là aussi, rien ne nous empêche d'ajouter des parenthèses
x,y
# Espace de noms.
# Les fonctions ont leur propre espace de nom.
# Toute variable définie dans une fonction est locale à la fonction.
x = 5
def setX(num):
# La variable locale x n'est pas la même que la variable globale x
x = num # => 43
print (x) # => 43
# On peut exceptionellement changer la valeur d'une variable de l'espace de nom global
# depuis une fonction en utilisant le mot clé global
# C'est en général considéré comme une mauvaise pratique.
def setGlobalX(num):
global x
print (x) # => 5
x = num # la variable globale x est maintenant 6
print (x) # => 6
setX(43)
setGlobalX(6)
x
# On peut définir une application partielle avec functools.partial
from functools import partial
def f(x,y):
return 3*x+2*y
f(1,1) # => 5
g = partial(f, y=1) #=> g(x) = f(x,1); g est l'application partielle de f où y est fixé à 1.
g(1) #=> 5
# lambda permet d'écrire des "fonctions anonymes" en une seule ligne.
(lambda x: x > 2)(3) # => True
(lambda x, y: x ** 2 + y ** 2)(2, 1) # => 5
# Map permet d'appliquer une fonction à chaque élément d'un itérable et renvoie un itérable
map(add, [1, 2, 3]) # => [11, 12, 13]
map(max, [1, 2, 3], [4, 2, 1]) # => [4, 2, 3]
# Filter permet de filtrer les éléments d'un itérable par une fonction booléeene.
filter(lambda x: x > 5, [3, 4, 5, 6, 7]) # => [6, 7]
Compréhensions de listes¶
# La compréhension de liste est un élément important du langage Python.
# Elle permet de faire des map et des filter de façon très intuitive.
# Prenons une fonction et une liste:
def f(x):
return 3*x
a = [1,3,4,20]
# Pour appliquer la fonction aux éléments de la liste on peut faire:
b = []
for x in a:
b.append(f(x))
# ... Ou utiliser une compréhension de liste:
b = [f(x) for x in a]
# ... ou map
b = list(map(f, a))
# Pour appliquer un filtre on peut faire
b = []
for x in a:
if x%2==0:
b.append(x)
# ...ou utiliser une compréhension de liste
b = [x for x in a if x%2==0]
# ...ou un filter
b = list(filter(lambda x: x%2==0, a))
# On peut aussi combiner les deux:
b = [f(x) for x in a if x%2==0]
b = list(map(f, filter(lambda x: x%2==0, a)))
# On peut aussi faire des compréhension de dictionnaires:
b = {x*2:x for x in a}
c = {k:v**2 for k,v in b.items()}
Classes¶
# On utilise l'opérateur "class" pour définir une classe d'objet.
class Human:
# Un attribut de la classe. Il est partagé par toutes les instances de la classe.
species = "H. sapiens"
# L'initialiseur de base. Il est appelé quand la classe est instanciée.
# Note : les doubles underscores au début et à la fin sont utilisés pour
# les fonctions et attributs utilisés par Python mais contrôlés par l'utilisateur.
# Les méthodes (ou objets ou attributs) comme: __init__, __str__,
# __repr__ etc. sont appelés méthodes magiques.
# Vous ne devriez pas inventer de noms de ce style.
def __init__(self, name):
# Assigner l'argument à l'attribut de l'instance
self.name = name
# Une méthode de l'instance. Toutes prennent "self" comme premier argument.
def say(self, msg):
return "{name}: {message}".format(name=self.name, message=msg)
# Instantier une classe signifie créer un objet de cette classe.
i = Human(name="Ian")
print(i.say("hi")) # affiche "Ian: hi"
j = Human("Joel")
print(j.say("hello")) # affiche "Joel: hello"
Modules¶
# On peut importer des modules
import math
print(math.sqrt(16)) # => 4.0
# On peut importer des fonctions spécifiques d'un module
from math import ceil, floor
print(ceil(3.7)) # => 4.0
print(floor(3.7)) # => 3.0
# On peut importer toutes les fonctions d'un module
# Attention: ce n'est pas recommandé.
from math import *
# On peut raccourcir un nom de module
import math as m
math.sqrt(16) == m.sqrt(16) # => True
# Les modules Python sont juste des fichiers Python.
# Vous pouvez écrire les vôtres et les importer. Le nom du module
# est le nom du fichier.
# On peut voir quels fonctions et objets un module définit
import math
#dir(math)
Fichiers¶
# Lire un fichier avec open renvoie un objet file:
handler = open('file.txt', 'r')
# Le premier argument est le chemin vers le fichier.
# Le second est le mode r:lecture, w:écriture, a: ajout. (rb, wb, ab pour lire/écrire directement en binaire)
# FileNotFoundError est levée si le fichier n'exsite pas en mode 'r'.
open('non_existing_file.txt', 'r') #=> FileNotFoundError
# Un fichier ouvert avec open doit être fermé avec .close.
handler = open('test.txt', 'r')
handler.read() #=> Première ligne\nDeuxième ligne"
handler.close()
handler.read() #=> Lire un fichier fermé lève une ValueError
# Pour ne pas oublier de fermer les fichier il est recommandé d'utiliser un bloc with
with open('test.txt','r') as handler:
handler.read()
#=> close est appelé automatiquement à la fin du bloc.
handler.read() #=> ValueError
# Itérer sur les lignes d'un fichier
with open('test.txt','r') as handler:
for line in handler:
print(line)
# Écrire dans un fichier:
with open('new.txt','w') as handler:
handler.write('Première ligne\n')
with open('new.txt','w') as handler:
handler.write('En mode "w", le fichier est écrasé\n')
# Ajouter à la fin d'un fichier
with open('new.txt','a') as handler:
handler.write('Seconde ligne')
open('new.txt','r').read(); #=> 'En mode "w", le fichier est écrasé\nSeconde ligne'
Expressions Régulières¶
Les Expressions Régulières (ou regexp voire RE) constituent un petit langage de programmation qui permet de manipuler des chaînes de caractères. Référence: How To Regex (Python documentation), xkcd 208, xkcd 1313.
# Les regexp font partie de la bibliothèque standard de Python. Il n'y a pas de module à installer.
import re
# Deux interfaces sont disponibles:
poeme = """Ce que l’on conçoit bien s’énonce clairement,
Et les mots pour le dire arrivent aisément"""
# Fonctionnel
# Est-ce que la chaine commencer par le pattern ?
re.match('mot', poeme) #=> None, match ne cherche à matcher le pattern que dans
# Est-ce que le pattern se trouve dans la chaine ? (Retourne la position de occurence)
re.search('mot', poeme) #=> Match object
# Renvoyer toutes les occurences du pattern dans la chaine sous forme de chaines.
re.findall('mot', poeme) #=> ['mot']
# Trouver toutes les occurences du pattern dans la chaine sous.
re.finditer('mot', poeme) #=> iterable of match object
# Séparer la chaine à chaque occurence du pattern
re.split('mot', poeme) #=> ['ce que...', 'pour le dire...']
# Remplacer toutes les occurences du pattern
re.sub('mot', 'terme', poeme) #=> '(...) Et les termes pour le dire (...)'
# Objet:
regexp = re.compile('mot')
regexp.match(poeme)
regexp.search(poeme)
regexp.findall(poeme)
regexp.finditer(poeme)
regexp.split(poeme)
regexp.sub('terme', poeme);
# Les diffèrences de performances sont négligeables car toutes les regexp sont mises
# en cache quelque soit l'inteface utilisée.
Caractère | Signification |
---|---|
. |
Tout caractère (sauf \n ) |
[abc] | a,b ou c |
[a-z] |
abcdefghijklmopqrstuvwyz |
[^abc] |
tout sauf a,b ou c |
\[ |
[ |
A ⎮ B |
A ou B |
Position | |
^ |
Début de la chaîne |
$ |
Fin de la chaîne |
Répétition | (du caractère juste avant) |
* |
Répété 0+ fois |
+ |
Répété 1+ fois |
'?` | Répété 0 ou 1 fois |
{m} |
Répété m fois |
{m,n} |
Répété entre m et n fois |
Groupes | |
(blah) |
définit un groupe pour l'objet match |
# L'objet Match
match.groups() # un n-uplet contenant tous les sous groupes d'un match
match.start(group) # Position du début groupe n.(le groupe 0 est tout le match)
match.end(group) # Position de la fin du groupe n.
match.span(group) # (debut, fin)
Calcul numérique avec Numpy¶
Numpy est la bibliothèque de calcul numérique qui implémente un objet très pratique: le tableau n-dimensionel (np.array).
N'hésitez pas à lire le tutorial numpy en entier. Gardez la liste des fonctions de la bibliothèque sous le coude.
# Il est d'usage de l'importer sous le nom np (en utilisant l'operateur `as`)
import numpy as np
L'objet principal de numpy est le tableau (np.array
). Ces valeurs sont stockées en continu dans la mémoire de l'ordinateur, ce qui les rend bien plus rapide que les listes python pour de nombreuses opérations.
Un tableau numpy a 4 attributs importants:
np.array.ndim
: le nombre de dimensions du tableau (axes)np.array.shape
: la taille du tableau le long de chaque axe. (2,3) signifie 2 lignes, 3 colonnes.np.array.size
: le nombre total d'éléments du tableau.np.array.dtype
: Le type des éléments du tableau, en général des nombres à virgule flotante sur 64 bit (np.float64
) mais ça peut être des entiers sur 32 (np.int32
) ou des nombres complexes (np.complex
) voire des objets (objects
)
Pour instancier un tableau vous pouvez utiliser:
np.array(iterateur)
pour l'initialiser avec une séquence de votre choix.np.zeros(shape)
pour initialiser un tableau de zéros.np.arange(debut,fin,pas)
pour une séquence arithmétique d'entiers.np.linspace(deut,fin,nombre_de_pas)
pour une séquence artihmétique de nombre à virgule flotante.
# Examples
a = np.array([1,2,3,4])
b = np.array([(1.5,2,3), (4,5,6)])
c = np.array( [ [1,2], [3,4] ], dtype=complex )
d = np.zeros((3,3))
e = np.arange(10)
f = np.linspace(0,1,10)
for elt in (a,b,c,d,e,f):
print(elt, end='\n\n')
# Les opérations aritmhétiques: `+, -, *, ** , <, >` sont appliquées élément par élément.
a = np.array([1,2,3])
print(a)
print(a + a)
print(a - a)
print(a * a)
print(a**2)
print(a>1)
On peut sommer ou multiplier les tableau avec des scalaires, l'opération se fait élément par élément (on parle de broadcasting)
a = np.array([4,2,3])
print(a + 10)
print(a * 3)
Les np.array
s ont des méthodes prédéfinies pour le min
, max
, sum
...
a = np.arange(10)
print(a.max())
print(a.min())
print(a.sum())
Quelques méthodes et fonctions d'algèbre linéaire:
A = np.array([[1.0, 2.0], [3.0, 4.0]])
A.transpose() # transposée
np.dot(A,A) # produit matriciel
np.linalg.inv(A) # Inverse
np.trace(A) # trace
np.linalg.eig(A) # valeurs propres et vecteurs propres associées.
print(A)
L'indexage se fait comme pour les listes python: debut:fin:pas
, sauf que l'on peut avoir plusieurs axes:
B = np.eye(4,4)
B[2,3]
B[0:5, 1]
B[ : ,1]
B[1:3, : ]
B[ : ,1] == B[ 0:, 1]
B[ : ,1] == B[ :4, 1]
B[ : ,1] == B[ 0:4, 1];
Graphes avec Matplotlib¶
Matplotlib est la bibliothèque de grapheur la plus utilisée dans la communauté Python. Encore une fois, si vous voulez en savoir plus allez voir le tutorial officiel.
# Matplotlib est la bibliothèque qui s'occupe de tracer des graphes.
# Son module pyplot offre une interface simple et fonctionnelle.
# Il est d'usage de l'importer sous le nom plt.
import matplotlib.pyplot as plt
# Cette commande (spécifique à jupyter) permet d'afficher les graphes de matplotlib directement dans
# le navigateur.
%matplotlib inline
# Alternativement, utilisez `notebook` plutôt que `inline` pour une version interactive.
#%matplotlib notebook
# plot permet de tracer des courbes par interpolation linéaire.
x = np.linspace(0,3*np.pi)
y = np.sin(x)
plt.plot(x,y)
# scatter permet de tracer des points.
plt.scatter(x,y)
# hist permet de tracer des histogrammes.
plt.hist(y)
# Il y a deux types d'objets principaux en matplotlib: les figures et les axes (ou sous-figures).
# Par défaut, les plots sont fait sur l'objet axe courant (plt.gca()).
# On peut changer l'objet axe courant avec plt.subplot(position) ou
# (plus pratique) on peut assigner tous les objets axes d'un coup:
fig, (ax1,ax2,ax3) = plt.subplots(1,3, figsize=(12,4))
ax1.plot(x,y)
ax2.scatter(x,y)
ax3.hist(y)
# La méthode set des objets axe permet de donner un titre ou de changer
# les propriétés du graphe (limites de la zone à tracer, graduations...)
ax1.set(xlabel='titre x', ylabel='titre y', title='titre du graphe')
ax2.set(xlim=(-1,15))
ax3.set(xticks=(-1,0,.7,1))
# La méthode savefig de l'objet figure permet de sauvegarder l'image dans un fichier.
fig.savefig('test.png')
Calcul scientifique avec Scipy¶
La bibliothèque scipy (scientific python) contient toute une foule de fonctions utilie pour le calcul numérique. En voici quelques morceaux choisis. N'oubliez pas de vous référer à la documentation pour plus de détails.
Intégration numérique¶
# Le module scipy.integrate permet de réaliser de l'intégration numérique.
import scipy.integrate
$$\int_0^1 f(x) dx$$
# Calcul de l'intégrale d'une fonction sur un intervalle
def f(x):
return 4*np.sqrt(1-x**2)
scipy.integrate.quad(f, 0, 1)
$$ y(t) = y(0) + \int_0^t f(y(t)) $$
# Intégration numérique d'une équation différentielle
def dxdt(x, t):
return 3*x
y0 = 1
traj = scipy.integrate.odeint(dxdt, y0, np.linspace(0,1))
plt.plot(traj)
Statistiques¶
# Le module scipy.stats contient de nombreuses distributions de probabilités et des tests statistiques.
import scipy.stats
X = scipy.stats.norm(loc=0) # Une variable aléatoire normalement distribuée centrée réduite
X.mean() #=> 0
X.std() #=> 1
X.pdf(1) #=> \phi(1)
Y = scipy.stats.norm(loc=10) # v.a. gaussienne, espérance 10
Z = scipy.stats.norm(loc=10, scale=.3) # v.a. gaussienne, espérance 10, ecart-type .3
# Échantilloner une variable aléatoire:
sampleX = X.rvs(size=100)
sampleY = Y.rvs(size=100)
sampleZ = Z.rvs(size=100)
x = np.linspace(-10,12,100)
# rv.pdf() donne la fonction densité de probabilité
plt.plot(x, X.pdf(x))
plt.plot(x, Y.pdf(x))
plt.plot(x, Z.pdf(x))
plt.hist(sampleX, alpha=.5, label='X', normed=True, color='C0')
plt.hist(sampleY, alpha=.5, label='Y', normed=True, color='C1')
plt.hist(sampleZ, alpha=.5, label='Z', normed=True, color='C2')
plt.legend();
# T-test à deux échantillons
scipy.stats.ttest_ind(sampleX, sampleY) # => pvalue < 0.05
scipy.stats.ttest_ind(sampleY, sampleZ) # => pvalue > 0.05
Calcul symbolique avec Sympy¶
Sympy est une bibliothèque de calcul symbolique en Python. Elle permet de résoudre un grand nombre de problèmes courants en calcul scientifique: simplifications, résolution d'équations, différentiation, calcul de limites... Lisez la documentation pour plus d'informations.
import sympy
sympy.init_printing() # Pretty_print permet d'afficher les équations dans le notebook via LaTeX.
# Tout d'abord il faut créer un objet symbole pour chaque grandeur utilisée.
x,y, a, b, c = sympy.symbols('x,y,a,b,c')
# On peut ensuite créer des expressions en utilisant les opérateurs usuels de python.
expr = a * (x + b)
expr
# .expand permet de développer une expression
expr.expand()
# .simplify permet de la simplifier
(a + (-a + b * 1/2 * c )+ b).simplify()
# La simplification par défaut n'est pas toujours satisfaisante.
# voir https://docs.sympy.org/latest/tutorial/simplification.html
# Créer une equation se fait avec l'objet Eq.
eq = sympy.Eq(a*x+b, 0)
eq
# Résoudre une équation pour une variable donnée:
sympy.solve(eq, x)
# Dériver une expression
sympy.diff(expr, x)
expr = 4*sympy.sqrt(1-x**2)
expr
# Calcul de l'intégrale d'une fonction sur un intervalle
sympy.integrate(expr,(x,0,1))
Tableaux de données avec Pandas¶
Pandas est une bibliothèque qui implémente la classe "tableau" de donnée (pandas.Dataframe
). Il s'agit d'un tableau a double entrée constitué de colonnes (pandas.Series
) qui peuvent être de différent types. Lisez la documentation pour plus d'information. Le tutoriel pandas en 10 minutes est très pratique.
import pandas as pd
# On peut initialiser un pd.Dataframe avec un np.Array.
data = pd.DataFrame(np.random.random(size=(10,3)), columns=['A','B','C'])
# Pandas possède de nombreuses fonctions pour importer des données depuis un fichier:
try:
pd.read_csv("data.csv")
pd.read_csv("data.tsv", sep='\t')
pd.read_json("data.json")
pd.read_excel("data.xls")
except FileNotFoundError:
pass
data
# Dataframe.describe() donne quelques statistiques descriptives.
data.describe()
# L'indexage avancé permet de filtrer les tableaux avec des booléens.
data[data.A>0.5]
data['GrandA'] = ['Oui' if x>0.5 else 'Non' for x in data.A]
data
# Dataframe.groupby permet de grouper les valeurs en fonction de la valeur d'une colonne.
# Cela donne un objet groupby qui a ses méthodes mean, max, sum...
data.groupby('GrandA').mean()
# Et sur lequel on peut itérer:
for value, dt in data.groupby('GrandA'):
print('Le groupe "{}" a {} valeurs.'.format(value, dt.shape[0]))
# Pandas est compatible avec matplotlib...
plt.scatter(data.A, data.B);
#...et les dataframes possèdent une méthode permettant des visualisations rapides.
data.plot()
# Les dataframe permettent des opérations sophistiquées de bases de données comme la jointure de table.
etudiants = pd.DataFrame([{"Nom":"Alice","Parcours":"Biologie", "Niveau":"L3"},
{"Nom":"Bob","Parcours":"Biologie", "Niveau":"L3"},
{"Nom":"Megan","Parcours":"Biologie", "Niveau":"M1"}])
notes = pd.DataFrame({'Nom':['Alice','Alice','Bob','Bob','Megan', 'Megan'],
'Cours':['Python','R']*3,
'Note':[15,12,10,16,18,11]})
from IPython.display import display
display(etudiants, notes)
pd.merge(etudiants, notes, left_on='Nom', right_on='Nom')