Les erreurs de code ne sont pas l’exception en Data Science : elles sont le quotidien. Et certaines ont changé de nature en 2026 — depuis pandas 3.0, du code qui tournait parfaitement se met à lever des exceptions. Ce qui sépare encore un script de débutant d’un projet professionnel n’est d’ailleurs plus la syntaxe, mais l’outillage. Voici dix erreurs fréquentes, corrigées, avec du code qui s’exécute réellement aujourd’hui.
Pourquoi les erreurs sont normales en apprentissage
Quand on apprend Python, on tombe forcément sur des erreurs. Et c’est tant mieux : ce sont elles qui obligent à réfléchir, à chercher, à comprendre. Elles font partie intégrante du processus d’apprentissage. Je dis souvent à mes apprenants d’apprendre à les aimer, car elles nous challengent — et on en ressort avec de nouvelles compétences.
Une nuance à poser avant de commencer : la répartition des erreurs n’est pas celle qu’on imagine. Dans une étude menée sur les programmes d’étudiants en première année d’informatique, les erreurs de syntaxe représentent environ 30 à 50 % des cas, tandis que les NameError — l’erreur « nom non défini » — se situent autour de 12 à 24 % [1]. Autrement dit : NameError est la première erreur d’exécution, pas l’erreur la plus fréquente au global. Le mur que l’on rencontre en premier, quand on débute, c’est la syntaxe. Une bonne nouvelle : c’est aussi la plus facile à corriger.
Les 10 erreurs fréquentes (et leurs corrections)
Chaque erreur est présentée avec le code fautif, le code corrigé et la raison du changement. Les exemples utilisent les versions de septembre 2026 : pandas 3.0 et scikit-learn.
1. Oublier d’importer les bibliothèques
C’est l’erreur la plus basique, mais tout le monde la fait. Vous lancez votre code et vous voyez apparaître :
NameError: name 'pd' is not defined
Pourquoi ? Parce que vous utilisez pd.DataFrame sans avoir importé pandas au préalable :
data = {'Nom': ['Alice', 'Bob'], 'Age': [25, 30]}
df = pd.DataFrame(data)
# NameError: name 'pd' is not defined
La correction tient en une ligne, à placer en haut du script ou du notebook :
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
Bonne pratique. Regroupez tous vos imports en tête de fichier. Créez-vous un bloc d’imports standard que vous copiez au début de chaque projet. Le corollaire est moins évident : ne réimportez pas une bibliothèque au milieu du code. Un import tardif signale presque toujours que le code aurait dû être découpé en fonctions — c’est le point 5.
Conseil de pro — Un import oublié se corrige en dix secondes. Le vrai coût, c’est le temps passé à chercher pourquoi votre variable s’appelle
dfdans une cellule etdatadans la suivante. Une convention de nommage stable vaut mieux qu’un raccourci.
2. Confondre listes, arrays NumPy et DataFrames
Chaque structure a son usage :
- Liste Python — pour de petites collections hétérogènes.
- Array NumPy — pour les calculs numériques vectorisés et rapides.
- DataFrame Pandas — pour les données tabulaires, avec colonnes nommées et index.
Un exemple qui montre que la différence n’est pas seulement une question de performance :
import numpy as np
notes = [12, 15, 17]
print(sum(notes) / len(notes)) # 14.666666666666666
notes_np = np.array([12, 15, 17])
print(notes_np.mean()) # 14.666666666666666
print(notes_np * 10) # [120 150 170]
La moyenne de [12, 15, 17] n’est pas 14,6 : c’est 14,666…, soit 14,67 arrondi à deux décimales, ou 14,7 à une décimale. Le détail n’en est pas un : en Data Science, un arrondi affiché trop tôt dans une chaîne de calcul se propage et fausse les comparaisons.
Attention aussi à la multiplication. notes * 2 sur une liste Python la duplique — [12, 15, 17, 12, 15, 17] — alors que notes_np * 2 multiplie chaque valeur. C’est exactement le genre de confusion qui produit des résultats silencieusement faux.
Choisissez toujours la structure en fonction de ce que vous voulez faire, pas par habitude.
3. Ne pas vérifier les valeurs manquantes
C’est l’une des erreurs les plus fréquentes : ignorer les valeurs manquantes (NaN). Elles sont pourtant courantes — un client qui n’a pas renseigné son âge, une transaction sans date. Non traitées, elles faussent les calculs.
Un modèle scikit-learn lancé sur un tableau contenant des NaN s’arrête sur un message précis :
ValueError: Input contains NaN.
Ce message vient de _assert_all_finite, appelé par check_array avec ensure_all_finite=True par défaut. Et scikit-learn fait mieux que constater le problème : quand un estimateur refuse les NaN, il ajoute lui-même la marche à suivre, en citant HistGradientBoostingClassifier et HistGradientBoostingRegressor comme modèles qui les acceptent nativement, et en renvoyant vers la page sur l’imputation [5].
Nuance importante. Dire que « les algorithmes n’acceptent pas de données manquantes » est une généralisation fausse. HistGradientBoostingRegressor et HistGradientBoostingClassifier gèrent nativement les NaN, et check_array expose un contrôle explicite pour les autoriser — ensure_all_finite="allow-nan", nommé force_all_finite avant scikit-learn 1.6 [5]. Le bon réflexe n’est donc pas « il faut toujours imputer », mais « il faut savoir si ce modèle-ci accepte les valeurs manquantes ».
Le piège pandas 3.0 : le code qui marchait ne marche plus
Voici le code qu’on trouvait partout, y compris dans beaucoup de tutoriels encore en ligne :
# pandas < 3.0 — fonctionnait, avec un avertissement
df['salaire'].fillna(df['salaire'].median(), inplace=True)
df['ville'].fillna('Inconnue', inplace=True)
Depuis pandas 3.0, ce code lève pandas.errors.ChainedAssignmentError [3]. La raison est documentée noir sur blanc : avec le copy-on-write désormais toujours actif, « chained assignment can never work », car on écrit dans un objet temporaire — le résultat d’une opération d’indexation — qui « always behaves as a copy » [3] [2]. Écrire dans df['salaire'] ne modifie donc plus le DataFrame d’origine.
Les deux corrections valables :
# 1. Réaffecter explicitement la colonne
df['salaire'] = df['salaire'].fillna(df['salaire'].median())
df['ville'] = df['ville'].fillna('Inconnue')
# 2. Ou passer un dictionnaire à fillna sur le DataFrame entier
df = df.fillna({'salaire': df['salaire'].median(), 'ville': 'Inconnue'})
Le réflexe à garder : en pandas 3.0, on n’écrit plus « en place » sur une colonne, on réaffecte.
Bonnes pratiques, dans l’ordre : détecter avec df.isnull().sum() — le nombre de valeurs manquantes par colonne ; décider selon le contexte (supprimer, imputer par la médiane pour les numériques et le mode pour les catégorielles, ou créer une catégorie « Inconnue ») ; documenter votre choix. Pour industrialiser l’imputation dans un pipeline, SimpleImputer de scikit-learn fait le travail proprement — et s’ajuste sur le train uniquement [5].
Conseil de pro — Ne traitez pas toutes les colonnes de la même manière. Une valeur manquante dans
agen’a pas le même impact que danscode_postal. Analysez avant d’agir, et écrivez quelque part pourquoi vous avez tranché.
4. Mal gérer les types de variables
Ne pas vérifier les types est une source fréquente de calculs faux, de modèles qui plantent ou de résultats incohérents. Le premier réflexe est de regarder ce que pandas a déduit :
df.dtypes
Ce que pandas 3.0 change ici est majeur. Les colonnes texte n’ont plus le dtype object : elles utilisent un nouveau dtype str par défaut, adossé à PyArrow quand il est disponible, sinon à NumPy [4]. Conséquence directe : le test df.dtypes == object, vrai pendant des années, renvoie désormais False et ne détecte plus vos colonnes texte. Autre conséquence : les valeurs manquantes d’une colonne texte sont toujours NaN ; la distinction entre None et NaN disparaît [4].
# pandas 2.x : 'ville' était en dtype object
# pandas 3.0 : 'ville' est en dtype str
df['ville'].dtype # -> str, et non plus object
# Ce test ne détecte plus les colonnes texte
df.dtypes == object # -> False
Vient ensuite la conversion des types. Un piège classique, et directement lié au point précédent :
# Échoue si la colonne contient encore des NaN
df['revenu'] = df['revenu'].astype(int)
# ValueError — conversion impossible tant qu'il reste des valeurs manquantes
Deux solutions, selon ce que vous voulez faire des valeurs manquantes :
# 1. Imputer d'abord, convertir ensuite
df['revenu'] = df['revenu'].fillna(df['revenu'].median())
df['revenu'] = df['revenu'].astype(int)
# 2. Ou conserver les manquants avec un entier nullable
df['revenu'] = df['revenu'].astype('Int64')
Le dtype Int64 — avec un I majuscule — est un entier qui accepte les valeurs manquantes, ce que int64 ne sait pas faire. C’est la bonne option quand « manquant » a un sens métier qu’on ne veut pas écraser en le remplaçant par une médiane.
Deux autres conversions à connaître :
# Dates lues comme du texte
df['date'] = pd.to_datetime(df['date'])
# Booléens déguisés en texte
df['achat'] = df['achat'].map({'Oui': True, 'Non': False})
Sur les dates, un détail de pandas 3.0 qui se paie cher : les chaînes sont désormais analysées en microsecondes, avec repli sur les nanosecondes si nécessaire. La documentation officielle prévient : « One big exception is converting to integers, which will give integers 1000x smaller » [2]. Si vous convertissez une colonne de dates en entier, vérifiez l’unité avant de réutiliser les valeurs ailleurs.
Conseil de pro — Avant toute analyse, un
df.dtypeset undf.head(). Deux lignes, dix secondes, et vous corrigez immédiatement les incohérences les plus coûteuses.
5. Écrire du code sans fonction ni structure
Quand on débute, on répète les mêmes lignes. Ça fonctionne au début, mais le code devient vite illisible, difficile à maintenir et source d’erreurs.
# Avant — trois fois la même logique
df['age'] = df['age'].fillna(df['age'].median())
df['revenu'] = df['revenu'].fillna(df['revenu'].median())
df['score'] = df['score'].fillna(df['score'].median())
# Après — une fonction, des annotations de type, un docstring
def imputer_median(df: pd.DataFrame, colonne: str) -> pd.DataFrame:
"""Renvoie une copie du DataFrame avec la colonne imputée par sa médiane."""
resultat = df.copy()
resultat[colonne] = resultat[colonne].fillna(resultat[colonne].median())
return resultat
for colonne in ['age', 'revenu', 'score']:
df = imputer_median(df, colonne)
Trois gains immédiats : la logique n’existe qu’à un seul endroit, elle porte un nom lisible, et df.copy() évite de modifier l’objet reçu. Cet effet de bord — une fonction qui change discrètement son argument — provoque des bugs parmi les plus difficiles à traquer.
Conseil de pro — Des noms clairs, des annotations de type et un docstring. Dans six mois, vous serez reconnaissant·e envers « le vous du passé ».
6. Copier-coller du code sans comprendre
Chercher une solution sur un forum ou avec un assistant IA est une excellente habitude. L’erreur, c’est de copier sans lire ni comprendre : dès qu’un détail change dans votre projet, tout casse.
L’exemple canonique est le StandardScaler, et c’est aussi l’une des fautes les plus coûteuses en machine learning :
# Correct sur l'entraînement
X_train_scaled = scaler.fit_transform(X_train)
# FAUX sur le test : recalcule moyenne et écart-type sur le test
X_test_scaled = scaler.fit_transform(X_test)
# CORRECT sur le test
X_test_scaled = scaler.transform(X_test)
Le principe : fit apprend des paramètres, transform les applique. Le fit ne doit voir que les données d’entraînement — sinon les statistiques du test entrent dans le train et votre score de validation devient optimiste, donc faux.
Le bon réflexe en 2026 : laisser le Pipeline éviter cette erreur par construction. Un pipeline scikit-learn enchaîne prétraitement et modèle, et n’ajuste l’ensemble de la chaîne que sur le train ; sur le test, il n’applique que la transformation [6]. L’erreur devient structurellement impossible :
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
modele = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))
modele.fit(X_train, y_train) # ajustement sur le train
score = modele.score(X_test, y_test) # transform puis predict sur le test
Conseil de pro — Pas besoin de tout comprendre en profondeur immédiatement. Mais saisissez toujours l’intention du code, et testez-le sur un mini-exemple avant de l’appliquer à votre vrai jeu de données.
7. Négliger la visualisation des données
Vouloir « faire tourner un modèle » le plus vite possible, sans regarder ses données, est une erreur classique. La visualisation est l’étape clé qui permet de comprendre le jeu de données avant toute analyse ou modélisation.
# La moyenne semble normale
df['revenu'].describe()
# Le boxplot, lui, montre la valeur aberrante
import seaborn as sns
sns.boxplot(x=df['revenu'])
Commencez toujours par un EDA (Exploratory Data Analysis) simple : histogramme des variables numériques, décompte des modalités catégorielles, matrice de corrélation. Trois ou quatre graphiques bien choisis suffisent — vous cherchez des ordres de grandeur et des anomalies, pas une figure pour une publication.
Conseil de pro — La visualisation n’est pas une étape décorative. C’est comme prendre une carte avant une randonnée : sans elle, vous avancez, mais vous ne savez pas où vous allez.
8. Sur-apprendre sans validation croisée
L’overfitting arrive quand le modèle « apprend par cœur » les données d’entraînement. Il est excellent sur le train, catastrophique sur de nouvelles données. Le signe classique : un score très élevé à l’entraînement, très moyen sur le test.
La solution est la validation croisée. Attention à une imprécision qu’on lit partout : avec cross_val_score et un modèle de régression, la métrique par défaut est le R², pas une « précision » [7]. Nommer la métrique, c’est pouvoir l’interpréter — et l’expliciter évite de croire que l’on compare un taux de bonnes réponses :
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score
modele = LinearRegression()
scores = cross_val_score(modele, X, y, cv=5, scoring='r2')
print(f"R² moyen : {scores.mean():.3f}")
print(f"Écart-type : {scores.std():.3f}")
Le jeu de données est découpé en 5 parties ; le modèle est entraîné et évalué 5 fois sur des portions différentes. L’écart-type des scores est aussi informatif que la moyenne : un R² moyen correct accompagné d’un écart-type élevé signale un modèle instable.
Conseil de pro — Ne soyez pas impressionné·e par un score très élevé à l’entraînement. Soyez méfiant·e : c’est souvent le signe d’un modèle qui sur-apprend. Le bon modèle garde des performances solides sur des données qu’il n’a jamais vues.
Le piège voisin : la fuite de données
La fuite de données (data leakage) consiste à laisser une information du test — ou de la cible — entrer dans l’entraînement. Elle produit des scores brillants en validation et décevants en production. Deux formes courantes :
# FUITE 1 : mise à l'échelle avant le découpage.
# Le scaler voit le test, donc le train "connaît" déjà le test.
X_scaled = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, random_state=42)
# CORRECT : découper d'abord, ajuster sur le train seulement.
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# FUITE 2 : imputer avec la médiane de tout le jeu de données.
# La médiane doit être calculée sur le train, puis appliquée au test.
mediane = X_train['age'].median()
X_train['age'] = X_train['age'].fillna(mediane)
X_test['age'] = X_test['age'].fillna(mediane)
Fixer les graines aléatoires
Un découpage aléatoire sans graine change à chaque exécution : vos scores bougent, et vous ne savez plus si une variation vient de votre modification ou du hasard. random_state rend l’expérience reproductible.
from sklearn.model_selection import KFold, train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
cv = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(modele, X, y, cv=cv)
Fixer random_state ne rend pas un résultat « vrai ». Cela rend la comparaison possible : deux versions de votre code, exécutées avec la même graine, ne diffèrent plus que par ce que vous avez changé.
9. Utiliser Jupyter sans versionner son code
Beaucoup de débutants se retrouvent avec des fichiers aux noms improbables :
analyse.ipynb
analyse_final.ipynb
analyse_final2.ipynb
analyse_final_version_corrigee.ipynb
Très vite, on ne sait plus lequel est le bon. Et revenir en arrière devient presque impossible. La solution tient en quatre commandes :
git init
git add .
git commit -m "Ajout de l'analyse exploratoire"
git push origin main
Vous avez une version claire, sauvegardée, et la possibilité de revenir en arrière. Deux précautions avec les notebooks : videz les sorties avant de committer — elles alourdissent le fichier et polluent les diffs — et complétez toujours par un README.md qui dit ce que fait le projet et comment l’exécuter.
Conseil de pro — Même si vous travaillez seul·e, versionnez. C’est une sécurité, une clarté, et une preuve de professionnalisme que vous pouvez montrer sur votre profil GitHub.
10. Ne pas documenter ni commenter
Un code sans explications, c’est une carte sans légende. Sur le moment, on croit qu’on s’en souviendra ; quelques semaines plus tard, on relit son notebook sans comprendre ce qu’on avait voulu faire.
# Avant — que fait ce code ?
df2 = df1[df1['x'] > 10]
df3 = df2.groupby('cat').mean()
df3.to_csv('result.csv')
# Après — l'intention est explicite
# On ne garde que les observations où 'x' dépasse 10
df2 = df1[df1['x'] > 10]
# Moyenne de chaque colonne numérique, par catégorie
df3 = df2.groupby('cat').mean()
# Export pour le rapport
df3.to_csv('result.csv')
Commentez l’intention, pas l’évidence. Et pour les fonctions, préférez un docstring à un commentaire posé au-dessus :
def taux_achat(df: pd.DataFrame) -> float:
"""Calcule la part de lignes achetées, rapportée à l'effectif total."""
return df['achat'].mean()
La différence n’est pas cosmétique : un docstring est accessible depuis help() et par votre éditeur, un commentaire non. C’est exactement le genre de détail qui fait la différence quand quelqu’un d’autre — ou vous, dans six mois — reprend le code.
Conseil de pro — Commentez l’intention. Ajoutez un
README.mddans chaque projet. Documenter votre code, c’est un cadeau que vous faites à votre futur vous.
Les pièges de pandas 3.0 à connaître
pandas 3.0 est sorti le 21 janvier 2026, et il casse du code qui tournait depuis des années. Ce n’est pas un accident de parcours : c’est la version qui active le copy-on-write par défaut, avec la règle annoncée dans la documentation — tout sous-ensemble renvoyé « always behaves as a copy of the original » [2]. Voici les changements qui vous concernent directement.
| Sujet | Avant | Depuis pandas 3.0 |
|---|---|---|
| Dtype des colonnes texte | object | str — PyArrow si disponible, sinon NumPy [4] |
| Valeurs manquantes en texte | None et NaN distincts | toujours NaN [4] |
Écriture dans une colonne avec inplace=True | modifiait l’original | lève ChainedAssignmentError [3] |
Méthodes en place : replace(), fillna(), ffill(), bfill(), interpolate(), where(), mask(), clip() | renvoyaient None | renvoient self [2] |
| Analyse des chaînes de dates | nanosecondes | microsecondes, repli nanosecondes si nécessaire [2] |
SettingWithCopyWarning | avertissement déclenché à l’écriture | supprimé [2] |
À noter aussi : pandas 3.0 exige Python ≥ 3.11 et NumPy ≥ 1.26 [2]. Sur un environnement plus ancien, l’installation échoue avant même l’exécution du premier script — d’où l’intérêt, encore une fois, de figer ses versions.
Le changement le plus déroutant est celui des méthodes « en place ». Beaucoup de tutoriels — dont les miens à l’époque — enseignaient inplace=True comme bonne pratique, parce que cela « évitait de réaffecter ». Depuis pandas 3.0, ces méthodes renvoient self au lieu de None [2] :
# pandas < 3.0 : renvoyait None
resultat = df.fillna(0, inplace=True) # resultat vaut None
# pandas >= 3.0 : renvoie le DataFrame lui-même
resultat = df.fillna(0) # forme à privilégier : explicite, sans ambiguïté
La conséquence pratique est simple : inplace=True n’apporte plus rien. Écrivez df = df.fillna(0) — la réaffectation rend le comportement visible à la lecture, et vous saurez toujours quel objet vous manipulez. C’est aussi la correction du point 3 : réaffecter plutôt que modifier en place.
L’outillage : ce qui sépare un script d’un projet
C’est le point qu’on ne voit jamais dans les tutoriels, et pourtant : en 2026, ce qui distingue un code de débutant d’un code professionnel n’est pas la connaissance de pandas, c’est l’outillage autour. Cinq outils suffisent, et ils s’installent en quelques minutes.
uv : un seul outil à la place de six
uv remplace pip, pip-tools, pipx, poetry, pyenv et virtualenv — c’est la formulation de sa documentation officielle [8]. Un environnement virtuel par projet n’est pas une option : c’est ce qui évite qu’une mise à jour de bibliothèque casse un projet voisin.
# Créer un projet et son environnement
uv init mon-projet
cd mon-projet
# Ajouter une dépendance (écrite dans pyproject.toml)
uv add pandas scikit-learn
# Ajouter des outils de développement
uv add --dev pytest ruff mypy
# Exécuter un script dans l'environnement du projet
uv run python analyse.py
Le fichier central est pyproject.toml, qui suit la norme PEP 621 :
[project]
name = "mon-projet"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"pandas>=3.0",
"scikit-learn>=1.5",
]
[dependency-groups]
dev = [
"pytest",
"ruff",
"mypy",
]
Ce fichier est votre documentation d’installation : il dit la version de Python et les dépendances du projet. Le fichier uv.lock, à committer, fige les versions exactes pour que le projet s’installe à l’identique sur une autre machine — et dans six mois sur la vôtre.
ruff : le linter et le formateur
ruff réunit le lint et le formatage dans un seul outil, là où il fallait auparavant combiner plusieurs binaires et plusieurs fichiers de configuration [9].
uv run ruff check . # signale les problèmes
uv run ruff check . --fix # corrige ce qui peut l'être
uv run ruff format . # formate le code
L’intérêt n’est pas la discipline pour elle-même : un linter attrape des bugs réels — variable non utilisée, import oublié, comparaison suspecte, f-string mal formée — avant l’exécution. C’est du temps de débogage en moins, sur les erreurs les plus bêtes et les plus fréquentes.
pytest : trois tests valent mieux que dix relectures
Un test, c’est du code qui vérifie votre code. Le framework de test pytest s’installe en une commande et se lance sans configuration [10]. Sur la fonction d’imputation du point 5, cela donne :
import pandas as pd
from analyse import imputer_median
def test_imputer_median_remplace_les_manquants():
df = pd.DataFrame({'age': [20.0, None, 40.0]})
resultat = imputer_median(df, 'age')
assert resultat['age'].isna().sum() == 0
assert resultat['age'].iloc[1] == 30.0
uv run pytest
La règle simple : chaque fois que vous corrigez un bug, écrivez le test qui échouait avant la correction. Vous ne le réintroduirez jamais — et vous pourrez modifier votre code sans peur.
mypy : vérifier les types avant l’exécution
Les annotations de type ne servent pas qu’à documenter. Un vérificateur statique comme mypy lit ces annotations et signale les incohérences sans exécuter le code [11] : passer un DataFrame là où une chaîne est attendue, oublier de gérer un None, appeler une méthode qui n’existe pas.
uv run mypy analyse.py
Sur un notebook d’exploration, le typage complet est superflu. Sur du code qui part en production — un pipeline réutilisé chaque semaine, une API interne — c’est ce qui attrape les erreurs que les tests ne couvrent pas, parce qu’on n’avait pas imaginé le cas.
pre-commit : automatiser plutôt que se souvenir
Dernière pièce : pre-commit exécute ces outils automatiquement avant chaque commit. Plus besoin de se rappeler de lancer le linter — s’il reste une erreur, le commit est refusé. C’est le point d’entrée habituel d’une chaîne de qualité de code, et il transforme une bonne intention en garde-fou permanent.
Une nuance de gouvernance à connaître
Si je recommande uv et ruff, il faut le faire en connaissance de cause : le 19 mars 2026, OpenAI a annoncé l’acquisition d’Astral, l’entreprise qui développe uv, Ruff et ty. Astral précise que l’accord n’était pas encore finalisé à la publication et s’engage à poursuivre le développement de ses outils open source une fois la transaction close : « OpenAI will continue supporting our open source tools after the deal closes » [12].
Est-ce un risque ? Les outils restent open source, donc forkables : même dans le scénario le plus défavorable, la communauté peut reprendre le flambeau. Mais le rythme de développement et la feuille de route d’un outil aussi central dans la chaîne Python ne dépendent plus d’une entreprise indépendante. C’est un sujet de débat légitime dans la communauté, et vous avez le droit d’en tenir compte — y compris en gardant venv et pip pour un projet où la stabilité prime sur la modernité de l’outillage.
Transformer ses erreurs en apprentissage durable
Faire des erreurs en Python, ce n’est pas un échec. C’est une étape normale, presque obligatoire, dans l’apprentissage. Chaque bug, chaque blocage est une chance d’apprendre un peu plus.
Ce qui change en 2026, c’est la nature des erreurs qui coûtent cher. Une faute de syntaxe se corrige en trente secondes avec un bon message. Une fuite de données, un fillna écrit à l’ancienne, un environnement non figé : celles-là ne se voient pas tout de suite, et se paient en semaines.
Trois réflexes suffisent à changer d’échelle : vérifier ses données et ses types avant de modéliser ; figer son environnement avec un pyproject.toml et un uv.lock ; automatiser avec un linter, des tests et un vérificateur de types. Le reste — la curiosité, la lecture attentive des messages d’erreur, l’envie de comprendre — vous l’avez déjà. C’est ce qui compte le plus.
Ressources pour apprendre
- Formations vidéo : mes deux cours Udemy de 8h — Python pour la Data Science et R pour la Data Science. Toutes ces erreurs y sont expliquées pas à pas, avec des exercices pratiques.
- Livre : Python pour la Data Science aux éditions ENI — le guide que j’aurais aimé avoir à mes débuts : clair, pratique, pensé pour progresser pas à pas.
- Roadmap : Apprendre la Data Science en 2026 : roadmap et étapes clés.
Questions fréquentes
Quelle est l’erreur la plus fréquente chez les débutants en Python ?
Ce sont les erreurs de syntaxe, et de loin : dans une étude menée sur des programmes d’étudiants en première année d’informatique, elles représentent environ 30 à 50 % des cas, contre environ 12 à 24 % pour les NameError. Autrement dit, NameError est la première erreur d’exécution, mais pas l’erreur la plus fréquente au global — la syntaxe reste le premier obstacle, et c’est aussi le plus rapide à corriger.
Pourquoi fillna(inplace=True) ne fonctionne plus avec pandas 3.0 ?
Parce que pandas 3.0 active le copy-on-write par défaut : tout sous-ensemble ou Series renvoyé « always behaves as a copy of the original », et l’écriture chaînée ne modifie plus le DataFrame d’origine. Le code df['salaire'].fillna(..., inplace=True) lève donc désormais pandas.errors.ChainedAssignmentError. La correction consiste à réaffecter explicitement la colonne — df['salaire'] = df['salaire'].fillna(...) — ou à passer un dictionnaire à df.fillna().
Qu’est-ce que le nouveau dtype str de pandas 3.0, et pourquoi ça casse mon code ?
Les colonnes texte n’utilisent plus le dtype object : pandas 3.0 leur applique un dtype str par défaut, adossé à PyArrow quand il est disponible et sinon à NumPy. Conséquence directe, le test df.dtypes == object, vrai pendant des années, renvoie désormais False et ne détecte plus vos colonnes texte. Autre conséquence : les valeurs manquantes d’une colonne texte sont toujours NaN, la distinction entre None et NaN disparaît. Le bon réflexe est de comparer au dtype réel, par exemple df['ville'].dtype.
Comment convertir une colonne en entier quand elle contient des NaN ?
astype(int) échoue dès qu’il reste une valeur manquante. Deux solutions selon ce que vous voulez faire de ces valeurs : imputer d’abord puis convertir — df['revenu'] = df['revenu'].fillna(df['revenu'].median()) suivi de .astype(int) — ou conserver les manquants en utilisant un entier nullable, df['revenu'].astype('Int64'), avec un I majuscule. C’est la bonne option quand « manquant » a un sens métier qu’on ne veut pas écraser.
Les algorithmes de machine learning acceptent-ils les données manquantes ?
Pas tous, et la généralisation inverse est fausse. HistGradientBoostingRegressor et HistGradientBoostingClassifier gèrent nativement les NaN, et check_array expose un contrôle explicite pour les autoriser (ensure_all_finite="allow-nan", anciennement force_all_finite). Le message d’erreur de scikit-learn oriente d’ailleurs lui-même vers ces modèles et vers l’imputation. Le bon réflexe n’est pas « il faut toujours imputer », mais « il faut savoir si ce modèle-ci accepte les valeurs manquantes ».
Pourquoi faut-il ajuster le scaler uniquement sur les données d’entraînement ?
Parce que fit apprend des paramètres et transform les applique. Si vous appelez fit_transform sur le test, la moyenne et l’écart-type du test entrent dans l’entraînement : c’est une fuite de données, et votre score de validation devient optimiste, donc faux. La bonne pratique est fit_transform sur le train, transform sur le test — et un Pipeline scikit-learn applique cette règle par construction, ce qui rend l’erreur impossible à commettre.
Quelle métrique cross_val_score utilise-t-il par défaut en régression ?
Le R², et non une « précision ». Pour un modèle de régression, cross_val_score(model, X, y, cv=5) renvoie des scores de R² ; préciser scoring='r2' rend la métrique explicite dans le code et évite de croire qu’on compare un taux de bonnes réponses. Regardez aussi l’écart-type des scores : un R² moyen correct accompagné d’un écart-type élevé signale un modèle instable.
Quels outils faut-il pour passer d’un script à un vrai projet Python ?
Cinq outils suffisent en 2026. uv, qui remplace pip, pip-tools, pipx, poetry, pyenv et virtualenv et gère un environnement par projet à partir d’un pyproject.toml conforme à la norme PEP 621 ; ruff, linter et formateur ; pytest pour les tests ; mypy pour la vérification statique des types ; et pre-commit pour exécuter tout cela automatiquement avant chaque commit. À signaler : le 19 mars 2026, OpenAI a annoncé l’acquisition d’Astral, l’éditeur d’uv et de Ruff — les outils restent open source, mais la gouvernance change.
Sources
- The Error Landscape: Characterizing the Mistakes of Novice ProgrammersACM — SIGCSE 2019
Étude de Rebecca Smith et Scott Rixner sur les erreurs de programmeurs débutants : les erreurs de syntaxe représentent environ 30 à 50 % des cas, les NameError environ 12 à 24 %. Établit que NameError est la première erreur d’exécution, pas l’erreur la plus fréquente au global. Le DOI renvoie un 403 aux requêtes automatisées : c’est une protection anti-robot, la référence reste accessible depuis un navigateur.
- What’s new in 3.0.0 (January 21, 2026)pandas
Notes de version officielles de pandas 3.0 : sémantique copy-on-write où tout sous-ensemble se comporte toujours comme une copie, arrêt du chained assignment, méthodes en place (replace, fillna, ffill, bfill, interpolate, where, mask, clip) qui renvoient self au lieu de None, analyse des chaînes de dates en microsecondes avec l’avertissement « 1000x smaller » sur la conversion en entiers, suppression de SettingWithCopyWarning, prérequis Python ≥ 3.11 et NumPy ≥ 1.26.
- pandas.errors.ChainedAssignmentErrorpandas
Référence de l’exception levée depuis pandas 3.0 lors d’une écriture par accès chaîné, par exemple df['col'].fillna(..., inplace=True) : le code qui fonctionnait avant 3.0 échoue désormais.
- Migration guide for the new string data type (pandas 3.0)pandas
Le dtype object des colonnes texte est remplacé par un dtype str par défaut, adossé à PyArrow quand il est disponible et sinon à NumPy. Les valeurs manquantes sont uniformément NaN, ce qui rend caduc le test df.dtypes == object.
- Imputation of missing valuesscikit-learn
Page de référence sur la gestion des valeurs manquantes : SimpleImputer, modèles qui acceptent les NaN nativement (HistGradientBoostingClassifier et Regressor) et liste des estimateurs concernés. C’est la page vers laquelle renvoie le message d’erreur de scikit-learn lui-même.
- Pipelines and composite estimatorsscikit-learn
Documentation des pipelines : un Pipeline enchaîne prétraitement et estimateur et n’ajuste chaque étape que sur les données d’entraînement, ce qui évite par construction la fuite de données entre train et test.
- Cross-validation: evaluating estimator performancescikit-learn
Documentation de la validation croisée et de cross_val_score. Précise notamment le scoring par défaut : le R² pour les modèles de régression, pas une exactitude de classification.
- uv — an extremely fast Python package and project managerAstral
Documentation officielle d’uv, qui remplace pip, pip-tools, pipx, poetry, pyenv et virtualenv. Gestion d’un environnement virtuel par projet, dépendances déclarées dans pyproject.toml et versions figées dans uv.lock.
- Ruff — an extremely fast Python linter and code formatterAstral
Documentation officielle de Ruff, qui réunit lint et formatage dans un seul outil configurable depuis pyproject.toml.
- pytest documentationpytest
Documentation officielle de pytest : installation, découverte automatique des tests et assertions Python natives, sans configuration initiale.
- mypy documentationmypy
Documentation officielle de mypy, vérificateur statique de types qui analyse les annotations sans exécuter le code.
- Astral is joining OpenAIAstral
Annonce du 19 mars 2026 : OpenAI acquiert Astral, l’éditeur d’uv, Ruff et ty. Astral indique que l’accord n’était pas encore finalisé à la publication et s’engage à poursuivre le développement de ses outils open source ensuite : « OpenAI will continue supporting our open source tools after the deal closes ».
Chiffres et affirmations vérifiés sur ces sources le .



