TP3 - Introduction à Python

Analyse de Données Massives - Master 1ère année

Python est un langage de programmation en pleine croissance dans le domaine de la Data Science et du Big Data. Il a une syntaxe particulière pour certains aspects, avec des points communs tout de même avec de nombreux autres. Nous allons ici aborder les premiers éléments de syntaxe de ce langage.

Eléments de base

Utilisation en mode console

python est un langage scripté, dont l'exécution se fait dans une console. Dans celle-ci, il est donc possible d'exécuter les commandes les unes après les autres. Il est aussi possible (et recommander) d'écrire son script dans un fichier texte (souvent avec l'extension .py) et de l'exécuter via execfile().

Pour accéder à l'aide d'une fonction, il existe la fonction help(), prenant éventuellement en paramètre une fonction directement, ou une chaîne de caractère. Si la fonction n'a pas de paramètre, elle démarre l'aide interactive.

Notebook

Le module jupyter permet de travailler avec des notebook, qui sont des documents contenant à la fois le code, les résultat et du texte. Pour pouvoir l'utiliser, il faut d'abord installer le module dans python, avec la commande pip3 dans un terminale de commande.

{bash}
pip3 install jupyter

Une fois le module installé, il est possible de lancer le serveur jupyter avec la commande suivante, à faire dans un terminale de commande.

{bash}
jupyter notebook

Il faut appuyer sur Ctrl + C pour stopper le processus.

Types de données

Comme tous les autres langages, python a plusieurs types possibles pour les données. En voici quelques uns. Vous pouvez exécuter les commandes dans une console pour voir le résultat.

In [82]:
1
Out[82]:
1
In [83]:
type(1)
Out[83]:
int
In [84]:
1.234
Out[84]:
1.234
In [85]:
type(1.234)
Out[85]:
float
In [86]:
"chaîne"
Out[86]:
'chaîne'
In [87]:
type("chaîne")
Out[87]:
str
In [88]:
(1, 2)
Out[88]:
(1, 2)
In [89]:
type((1, 2))
Out[89]:
tuple
In [90]:
[1, 2]
Out[90]:
[1, 2]
In [91]:
type([1, 2])
Out[91]:
list
In [92]:
{"a": 1, "b": "deux"}
Out[92]:
{'a': 1, 'b': 'deux'}
In [93]:
type({"a": 1, "b": "deux"})
Out[93]:
dict

Les tuples, lists et les dicts peuvent s'imbriquer les uns dans les autres.

In [94]:
(1, (2, 3), [4, 5], {"a": 1, "b": "deux"})
Out[94]:
(1, (2, 3), [4, 5], {'a': 1, 'b': 'deux'})
In [95]:
[1, [2, 3], (4, 5), {"a": 1, "b": "deux"}]
Out[95]:
[1, [2, 3], (4, 5), {'a': 1, 'b': 'deux'}]
In [96]:
{"a": 1, "b": "deux", "c": (5, 6), "d": [7, 8]}
Out[96]:
{'a': 1, 'b': 'deux', 'c': (5, 6), 'd': [7, 8]}

Il existe des fonctions permettant de passer d'un type à l'autre (quand cela est possible), telles que int(), float(), str(), tuple() et list().

Il existe aussi des valeurs prédéfinies, telles que True (vrai), False (faux) et None (donnée absente).

Création et suppression de variables

Il n'y a pas de mot-clé pour la définition d'une variable. Celle-ci est définie/créée lors de sa première affectation. Si elle n'existe pas mais qu'on essaie de l'utiliser, alors un message d'erreur apparaît. Il est aussi possible de la supprimer via la fonction del().

In [97]:
a = 1
In [98]:
print(a)
1
In [ ]:
print(b)
In [99]:
del(a)
In [ ]:
print(a)

Type dynamique

Bien que python soit rigoureux dans l'évaluation des expressions (il ne fait pas de cast automatique - i.e. changement de type des données), le type d'une variable est dit dynamique. Le type d'une variable dépend uniquement de la valeur de son affectation. Voici un exemple simple de ce phénomène.

