Module 2 — Structures de données et compréhensions
Quatre structures natives portent tout le travail de préparation en Python : la liste, le dictionnaire, le tuple et l'ensemble. Savoir laquelle choisir — et transformer l'une en l'autre d'une ligne — est la compétence qui sépare le code laborieux du code fluide. C'est aussi la gymnastique mentale qui rendra pandas naturel.
La liste : la séquence à tout faire
Ordonnée, modifiable, hétérogène si besoin (mais ne le faites pas sans raison) :
scores = [0.91, 0.87, 0.94, 0.79]
scores.append(0.88) # ajout en fin
scores[0] # premier élément
scores[-1] # dernier
len(scores), sum(scores) # 5, 4.39
sorted(scores, reverse=True) # nouvelle liste triée
Deux méthodes qui se confondent : sorted(liste) renvoie une copie triée ; liste.sort() trie en place et renvoie None — source d'un bug classique (resultat = liste.sort() donne None).
Le tri par clé sert constamment :
modeles = [("baseline", 0.71), ("foret", 0.86), ("boosting", 0.89)]
meilleur = max(modeles, key=lambda m: m[1]) # ('boosting', 0.89)
Le dictionnaire : des clés vers des valeurs
La structure la plus importante de Python — les DataFrames de pandas en sont conceptuellement une extension. Accès par clé en temps constant :
config = {"modele": "xgboost", "profondeur": 6, "seuil": 0.5}
config["profondeur"] # 6 — KeyError si absente
config.get("cache", False) # False — valeur par défaut si absente
config["seuil"] = 0.4 # modification
Les trois parcours :
for cle in config: # les clés
for valeur in config.values(): # les valeurs
for cle, valeur in config.items(): # les deux — le plus courant
Idiome de comptage, omniprésent en exploration de données :
compte = {}
for categorie in colonnes_categorie:
compte[categorie] = compte.get(categorie, 0) + 1
# ou, en une ligne : from collections import Counter ; Counter(colonnes_categorie)
Tuple et ensemble : les deux spécialistes
Le tuple est une séquence immuable : ce qui est figé ne peut pas être modifié par accident. On l'utilise pour les groupes de valeurs qui vont ensemble — coordonnées, paires (nom, score) — et pour les retours multiples de fonctions :
def evaluer(y_vrai, y_pred):
return precision, rappel # retourne un tuple
precision, rappel = evaluer(yt, yp) # déballage (unpacking)
L'ensemble (set) stocke des éléments uniques, sans ordre, avec un test d'appartenance instantané. Deux usages quotidiens en données :
doublons = len(identifiants) - len(set(identifiants)) # compter les doublons
colonnes_attendues = {"id", "montant", "date"}
manquantes = colonnes_attendues - set(df_colonnes) # différence d'ensembles
Le test x in ensemble est en temps constant, contre un parcours complet pour x in liste — sur des millions d'éléments, la différence se mesure en minutes.
Les compréhensions : transformer en une ligne
La compréhension de liste est la construction la plus idiomatique de Python : transformer et filtrer une séquence sans boucle explicite.
# transformer
montants_ttc = [m * 1.15 for m in montants]
# filtrer
valides = [x for x in mesures if x is not None]
# les deux
logs_erreur = [l.strip() for l in lignes if "ERROR" in l]
Le patron général : [expression for element in sequence if condition]. Il existe en version dictionnaire et ensemble :
prix_par_id = {p["id"]: p["prix"] for p in produits} # dict
extensions = {f.split(".")[-1] for f in fichiers} # set
Une compréhension est supérieure à la boucle équivalente tant qu'elle tient lisiblement sur une ligne, avec au plus un if. Une compréhension imbriquée sur trois niveaux est une boucle déguisée en énigme : écrivez la boucle. Le critère est la relecture par un collègue, pas la concision.
Ce mode de pensée — « applique cette expression à chaque élément, garde ceux qui passent le filtre » — est exactement celui de NumPy et de pandas. [m * 1.15 for m in montants] deviendra montants * 1.15 au module 4 ; le filtre deviendra un masque booléen. Les compréhensions sont l'école de la vectorisation.
Copie et références : le piège structurel
Les variables Python sont des références. Affecter ne copie pas :
a = [1, 2, 3]
b = a # b désigne LA MÊME liste
b.append(4)
a # [1, 2, 3, 4] — surprise
Pour copier réellement : b = a.copy() (copie superficielle) ou copy.deepcopy(a) pour les structures imbriquées. Ce comportement se retrouvera dans pandas, où la distinction vue/copie sur les DataFrames est une source d'avertissements célèbres (SettingWithCopyWarning, module 6).
Corollaire : ne jamais utiliser une liste comme valeur par défaut d'un paramètre (def f(x, acc=[])) — la liste est créée une seule fois et partagée entre les appels. L'idiome correct : acc=None puis if acc is None: acc = [].
Ce qu'il faut retenir
- Liste pour les séquences ordonnées, dictionnaire pour les associations clé → valeur, tuple pour les groupes figés, ensemble pour l'unicité et l'appartenance rapide.
sorted()renvoie,.sort()modifie en place ; tri par clé aveckey=.- Les compréhensions transforment et filtrent en une ligne — et entraînent le réflexe de vectorisation ; au-delà d'un
if, revenez à la boucle. - Les variables sont des références :
.copy()pour copier, jamais de mutable en défaut de paramètre.
Au module suivant : les fonctions et l'organisation du code — passer du script jetable au module réutilisable.