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
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 liens
Cette note n’a pas encore de liens vers d’autres notes.