In [100]:
a = 1
In [101]:
print(a)
1
In [102]:
type(a)
Out[102]:
int
In [103]:
a = "deux"
In [104]:
print(a)
deux
In [105]:
type(a)
Out[105]:
str

La variable a est passé du type int au type str sans qu'on l'explicite. Il faut donc faire attention lors de l'écriture de ses programmes.

Affichage

Comme vu précédemment, il existe la fonction print() permettant d'afficher du texte et/ou le contenu des variables dans la console. Celle-ci peut prendre les paramètres sep, qui permet d'indiquer le ou les caractères séparant les champs (un espace " " par défaut), et end, qui permet d'indiquer le caractère de fin de ligne (retour à la ligne "\n" par défaut).

In [106]:
print("Bonjour")
Bonjour
In [107]:
print("a =", a)
a = deux
In [108]:
print("a", a, sep = "=")
a=deux
In [109]:
print("a=", end = "")
a=
In [110]:
print(a)
deux

Opérateurs

Arithmétiques

Il existe bien évidemment tous les opérateurs arithmétiques classiques, tels que présentés ci-dessous.

In [111]:
5 + 2
Out[111]:
7
In [112]:
5 - 2
Out[112]:
3
In [113]:
5 * 2
Out[113]:
10
In [114]:
5 / 2
Out[114]:
2.5
In [115]:
5 // 2
Out[115]:
2
In [116]:
5 % 2
Out[116]:
1
In [117]:
5 ** 2
Out[117]:
25

Comparaisons

De même pour les opérateurs de comparaisons, tous très classiques.

In [118]:
5 > 2
Out[118]:
True
In [119]:
5 >= 2
Out[119]:
True
In [120]:
5 < 2
Out[120]:
False
In [121]:
5 <= 2
Out[121]:
False
In [122]:
5 == 2
Out[122]:
False
In [123]:
5 != 2
Out[123]:
True

Booléens

Idem aussi pour les opérateurs booléens. Il faut juste noter la fonction not() pour obtenir la négation booléenne.

In [124]:
True | False
Out[124]:
True
In [125]:
True & True
Out[125]:
True
In [126]:
not(True)
Out[126]:
False

Eléments de langage

Traitement conditionnel

Comme dans tout langage, le traitement conditionnel se fait à partir d'un if. Voici un exemple très simple d'utilisation.

In [127]:
a = 3
if (a > 2):
    print("sup")
sup

Une particularité de python est d'utiliser l'indentation (i.e. le décalage à droite à l'aide d'au moins une tabulation) pour définir les opérations à réaliser dans un bloc. Si on veut faire plusieurs opérations dans le if, voila comment procéder par exemple.

In [128]:
if (a > 2):
    print("dans le IF")
    print("sup")
dans le IF
sup

Il existe aussi la possibilité d'ajouter soit un traitement alternatif simple (avec else), soit un traitement alternatif conditionnel lui aussi (avec elif).

In [129]:
# Avec un else seulement
a = 1
if (a > 2):
    print("sup")
else:
    print("inf")
inf
In [130]:
# Avec elif en plus
if (a > 2):
    print("sup")
elif (a > 0):
    print("mid")
else:
    print("inf")
mid

Il n'existe rien dans python pour le switch/casetel qu'on peut le voir par ailleurs. Mais on peut passer par un dictionnaire pour des tests d'égalité (cf plus bas pour plus d'informations sur les dictionnaires).

In [131]:
jour = {
    0: "lundi",
    1: "mardi",
    2: "mercredi",
    3: "jeudi",
    4: "vendredi",
    5: "samedi",
    6: "dimanche"
}
jour.get(2)
Out[131]:
'mercredi'

Traitement itératif

On utilise en premier la boucle for dans laquelle on peut utiliser la fonction range() pour avoir les valeurs entre 0 (par défaut) et la valeur passée en paramètre. Vous remarquerez que i est persistant à la boucle et garde la dernière valeur.

In [132]:
for i in range(5):
    print(i)
print("dernière valeur de i :", i)
0
1
2
3
4
dernière valeur de i : 4

Cette fonction range() peut prendre deux paramètres, et dans ce cas, génère la boucle entre les deux par pas de 1.

