Aller au contenu principal

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
La limite de lisibilité

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é avec key=.
  • 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.