Aller au contenu principal

Idiomes Pandas

Un code pandas lisible expose la suite de transformations de la table et emploie l'opération disponible la plus précise.

Style de transformation

import pandas as pd

orders = pd.DataFrame({
"customer_id": ["A", "A", "B"], "status": ["paid", "paid", "pending"],
"gross": [100, 50, 80], "fee": [10, 5, 8],
})
result = (
orders.loc[lambda x: x["status"].eq("paid")]
.assign(net=lambda x: x["gross"] - x["fee"])
.groupby("customer_id", as_index=False)
.agg(total_net=("net", "sum"), orders=("net", "size"))
.sort_values("total_net", ascending=False)
)

Découpez une chaîne en étapes nommées lorsqu'elle devient difficile à examiner, réutiliser ou déboguer. Le chaînage de méthodes sert la lisibilité ; il ne garantit pas les performances.

Choisir l'opération

BesoinPréférence
Arithmétique scalaire ou opération typéeexpression vectorisée / accesseur
Faire passer chaque valeur par une table ou fonctionSeries.map / DataFrame.map
Réduire des valeurs en résumésagg
Renvoyer des valeurs de même forme que le groupe ou la colonnetransform
Appliquer une fonction aux lignes ou colonnesapply
Insérer une fonction sur la table entière dans une chaînepipe
def require_positive(frame, column):
if frame[column].isna().any() or not frame[column].ge(0).all():
raise ValueError(f"{column} must be present and non-negative")
return frame

clean = orders.pipe(require_positive, "gross")

Vectoriser avant apply

# À privilégier
df = pd.DataFrame({"numerator": [10.0, 0.0], "denominator": [2.0, 0.0]})
df["ratio"] = df["numerator"].div(df["denominator"].replace(0, float("nan")))

# À réserver à une logique Python qui dépend réellement de la ligne entière
def classify_row(row):
if pd.isna(row["ratio"]):
return "undefined"
return "high" if row["ratio"] >= 4 else "low"

df["label"] = df.apply(classify_row, axis="columns")

Un apply ligne par ligne construit une Series pour chaque ligne et invoque Python à répétition. Ne mesurez les performances qu'après avoir choisi une expression correcte et claire, puis l'avoir testée sur des données représentatives.

Règle de mutation

Préférez les expressions qui renvoient de nouveaux objets et les affectations .loc en une étape. Évitez l'affectation chaînée et ne comptez pas sur inplace=True comme optimisation.

Suivre l’objet renvoyé

La chaîne renvoie une ligne : client A, total_net=135, orders=2. orders garde ses trois lignes sans colonne net. pipe renvoie exactement le résultat de sa fonction. Ici, require_positive valide puis renvoie la table reçue : clean.equals(orders) est vrai ; aucune valeur n’est nettoyée. Ne comptez pas sur l’identité des objets entre versions : pandas 3.0 transmet une copie superficielle à la fonction.

L’exemple de ratio remplace explicitement le dénominateur nul par une valeur manquante, donnant [5.0, NaN] et les étiquettes high, undefined. Sans règle, une division non nulle par zéro peut produire un infini. Ce petit classificateur illustre seulement le contrat par ligne ; cette logique simple peut aussi être vectorisée. DataFrame.map nécessite pandas 2.1 ou ultérieur. DataFrame.apply traite les colonnes par défaut (axis=0), les lignes avec axis=1 ; la forme du résultat dépend du retour de la fonction.

Sources

Explorer les liensOuvrir le réseau