In [133]:
for i in range(5, 10):
    print(i)
5
6
7
8
9

Et si l'on souhaite modifier le pas de séquence, on ajoute un troisième paramètre. Celui-ci doit être cohérent par rapport aux deux premières valeurs.

In [134]:
for i in range(10, 5, -1):
    print(i)
10
9
8
7
6

Il est possible d'utiliser une list ou un tuple pour définir les valeurs dans lesquelles naviguer.

In [135]:
for i in [4, 1, 10]:
    print(i)
4
1
10

Et en utilisant une chaîne de caractère, on peut naviguer dans celle-ci.

In [136]:
for l in "Bonjour":
    print(l)
B
o
n
j
o
u
r

Mais en utilisant un groupe de chaîne (list ou tuple), on travaille sur les chaînes au complet.

In [137]:
for l in ("jour", "soir"):
    print("Bon", l, sep = "")
Bonjour
Bonsoir

La fonction enumerate() permet de récupérer à la fois les indices des valeurs et les valeurs.

In [138]:
a = [3, 1, 9, 4]
for i, x in enumerate(a):
    print("i =", i, "\tx =", x)
i = 0 	x = 3
i = 1 	x = 1
i = 2 	x = 9
i = 3 	x = 4

Et la fonction zip() permet elle de travailler sur plusieurs groupes de valeurs (ici deux listes). Notez que cette fonction limite le résultat à la taille du plus petit regroupement.

In [139]:
b = ["trois", "un", "neuf"]
for i, j in zip(a, b):
    print(" i =", i, "\tj =", j)
 i = 3 	j = trois
 i = 1 	j = un
 i = 9 	j = neuf

Enfin, on dispose aussi de la boucle while qui teste en début de boucle si une condition est toujours vérifiée. Bien évidemment, à la fin de la boucle, i a la première valeur à rendre la condition fausse. Ici, i += 1 est un raccourci pour i = i + 1.

In [140]:
i = 0
while i < 10:
    print(i)
    i += 1
print("Valeur de i :", i)
0
1
2
3
4
5
6
7
8
9
Valeur de i : 10

Création et manipulations d'objets

Comme indiqué, il existe différents types d'objets en python. Sont présentés ici des exemples de créations et de manipulations de chaînes (str), de tuples, de list et de dictionnaires (dict).

Chaînes

Une chaîne de caractère se définit à l'aide des quotes simples ('') ou doubles (""). Par défaut, python présentera les chaînes avec des simples quotes. Mais en présence d'une apostrophe dans la chaîne, il faut la déclarer avec des doubles quotes. Il est possible de connaître la longueur de la chaîne avec la fonction len().

In [141]:
"bonjour"
Out[141]:
'bonjour'
In [142]:
"aujourd'hui"
Out[142]:
"aujourd'hui"
In [143]:
a = 'bonjour'
len(a)
Out[143]:
7

Pour extraire des sous-chaînes, on utilise l'indexation en séquence python, en prenant en compte que le premier caractère est en position 0. La séquence par défaut est par pas de 1 (par exemple, 1:5 renvoie les positions 1, 2, 3, 4, 5). Si on omet le premier ou le dernier, python comprend qu'on désire le début ou la fin de la chaîne. On peut ajouter un paramètre à la séquence, permettant de jouer sur le pas entre les valeurs de la séquence.

In [144]:
a[1:5]
Out[144]:
'onjo'
In [145]:
a[0:3]
Out[145]:
'bon'
In [146]:
a[:3]
Out[146]:
'bon'
In [147]:
a[3:len(a)]
Out[147]:
'jour'
In [148]:
a[3:]
Out[148]:
'jour'
In [149]:
a[::]
Out[149]:
'bonjour'
In [150]:
a[::-1]
Out[150]:
'ruojnob'
In [151]:
a[::2]
Out[151]:
'bnor'
In [152]:
a[1:5:2]
Out[152]:
'oj'
In [153]:
a[5:1:2]
Out[153]:
''

