
Pourquoi devriez-vous arrêter d’écrire des boucles dans Pandas
: quand j’ai commencé à utiliser Pandas, j’écrivais tout le temps des boucles comme celle-ci :
for i in range(len(df)):
if df.loc[i, "sales"] > 1000:
df.loc[i, "tier"] = "high"
else:
df.loc[i, "tier"] = "low"
Cela a fonctionné. Et j’ai pensé, « Hé, ça va, non? »
Il s’avère que… pas tellement.
Je ne m’en rendais pas compte à l’époque, mais les boucles comme celle-ci sont un piège classique pour les débutants. Ils obligent les Pandas à faire beaucoup plus de travail que nécessaire, et ils intègrent un modèle mental qui vous permet de réfléchir ligne par ligne plutôt que colonne par colonne.
Une fois que j’ai commencé à réfléchir colonnesles choses ont changé. Le code est devenu plus court. L’exécution est devenue plus rapide. Et soudain, Pandas eut l’impression qu’il avait été construit pour aide-moine me ralentis pas.
Pour montrer cela, utilisons un petit ensemble de données auquel nous ferons référence tout au long :
import pandas as pd
df = pd.DataFrame({
"product": ["A", "B", "C", "D", "E"],
"sales": [500, 1200, 800, 2000, 300]
})
Sortir:
product sales
0 A 500
1 B 1200
2 C 800
3 D 2000
4 E 300
Notre objectif est simple : étiquetez chaque ligne comme high si les ventes sont supérieures à 1000, sinon low.
Laisse-moi te montrer comment j’ai fait d’abordet pourquoi il existe un meilleur moyen.
L’approche en boucle avec laquelle j’ai commencé
Voici la boucle que j’ai utilisée lors de mon apprentissage :
for i in range(len(df)):
if df.loc[i, "sales"] > 1000:
df.loc[i, "tier"] = "high"
else:
df.loc[i, "tier"] = "low"
print(df)
Il produit ce résultat :
product sales tier
0 A 500 low
1 B 1200 high
2 C 800 low
3 D 2000 high
4 E 300 low
Et oui, ça marche. Mais voici ce que j’ai appris à mes dépens :
Les pandas font une petite opération pour chaque ligneau lieu de gérer efficacement toute la colonne en même temps.
Cette approche n’est pas évolutive : ce qui semble correct avec 5 lignes ralentit avec 50 000 lignes.
Plus important encore, cela vous permet de réfléchir comme un débutant – ligne par ligne – plutôt que comme un utilisateur professionnel de Pandas.
Chronométrer la boucle (le moment où j’ai réalisé que c’était lent)
Lorsque j’ai exécuté ma boucle pour la première fois sur ce petit ensemble de données, j’ai pensé : « Pas de problème, c’est assez rapide. » Mais ensuite je me suis demandé… et si j’avais un plus grand ensemble de données ?
Alors j’ai essayé :
import pandas as pd
import time
# Make a bigger dataset
df_big = pd.DataFrame({
"product": ["A", "B", "C", "D", "E"] * 100_000,
"sales": [500, 1200, 800, 2000, 300] * 100_000
})
# Time the loop
start = time.time()
for i in range(len(df_big)):
if df_big.loc[i, "sales"] > 1000:
df_big.loc[i, "tier"] = "high"
else:
df_big.loc[i, "tier"] = "low"
end = time.time()
print("Loop time:", end - start)
Voici ce que j’ai obtenu :
Loop time: 129.27328729629517
C’est 129 secondes.
Sur deux minutes juste pour étiqueter les lignes comme "high" ou "low".
C’est à ce moment-là que ça a cliqué pour moi. Le code n’était pas seulement « un peu inefficace ». Il s’agissait fondamentalement d’utiliser Pandas dans le mauvais sens.
Et imaginez que cela s’exécute dans un pipeline de données, dans une actualisation de tableau de bord, sur des millions de lignes chaque jour.
Pourquoi c’est si lent
La boucle oblige les Pandas à :
- Accédez à chaque ligne individuellement
- Exécuter la logique au niveau Python pour chaque itération
- Mettre à jour le DataFrame une cellule à la fois
En d’autres termes, il transforme un moteur de colonnes hautement optimisé en un processeur de liste Python glorifié.
Et ce n’est pas pour cela que Pandas est conçu.
Le correctif en une seule ligne (et le moment où il a cliqué)
Après avoir vu 129 secondesje savais qu’il devait y avoir une meilleure façon.
Ainsi, au lieu de parcourir les lignes en boucle, j’ai essayé d’exprimer la règle au niveau du niveau de colonne:
« Si les ventes sont supérieures à 1 000, étiquetez « élevé ». Sinon, étiquetez « faible ».
C’est ça. C’est la règle.
Voici la version vectorisée :
import numpy as np
import time
start = time.time()
df_big["tier"] = np.where(df_big["sales"] > 1000, "high", "low")
end = time.time()
print("Vectorized time:", end - start)
Et le résultat ?
Vectorized time: 0.08
Laissez cela pénétrer.
Version en boucle : 129 secondes
Version vectorisée : 0,08 seconde
C’est fini 1 600 fois plus rapide.
Que vient-il de se passer ?
La principale différence est la suivante :
La boucle a traité le DataFrame rangée par rangée. La version vectorisée a traité l’intégralité sales colonne en une seule opération optimisée.
Quand tu écris :
df_big["sales"] > 1000
Pandas ne vérifie pas les valeurs une par une en Python. Il effectue la comparaison à un niveau inférieur (via NumPy), en code compilé, sur l’ensemble du tableau.
Alors np.where() applique les étiquettes en un seul passage efficace.
Voici le changement subtil mais puissant :
Au lieu de demander :
« Que dois-je faire de cette rangée ? »
Vous demandez :
« Quelle règle s’applique à cette colonne ? »
C’est la limite entre les Pandas débutants et les Pandas professionnels.
À ce stade, je pensais avoir « monté de niveau ». Puis j’ai découvert que je pouvais le rendre encore plus simple.
Et puis j’ai découvert l’indexation booléenne
Après avoir chronométré la version vectorisée, je me suis senti assez fier. Mais ensuite j’ai eu une autre prise de conscience.
je n’ai même pas besoin np.where() pour ça.
Revenons à notre petit ensemble de données :
df = pd.DataFrame({
"product": ["A", "B", "C", "D", "E"],
"sales": [500, 1200, 800, 2000, 300]
})
Notre objectif est toujours le même :
Étiquetez chaque ligne high si ventes > 1000, sinon low.
Avec np.where() nous avons écrit :
df["tier"] = np.where(df["sales"] > 1000, "high", "low")
C’est plus propre et plus rapide. Bien mieux qu’une boucle.
Mais voici la partie qui a vraiment changé ma façon de penser les Pandas :
Cette ligne ici…
df["sales"] > 1000
… renvoie déjà quelque chose d’incroyablement utile.
Regardons-le :
Sortir:
0 False
1 True
2 False
3 True
4 False
Name: sales, dtype: bool
C’est une série booléenne.
Les pandas viennent d’évaluer la condition de la colonne entière à la fois.
Pas de boucle. Non if. Pas de logique ligne par ligne.
Il a produit un masque complet de valeurs Vrai/Faux en une seule fois.
L’indexation booléenne ressemble à un super pouvoir
Maintenant, c’est là que cela devient intéressant.
Vous pouvez utiliser ce masque booléen directement pour filtrer les lignes :
df[df["sales"] > 1000]
Et Pandas vous offre instantanément :

