Python Data Science : les 9 étapes clés pour passer de zéro à opérationnel
Python Data Science : l'essentiel en un article — vrai code, schémas et étapes concrètes, extraits d'un cours de 36 leçons.
Tout le monde peut apprendre Python Data Science — à condition de suivre les étapes dans le bon ordre. On a condensé un cours complet de 36 leçons en un parcours clair, avec les extraits de code les plus utiles.
- Introduction et Installation
- NumPy essentiel
- Pandas Series et DataFrames
- Lecture et ecriture de donnees
- Nettoyage de donnees
Doublons et incohérences
Objectifs pédagogiques
Pourquoi les doublons sont dangereux
Un doublon, c’est une même réalité comptée deux fois. Conséquences : chiffres d’affaires gonflés, moyennes biaisées, modèles surentraînés. Un doublon peut venir d’un import répété, d’une jointure mal faite, d’un formulaire soumis deux fois…
Détecter les doublons
import pandas as pd df = pd.DataFrame({ "id": [1, 2, 3, 2, 4, 1], "nom": ["Alice", "Bob", "Chloe", "Bob", "David", "Alice"], "ville": ["Paris", "Lyon", "Nice", "Lyon", "Lille", "Paris"] }) # Masque booléen : True si la ligne est un doublon df.duplicated() # Combien de doublons au total ? df.duplicated().sum() # Afficher uniquement les doublons df[df.duplicated()] # Doublons en se basant uniquement sur certaines colonnes df.duplicated(subset=["id"]) df.duplicated(subset=["nom", "ville"])
duplicated() marque True à partir de la 2ème occurrence. Le premier exemplaire est considéré comme l’original.Supprimer les doublons
# Garder la première occurrence (par défaut) df.drop_duplicates() # Garder la dernière occurrence df.drop_duplicates(keep="last") # Supprimer toutes les occurrences dupliquées df.drop_duplicates(keep=False) # Sur certaines colonnes seulement (utile si une col est un timestamp) df.drop_duplicates(subset=["id"]) # Inplace pour modifier le DataFrame directement df.drop_duplicates(inplace=True)
keep — "first" pour garder le plus ancien enregistrement, "last" pour le plus récent (souvent le bon choix si la donnée a été mise à jour), False pour tout supprimer et enquêter.Le piège des doublons approximatifs
« Paris » et « paris » sont différents pour Pandas, même si pour vous c’est la même ville. Avant de chercher les doublons, normalisez.
df = pd.DataFrame({
"email": ["Alice@MAIL.com", " alice@mail.com ", "bob@mail.com"],
"ville": ["Paris", "PARIS", "Lyon"]
})
# Normaliser les emails (espaces + minuscules)
df["email"] = df["email"].str.strip().str.lower()
df["ville"] = df["ville"].str.strip().str.title()
# MAINTENANT on peut détecter les vrais doublons
df.duplicated(subset=["email"])
print(df)Boîte à outils .str
L’accesseur .str donne accès à toutes les méthodes de chaînes Python, vectorisées :
s = pd.Series([" Alice DUPONT ", "bob martin", "CHLOE.LEROY"]) s.str.strip() # enleve espaces s.str.lower() # minuscules s.str.upper() # MAJUSCULES s.str.title() # Mot À Mot Capitalisé s.str.replace(".", " ") # remplacement s.str.contains("alice", case=False) # filtre s.str.startswith("A") s.str.len() # nombre de caractères s.str.split(" ", expand=True) # séparer en colonnes
unicodedata ou la bibliothèque unidecode : df["ville"].apply(unidecode).Incohérences catégorielles
Souvent on saisit la même catégorie de façons différentes : « H », « Homme », « M », « Masculin »… Pandas y voit 4 catégories distinctes.
df = pd.DataFrame({
"sexe": ["H", "Homme", "M", "Masculin", "F", "Femme", "f"]
})
# Avant : voir les valeurs uniques
print(df["sexe"].value_counts())
# Stratégie : dictionnaire de mapping
mapping = {
"H": "Homme", "M": "Homme", "Masculin": "Homme",
"F": "Femme", "f": "Femme", "Femme": "Femme"
}
df["sexe"] = df["sexe"].map(mapping)
print(df["sexe"].value_counts())df["col"].value_counts(). Les incohérences sautent immédiatement aux yeux.Mini-projet : nettoyer une base employés
import pandas as pd df = pd.DataFrame({ "id": [1, 2, 3, 2, 4, 5], "nom": ["Alice", " Bob ", "Chloe", "BOB", "David", "emma"], "poste": ["Dev", "DEV", "PM", "dev", "Pm", "Designer"], "ville": ["Paris", "paris", "Lyon", "PARIS", "Lille", "Lyon"] }) # 1. Normaliser les chaînes for col in ["nom", "poste", "ville"]: df[col] = df[col].str.strip().str.title() # 2. Détecter les doublons sur id print("Doublons :", df.duplicated(subset=["id"]).sum()) # 3. Garder le dernier enregistrement (le plus à jour) df = df.drop_duplicates(subset=["id"], keep="last") # 4. Vérifier print(df) print(df["poste"].value_counts())
Valeurs manquantes (NaN)
Objectifs pédagogiques
Qu’est-ce qu’un NaN ?
NaN = « Not a Number ». C’est la façon dont Pandas (hérité de NumPy) représente une valeur manquante dans une colonne numérique. Pour les dates, c’est NaT ; pour les objets génériques, None. Pandas les traite tous trois de manière unifiée via isnull().
import pandas as pd import numpy as np df = pd.DataFrame({ "nom": ["Alice", "Bob", None, "David"], "age": [30, np.nan, 25, 35], "date": pd.to_datetime(["2025-01-01", None, "2025-01-03", "2025-01-04"]) }) print(df)
NaN != NaN en Python pur (c’est volontaire). Donc x == np.nan est toujours faux. Utilisez toujours isnull() ou isna() pour tester.Détecter les valeurs manquantes
# Masque booléen cellule par cellule df.isnull() # True si NaN df.notna() # inverse # Compter par colonne df.isnull().sum() # Pourcentage de NaN par colonne (df.isnull().sum() / len(df) * 100).round(2) # Total dans tout le DataFrame df.isnull().sum().sum() # Lignes ayant au moins un NaN df[df.isnull().any(axis=1)]
df.isnull().sum(). C’est en 2 secondes votre tableau de bord de la qualité des données.Stratégie 1 — Supprimer (dropna)
Quand les NaN sont rares ou que l’analyse exige des données complètes :
# Supprimer toute ligne contenant au moins 1 NaN df.dropna() # Supprimer seulement si TOUTES les valeurs sont NaN df.dropna(how="all") # Supprimer si NaN dans une colonne précise df.dropna(subset=["age"]) # Garder une ligne si elle a au moins 3 valeurs non-NaN df.dropna(thresh=3) # Supprimer une colonne entière trop incomplète df.dropna(axis=1, thresh=100) # colonnes < 100 valeurs supprimées
df.dropna().shape vs df.shape.Stratégie 2 — Remplir (fillna)
Avec une valeur fixe
df["age"].fillna(0) df["ville"].fillna("Inconnu") # Sur tout le DataFrame avec un dict par colonne df.fillna({"age": 0, "ville": "Inconnu", "salaire": 2000})
Avec une statistique de la colonne
df["age"].fillna(df["age"].mean()) # moyenne df["age"].fillna(df["age"].median()) # médiane (robuste) df["ville"].fillna(df["ville"].mode()[0]) # valeur la plus fréquente
Par propagation (utile pour séries temporelles)
# Remplir avec la valeur précédente (forward fill) df["prix"].ffill() # Avec la valeur suivante (backward fill) df["prix"].bfill() # Limiter le nombre de remplissages consécutifs df["prix"].ffill(limit=3)
Types de données et conversion
Objectifs pédagogiques
dtypes, convertir en nombre/date/catégorie, utiliser errors="coerce" pour les valeurs sales, et réduire l’empreinte mémoire d’un DataFrame en passant object → category.Pourquoi le type importe
Un « 42 » stocké comme texte (object) ne peut pas être sommé, ni comparé numériquement. Une date stockée comme texte ne permet pas .dt.year. Mauvais type = bugs silencieux ou perte de fonctionnalités.
import pandas as pd df = pd.DataFrame({ "prix": ["12.50", "8.99", "15.00"], "date": ["2025-01-01", "2025-02-15", "2025-03-10"] }) print(df.dtypes) # prix object <-- texte ! # date object <-- texte ! df["prix"].sum() # concaténe « 12.508.9915.00 » !
object = « n’importe quoi », le plus souvent du texte. Si vous voyez object sur une colonne censée être numérique, il y a un problème à résoudre avant toute analyse.Les types Pandas en bref
| Dtype | Usage | Exemple |
|---|---|---|
int64 | Entier | Ages, quantités |
float64 | Réel | Prix, températures |
bool | Vrai/Faux | Actif, payant |
object | Générique (souvent str) | Noms, emails |
datetime64[ns] | Date/heure | Commande, naissance |
timedelta64 | Durée | Différence entre 2 dates |
category | Catégories répétées | Sexe, ville, statut |
Conversion avec astype
# Conversion simple, échoue si une valeur ne convient pas df["age"] = df["age"].astype(int) df["prix"] = df["prix"].astype(float) df["actif"] = df["actif"].astype(bool) df["sexe"] = df["sexe"].astype("category") # Sur plusieurs colonnes à la fois df = df.astype({"age": int, "prix": float, "ville": "category"})
astype(int) plante dès qu’une valeur ne se convertit pas (ex : "12 EUR"). Pour gérer ces cas, utilisez pd.to_numeric.pd.to_numeric — le tolérant
s = pd.Series(["12.5", "8.99", "oops", "15"]) # Mode strict : crash si une valeur n’est pas numérique pd.to_numeric(s) # ValueError # Mode tolérant : valeurs sales -> NaN pd.to_numeric(s, errors="coerce") # 0 12.50 # 1 8.99 # 2 NaN <-- "oops" devient NaN # 3 15.00 # Choisir le sous-type pour gagner de la mémoire pd.to_numeric(s, errors="coerce", downcast="float") pd.to_numeric(s, errors="coerce", downcast="integer")
errors="coerce" → comptez les NaN générés → décidez quoi faire (imputer, supprimer, signaler).pd.to_datetime — toutes les dates
# Reconnaît la plupart des formats automatiquement pd.to_datetime(["2025-01-15", "15/01/2025", "Jan 15, 2025"]) # Format explicite (plus rapide, plus sûr) pd.to_datetime(df["date"], format="%%d/%%m/%%Y") # Tolérant aux valeurs sales pd.to_datetime(df["date"], errors="coerce") # Une fois en datetime, on accède aux composants df["date"] = pd.to_datetime(df["date"]) df["annee"] = df["date"].dt.year df["mois"] = df["date"].dt.month df["jour_sem"] = df["date"].dt.day_name()
format= peut multiplier la vitesse par 100. Pandas n’a plus à deviner.Type category : la magie mémoire
Pour une colonne « ville » répétée 1 million de fois, stocker chaque chaîne est une perte de place. category stocke chaque valeur unique une fois, et référence un index entier.
import pandas as pd df = pd.DataFrame({ "ville": ["Paris", "Lyon", "Paris"] * 1_000_000 }) print(df.memory_usage(deep=True).sum() / 1e6, "Mo") # Environ 200 Mo df["ville"] = df["ville"].astype("category") print(df.memory_usage(deep=True).sum() / 1e6, "Mo") # Environ 8 Mo -- gain x25 !
category ? — Quand le nombre de valeurs uniques est petit par rapport au nombre total de lignes (typiquement < 5 %%). Sinon, peu de gain.Mini-projet : nettoyer une exportation comptable
import pandas as pd df = pd.DataFrame({ "montant": ["125,50", "99,00", "N/D", "42,75"], "date": ["01/03/2025", "02/03/2025", "oops", "04/03/2025"], "categorie": ["Achat", "Vente", "Achat", "Vente"], "paye": ["Oui", "Non", "Oui", "Oui"] }) # 1. Montant : virgule -> point, puis numérique df["montant"] = df["montant"].str.replace(",", ".") df["montant"] = pd.to_numeric(df["montant"], errors="coerce") # 2. Date au format jj/mm/aaaa df["date"] = pd.to_datetime(df["date"], format="%%d/%%m/%%Y", errors="coerce") # 3. Categorie -> type category df["categorie"] = df["categorie"].astype("category") # 4. Oui/Non -> bool df["paye"] = df["paye"].map({"Oui": True, "Non": False}) print(df.dtypes) print(df)
Cet article couvre les extraits les plus utiles — le cours complet Python Data Science (11 chapitres, 36 leçons, exercices corrigés et projet final) t'emmène jusqu'au bout.
./acceder-au-cours-complet cours gratuit : Maîtriser Claude CodeFAQ
Combien de temps pour apprendre Python Data Science ?
Faut-il des prérequis ?
Par où commencer concrètement ?
📬 Tu veux recevoir ce type de guide chaque semaine ? Abonne-toi gratuitement — code réel, zéro blabla.