Sur ces chaînes, on peut réaliser un certain nombre d'opérations classiques, telles que le changement de casse (upper() ou lower()), la mise en majuscule des premières lettres de chaque mot (capitalize()), la recherche d'une sous-chaîne (find() - première occurence), le remplacement d'une sous-chaîne (replace()), le dénombrement de sous-chaînes (count()) ou le découpage en sous-chaînes selon un caractère (split()). En voici quelques exemples.

In [154]:
a.upper()
Out[154]:
'BONJOUR'
In [155]:
a.capitalize()
Out[155]:
'Bonjour'
In [156]:
a.find('j')
Out[156]:
3
In [157]:
a.replace('jour', 'soir')
Out[157]:
'bonsoir'
In [158]:
a.count('o')
Out[158]:
2
In [159]:
a.split('j')
Out[159]:
['bon', 'our']

Tuples

Un tuple en python est un ensemble déclaré via des (), composé de valeurs pas forcément de même type et éventuellement complexe, qu'il n'est pas possible de modifier. C'est en quelque sorte une constante, une fois déclarée.

In [160]:
a = (3, 1, 9, 7)
print(a)
(3, 1, 9, 7)
In [161]:
a[0]
Out[161]:
3
In [ ]:
a[0] = 5 # Pas possible : tuple == constante

Il est possible d'utiliser les mêmes outils d'indexation en séquence vu pour les chaînes.

Listes

Une list est aussi un ensemble déclarée via des [], composé d'éléments pas forcément tous du même type et possiblement complexe. A la différence d'un tuple, une liste est modifiable.

In [162]:
a = [3, 1, 9, 7]
print(a)
[3, 1, 9, 7]
In [163]:
len(a)
Out[163]:
4
In [164]:
a[0]
Out[164]:
3
In [165]:
a[1:3]
Out[165]:
[1, 9]

Il est possible d'utiliser les mêmes outils d'indexation en séquence vu pour les chaînes.