Nous pouvons même construire le tier colonne utilisant directement l’indexation booléenne :
df["tier"] = "low"
df.loc[df["sales"] > 1000, "tier"] = "high"
Je dis en gros :
- Supposons que tout est
"low". - Remplacez uniquement les lignes dont les ventes sont supérieures à 1 000.
C’est ça.
Et du coup, je ne pense plus :
« Pour chaque ligne, vérifiez la valeur… »
Je pense :
« Commencez par une valeur par défaut. Appliquez ensuite une règle à un sous-ensemble. »
Ce changement est subtil, mais il change tout.
Une fois que je me suis familiarisé avec les masques booléens, j’ai commencé à me demander :
Que se passe-t-il lorsque la logique n’est pas aussi claire que « supérieur à 1 000 » ? Que faire si j’ai besoin de règles personnalisées ?
C’est là que j’ai découvert apply(). Et au début, c’était comme le meilleur des deux mondes.
N’est-ce pas apply() Suffisant?
Je vais être honnête. Après avoir arrêté d’écrire des boucles, je pensais avoir tout compris. Parce qu’il y avait cette fonction magique qui semblait tout résoudre :apply().
Cela semblait être le juste milieu entre des boucles désordonnées et une vectorisation effrayante.
Alors naturellement, j’ai commencé à écrire des choses comme ceci :
df["tier"] = df["sales"].apply(
lambda x: "high" if x > 1000 else "low"
)
Et à première vue ?
Cela a l’air génial.
- Non
forboucle - Pas d’indexation manuelle
- Facile à lire
Il se sent comme une solution professionnelle.
Mais voici ce que je n’ai pas compris à l’époque :
apply() exécute toujours du code Python pour chaque ligne.
Cela cache juste la boucle.
Lorsque vous utilisez :
df["sales"].apply(lambda x: ...)
Pandas c’est toujours :
- Prendre chaque valeur
- Le passer dans une fonction Python
- Renvoyer le résultat
- Répéter cela pour chaque ligne
C’est plus propre qu’un for boucle, oui. Mais niveau performances ? C’est beaucoup plus proche d’une boucle que d’une véritable vectorisation.
Cela a été un peu un signal d’alarme pour moi. J’ai réalisé que je remplaçais les boucles visibles par des boucles invisibles.
Alors, quand devriez-vous utiliser apply()?
- Si la logique peut être exprimée avec des opérations vectorisées → faites-le.
- Si cela peut être exprimé avec des masques booléens → faites-le.
- Si cela nécessite absolument une logique Python personnalisée → alors utilisez
apply().
Autrement dit:
Vectorisez d’abord. Atteindre apply()only quand il le faut.
Pas parce que apply() c’est mauvais. Mais parce que Pandas est le plus rapide et le plus propre lorsque vous pensez en colonnes, pas en fonctions par lignes.
Conclusion
Avec le recul, la plus grosse erreur que j’ai commise a été de ne pas écrire de boucles. On supposait que si le code fonctionnait, il était suffisant.
Les pandas ne vous punissent pas immédiatement pour avoir réfléchi en rangées. Mais à mesure que vos ensembles de données grandissent, que vos pipelines évoluent et que votre code se retrouve dans des tableaux de bord et des workflows de production, la différence devient évidente.
- La réflexion ligne par ligne n’est pas évolutive.
- Les boucles Python cachées ne sont pas mises à l’échelle.
- Les règles au niveau des colonnes le font.
C’est la véritable frontière entre l’utilisation débutante et professionnelle de Pandas.
Donc en résumé :
Arrêtez de demander quoi faire avec chaque ligne. Commencez à demander quelle règle s’applique à la colonne entière.
Une fois ce changement effectué, votre code devient plus rapide, plus propre, plus facile à réviser et à maintenir. Et vous commencez instantanément à repérer des modèles inefficaces, y compris les vôtres.