Nous disposons sur ces listes de plusieurs fonctions tels que reverse() (pour inverser la liste), sort() (tri, avec l'option reverse pour le choix du tri), pop() (pour récupérer et supprimer le dernier élément), append() (pour ajouter un élément à la fin), insert() (pour insérer un élément dans la liste, à la position indiquée - paramètres = position suivie de la valeur), remove() (pour supprimer les valeurs passées en paramètre). Toutes ces fonctions modifient directement la liste sur laquelle on les applique.

In [166]:
a.reverse()
print(a)
[7, 9, 1, 3]
In [167]:
a.sort()
print(a)
[1, 3, 7, 9]
In [168]:
a.sort(reverse=True)
print(a)
[9, 7, 3, 1]
In [169]:
a.pop()
print(a)
[9, 7, 3]
In [170]:
a.append(5)
print(a)
[9, 7, 3, 5]
In [171]:
a.insert(0, 6)
print(a)
[6, 9, 7, 3, 5]
In [172]:
a.remove(7)
print(a)
[6, 9, 3, 5]

Un autre moyen d'insérer une valeur, voire plusieurs, à une liste est d'utiliser l'opérateur +, tel qu'indiqué ci-dessous. Celui-ci permet une concaténation des deux listes en une seule. L'opérateur * permet lui de répéter une liste autant de fois que désiré.

In [173]:
a + [1, 2]
a * 2
Out[173]:
[6, 9, 3, 5, 6, 9, 3, 5]

On peut utiliser un mécanisme spécifique, appelé list comprehension (fonctionnant aussi sur les chaînes et les tuples), permettant de récupérer les valeurs (ou un calcul sur chaque valeur) pour tous les éléments de la liste (ou certains si on applique un if).

In [174]:
a = [3, 1, 9, 7]
[x**2 for x in a]
Out[174]:
[9, 1, 81, 49]
In [175]:
[x**2 for x in a if x >= 4]
Out[175]:
[81, 49]

Par contre, il faut faire très attention au passage de référence lorsqu'on copie une liste. En effet, dans le code suivant, on copie a dans b. Et en modifiant a, on remarque que b est aussi modifié. Et l'inverse est aussi vrai.

In [176]:
a = [1, 2, 3, 4]
print(a)
[1, 2, 3, 4]
In [177]:
b = a
print(b)
[1, 2, 3, 4]
In [178]:
a[0] = 5
print(a)
print(b)
[5, 2, 3, 4]
[5, 2, 3, 4]
In [179]:
b[1] = 9
print(b)
print(a)
[5, 9, 3, 4]
[5, 9, 3, 4]

Pour remédier à ce problème, on doit duppliquer la liste avec la fonction copy() de la liste initiale, comme ci-dessous.

In [180]:
b = a.copy()
a[0] = -1
print(a)
print(b)
[-1, 9, 3, 4]
[5, 9, 3, 4]

Dictionnaires

Les dictionnaires (dict en python) sont des listes nommées (définies via des {}), c'est-à-dire que chaque élément a un nom (appelé aussi clé). Ces éléments ne sont pas forcément tous du même type, et peuvent aussi être complexe.

In [181]:
a = { "nom": "Jollois", "prenom": "FX", "langues": ["R", "Python", "SQL", "SAS"], "labo": { "nom": "LIPADE", "lieu": "CUSP"}}
print(a)
len(a)
{'nom': 'Jollois', 'prenom': 'FX', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'labo': {'nom': 'LIPADE', 'lieu': 'CUSP'}}
Out[181]:
4

Pour accéder aux éléments du dictionnaire, on peut utiliser le formalisme suivant.

In [182]:
a["nom"]
Out[182]:
'Jollois'
In [183]:
a["langues"]
Out[183]:
['R', 'Python', 'SQL', 'SAS']
In [184]:
a["langues"][0]
Out[184]:
'R'
In [185]:
a["labo"]
Out[185]:
{'lieu': 'CUSP', 'nom': 'LIPADE'}
In [186]:
a["labo"]["lieu"]
Out[186]:
'CUSP'

Il existe aussi des fonctions utiles sur ces objets, telles que get() (pour récupérer la valeur d'une clé), keys() (pour avoir la liste des clés de l'objet), values() (pour avoir les valeurs des clés, dans le même ordre que listé dans keys()), popitem() (pour récupérer un dictionnaire avec le dernier item, et le supprimer du dictionnaire initiale) et pop() (pour récupérer la valeur de l'item passé en paramètre, et le supprimer de l'élément de départ).

In [187]:
a.get("nom")
Out[187]:
'Jollois'
In [188]:
a.keys()
Out[188]:
dict_keys(['nom', 'prenom', 'langues', 'labo'])
In [189]:
a.values()
Out[189]:
dict_values(['Jollois', 'FX', ['R', 'Python', 'SQL', 'SAS'], {'nom': 'LIPADE', 'lieu': 'CUSP'}])
In [190]:
a.popitem()
Out[190]:
('labo', {'lieu': 'CUSP', 'nom': 'LIPADE'})
In [191]:
print(a)
{'nom': 'Jollois', 'prenom': 'FX', 'langues': ['R', 'Python', 'SQL', 'SAS']}
In [192]:
a.pop("nom")
Out[192]:
'Jollois'
In [193]:
print(a)
{'prenom': 'FX', 'langues': ['R', 'Python', 'SQL', 'SAS']}

On peut ajouter facilement un item à un dictionnaire, en lui affectant une valeur.

In [194]:
a["type"] = "MCF"
print(a)
{'prenom': 'FX', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'type': 'MCF'}

De même que pour les listes, il faut faire attention lors de l'affectation d'un dictionnaire à un autre. La fonction copy() permet donc d'obtenir une copie indépendante de l'objet initial.

In [195]:
b = a
b["prenom"] = "Xavier"
print(b)
print(a)
{'prenom': 'Xavier', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'type': 'MCF'}
{'prenom': 'Xavier', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'type': 'MCF'}
In [196]:
b = a.copy()
b["prenom"] = "FX"
print(b)
print(a)
{'prenom': 'FX', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'type': 'MCF'}
{'prenom': 'Xavier', 'langues': ['R', 'Python', 'SQL', 'SAS'], 'type': 'MCF'}

Le mécanisme de list comprehension est aussi utilisable pour créer un dictionnaire. Il faut dans ce cas indiquer deux valeus : la clé et sa valeur. Dans notre cas, la fonction dict() appliqué sur le résultat de la fonction zip() des deux listes nous permet d'avoir le même résultat.

In [197]:
fruits = ["pommes", "bananes", "poires", "oranges"]
nombres = [5, 2, 10, 4]
{fruits[i]:nombres[i] for i in range(4)}
dict(zip(fruits, nombres))
Out[197]:
{'bananes': 2, 'oranges': 4, 'poires': 10, 'pommes': 5}

Fonctions

Définition

L'opérateur def permet de créer une fonction (ou une procédure qui sera juste une fonction ne renvoyant rien). L'opérateur return indiquant le résultat à renvoyer le cas échéant. Le premier exemple est une fonction renvoyant une valeur (approximative) de $\pi$. Comme pour un if, le bloc d'instructions est défini selon l'indentation.

In [198]:
def pi():
    res = 3.141593 ** 2
    return res
pi()
Out[198]:
9.869606577649

Ce deuxième exemple est une procédure affichant tout simplement "Bonjour".

In [199]:
def afficheBonjour():
    print("Bonjour")
afficheBonjour()
Bonjour

Il est bien évidemment possible de passer un paramètre à une fonction, sans qu'on ait à déclarer son type. Bien sûr, un appel de la fonction sans valeur pour un paramètre défini entraîne une erreur.

In [200]:
def afficheBonjour(nom):
    print("Bonjour", nom)
afficheBonjour("Jollois")
Bonjour Jollois
In [ ]:
afficheBonjour()

Lorqu'il y a plus d'un paramètre, on peut faire un appel classique. Mais il est aussi possible de nommer explicitement les paramètres. Avec ce mécanisme, il est ainsi possible de les déclarer dans l'ordre que l'on veut. Mais si l'on nomme un paramètre, il est obligatoire de nommer les autres (erreur d'exécution sur la dernière ligne).

In [201]:
def afficheBonjour(nom, prenom):
    print("Bonjour", prenom, nom)
afficheBonjour("Jollois", "FX")
Bonjour FX Jollois
In [202]:
afficheBonjour(nom = "Jollois", prenom = "FX")
Bonjour FX Jollois
In [203]:
afficheBonjour(prenom = "FX", nom = "Jollois")
Bonjour FX Jollois
In [ ]:
afficheBonjour(prenom = "FX", "Jollois")

Il existe la possibilité de définir une valeur par défaut à un paramètre dans une fonction. Ceci permet d'appeler la fonction sans donner de valeur pour ce paramètre (la fonction utilisera celle par défaut donc).

In [205]:
def afficheBonjour(nom, prenom = "?"):
    print("Bonjour", prenom, nom)
afficheBonjour("Jollois", "FX")
Bonjour FX Jollois
In [206]:
afficheBonjour("Jollois")
Bonjour ? Jollois

Pureté d'une fonction

Une fonction est dite pure si elle n'a pas d'effet de bords lors de son appel. Dans la fonction f1() définie ci-dessous, le paramètre a est local et son affectation ne change pas la valeur de la variable a globale. f1() sera donc considérée comme pure.

In [207]:
def f1(a):
    a = [b**2 for b in a]
    return a
a = list(range(5))
print(a)
print(f1(a))
print(a)
[0, 1, 2, 3, 4]
[0, 1, 4, 9, 16]
[0, 1, 2, 3, 4]

Maintenant, si nous définissons la fonction comme f2() ci-dessous, nous remarquons que la variable a globale est modifiée suite à l'appel de la fonction. f2() est considérée impure.

In [208]:
def f2(a):
    for i in a:
        a[i] = a[i] ** 2
    return a
a = list(range(5))
print(a)
print(f2(a))
print(a)
[0, 1, 2, 3, 4]
[0, 1, 4, 9, 16]
[0, 1, 4, 9, 16]

Gestion des erreurs

Il arrive régulièrement que l'on doive gérer les erreurs d'exécution dans une fonction. On utilise pour cela l'opérateur try, qui contiendra le code à exécuter normalement. Et dans le bloc except, on indiquera la marche à suivre en cas d'erreur lors de l'exécution du try. On peut définir une suite d'instruction à réaliser après la gestion de l'erreur avec un bloc finally (optionnel).

Ci-dessous, nous définissons une fonction renvoyant la somme d'une liste ou d'un tuple. Si la somme est impossible à réaliser (par exemple, car il y a une chaîne dans la liste passée en paramètre), on affiche un message d'erreur et on renvoie la valeur None.

In [209]:
def somme(v):
    try:
        res = sum(v)
    except:
        print("Erreur : somme impossible !")
        res = None
    finally:
        return res
a = somme([1, 3, 5])
print(a)
a = somme(["un", 3, 5])
print(a)
9
Erreur : somme impossible !
None

Fonctions sur Listes

Dans le cadre de l'utilisation de liste, il est nécessaire d'utiliser des fonctions particulières, dites high order functions. Ces dernières prennent en paramètre une fonction et une liste.

Pour les exemples, nous définissons une liste de nombres.

In [210]:
a = [13, 7, 2, 9, 1, 10, 6, 3, 8]
print(a)
[13, 7, 2, 9, 1, 10, 6, 3, 8]

map()

La fonction map() permet d'appliquer une fonction sur chaque élément d'une liste (ou d'un tuple). Elle renvoie un objet de type map que l'on peut transformer en liste avec la fonction list(). Une fois celle-ci appliqué, l'objet renvoyé par map() est vide.

Ici, nous définissons une fonction carre() qui renvoie le carré du paramètre passé. Et nous l'appliquons sur chaque élément de la liste a créé précédemment.

In [211]:
def carre(v):
    return v ** 2
carre(5)
Out[211]:
25
In [212]:
b = map(carre, a)
print(list(b))
[169, 49, 4, 81, 1, 100, 36, 9, 64]

filter()

L'idée de la fonction filter() est de filtrer les valeurs de la liste, en fonction du résultat de la fonction passée en paramètre. De même que pour map(), nous listons les résultats pour l'avoir sous une forme de list.

La fonction pair() ci-dessous teste si la valeur passée en paramètre est divisible par 2 (donc si elle est paire). L'utlisation de la fonction filter() sur a, avec cette fonction, nous renvoie les valeurs de a paires.

In [213]:
def pair(v):
    return v%2 == 0
pair(5)
pair(8)
Out[213]:
True
In [214]:
b = filter(pair, a)
print(list(b))
[2, 10, 6, 8]

reduce()

La dernière fonction intéressante sur les listes est reduce(), permettant de réduire une liste en une valeur. Elle est contenue dans le module functools, qu'on importe via la commande import.

La fonction à passer en paramètre doit donc prendre deux valeurs et renvoyer une valeur de même type. Le deuxième paramètre est la liste sur laquelle appliquer la réduction. Il est aussi possible d'ajouter une valeur initiale.

Cette fonction reduce() regroupe deux éléments de liste ensemble, puis regroupe le résultat précédent avec l'élément suivant, et ainsi de suite, jsuq'à épuisement de la liste.

Voici un exemple d'utilisation de reduce() pour le calcul de la somme des éléments d'une liste. On définit la fonction somme() qui prend deux éléments et qui renvoie la somme de deux. On l'applique, via reduce() sur la liste. Le résultat obtenu est bien la somme sur a. Le deuxième appel est avec une valeur initialisée à 100.

In [215]:
def somme(v1, v2): 
    return v1 + v2
somme(3, 10)
Out[215]:
13
In [216]:
sum(a)
Out[216]:
59
In [217]:
import functools
b = functools.reduce(somme, a)
print(b)
59
In [218]:
b = functools.reduce(somme, a, 100)
print(b)
159

Fonction anonyme

Dans ces appels de fonctions, il est courant de passer en paramètre une fonction simple. Dans ce cadre, il existe un mécanisme évitant la déclaration de la fonction avant. On parle de fonction anonyme, déclarée via l'opérateur lambda. Le principe est de définir la fonction lors de son passage comme paramètre. La contrainte est que cette fonction doit tenir sur une seule ligne.

Dans l'exemple qui suit, nous appliquons une fonction calculant le carré d'une valeur à une liste, via la fonction map().

In [219]:
b = map(lambda v: v ** 2, a)
print(list(b))
[169, 49, 4, 81, 1, 100, 36, 9, 64]

Il est possible d'ajouter une condition if dans la fonction lambda avec un formalisme de type valeurTrue if condition else valeurFalse. Ci-après, nous calculons le carré de chaque valeur, multiplié par -1 pour celles inférieur ou égale à 8.

In [220]:
b = map(lambda v: v **2 if v > 8 else -(v ** 2), a)
print(list(b))
[169, -49, -4, 81, -1, 100, -36, -9, -64]

Fichiers

Le module os, importé ci-dessous, dispose de plusieurs routines dédiés à la gestion des fichiers.

In [221]:
import os

Répertoire courant

Comme dans tout langage, il est possible d'accéder aux fichiers de deux façons : soit par adressage absolu (chemin complet du fichier), soit par adressage relatif (chemin par rapport à un répertoire). Pour la deuxième solution, il est donc important de connaître la notion de répertoire courant dans python. Celui-ci est le répertoire à partir duquel python va chercher les fichiers (script, de données ou autres).

In [222]:
# connaître
os.getcwd()
Out[222]:
'/Users/fxjollois/Sites/fxjollois.github.io'
In [223]:
# changer
os.chdir("donnees")
os.getcwd()
Out[223]:
'/Users/fxjollois/Sites/fxjollois.github.io/donnees'

Lecture d'un fichier

La fonction open() permet d'ouvrir une connexion vers un fichier (en mode lecture seule par défaut). L'objet renvoyé contient une fonction de lecture de toutes les lignes en une fois (readlines()). Celle-ci renvoie une liste de chaînes de caractères, chaque élément représentant une ligne du fichier. Il faut bien évidemment fermer la connexion au fichier, avec la fonction close().

Voici comment lire les lignes du fichier Iris.txt en python.

In [224]:
fichier = open("Iris.txt")
lignesBrutes = fichier.readlines()
fichier.close
Out[224]:
<function TextIOWrapper.close>

Exercice

Le fichier contient les informations de 150 iris, répartis en 3 espèces et décrits par 4 variables. Voici quelques informations sur ce fichier.

In [225]:
lignesBrutes[0:5]
Out[225]:
["'Sepal.Length';'Sepal.Width';'Petal.Length';'Petal.Width';'Species'\n",
 "5.1;3.5;1.4;0.2;'setosa'\n",
 "4.9;3;1.4;0.2;'setosa'\n",
 "4.7;3.2;1.3;0.2;'setosa'\n",
 "4.6;3.1;1.5;0.2;'setosa'\n"]
In [226]:
len(lignesBrutes)
Out[226]:
151
  1. Télécharger le fichier et importer les lignes dans python comme ci-dessus
  2. A partir de la liste de chaînes obtenue, créer une liste de 151 listes. Chaque sous-liste contiendra les 5 informations présentes sur chaque ligne (séparées par des ";"). Supprimer les quotes ("'") et le caractère de fin de ligne ("\n").
  3. Créer une fonction permettant de transformer une chaîne en réel
    • si cette chaîne n'est pas transformable, renvoyer la telle quelle
    • Exemple : f(1.234) renverra 1.234, mais f("rien") renverra "rien"
  4. Créer une liste de 150 dictionnaires, chaque dictionnaire sera un iris, avec des champs nommés. Les noms des variables sont sur la première ligne de la liste précédemment créée.
    • Idéalement à faire en une fois avec des list comprehension
  5. Calculer pour chaque iris le rapport entre la surface d'un pétale et la surface d'un sépale. La surface de chaque dépend uniquement du produit de la largeur et de la longueur. On fera donc le rapport entre les deux produits.
  6. Créer une sous-liste ne contenant que les iris setosa.
  7. Calculer la moyenne de chaque variable.
  8. Calculer la moyenne de chaque variable pour chaque espèce.
In [ ]: