ParcoursOuvrir l'appli

Nettoyer les données

Projet : analyser des ventes · 55 min

Les vraies données sont sales

Le fichier de janvier à mars était parfait. Celui d'avril, tapé à la main par le neveu de Mama Brigitte un soir de fatigue, l'est beaucoup moins. Ouvre-le :

date,produit,quantite,prix_unitaire,vendeur
2026-04-01,riz,2,1200,Awa
2026-04-01, Huile ,1,1500,Paul

2026-04-02,SAVON,4,500,Mireille
2026-04-03,riz,,1200,Paul
2026-04-03,sucre,3,900,Awa
2026-04-04,riz,1,1 200,Mireille
2026-04-05,lait,deux,650,Paul
2026-04-05,huile,2,1500
2026-04-06,Spaghetti,5,400,Awa
2026-04-07, riz,10,1200, Paul

Compte les problèmes :

Ligne Problème Que faire ?
Huile espaces autour, majuscule corriger : "huile"
(vide) ligne vide ignorer
SAVON majuscules corriger : "savon"
riz,,1200 quantité manquante rejeter : on ne peut pas deviner
1 200 espace dans le nombre corriger : 1200
deux nombre écrit en lettres rejeter
huile,2,1500 il manque le vendeur rejeter
riz, Paul espaces corriger

Avec la fonction lire_ventes du cours 1, ce fichier ferait planter le programme dès la ligne vide. Et même sans plantage, "Huile", " Huile " et "huile" seraient comptés comme trois produits différents.

Nettoyer un texte

Tu connais déjà les outils :

Méthode Effet Exemple
.strip() enlève les espaces au début et à la fin " Huile ".strip() vaut "Huile"
.lower() met tout en minuscules "SAVON".lower() vaut "savon"
.replace(a, b) remplace chaque a par b "1 200".replace(" ", "") vaut "1200"

On peut les enchaîner : " Huile ".strip().lower() vaut "huile". Python applique .strip() d'abord, puis .lower() au résultat.

Exercice : Deux petites fonctions de nettoyage

Écris :

  1. nettoyer_texte(texte), qui renvoie le texte sans espaces autour et en minuscules ;
  2. nettoyer_nombre(texte), qui enlève tous les espaces (y compris au milieu) et renvoie le nombre entier correspondant.
Voir une solution
def nettoyer_texte(texte):
    """Enlève les espaces autour et met en minuscules."""
    return texte.strip().lower()


def nettoyer_nombre(texte):
    """Enlève tous les espaces et renvoie le nombre entier."""
    return int(texte.replace(" ", ""))

Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice

Quand la conversion échoue : try et except

Que fait nettoyer_nombre("deux") ? Python essaie int("deux") et s'arrête :

ValueError: invalid literal for int() with base 10: 'deux'

Même chose avec "", la quantité vide. Pour un programme qui lit des milliers de lignes, s'arrêter à la première faute n'est pas acceptable. Il faut pouvoir dire : « essaie de convertir ; si ça rate, fais autre chose ».

texte = "deux"
try:
    quantite = int(texte)
    print("Conversion réussie")
except ValueError:
    quantite = None
    print("Ce n'est pas un nombre :", texte)

Ici, int(texte) échoue : la ligne print("Conversion réussie") n'est jamais exécutée, Python passe directement dans except.

Voir try et except en action

Lance, puis ajoute tes propres textes dans la liste.

essais = ["12", " 7 ", "1 200", "", "deux", "3.5"]

for texte in essais:
    try:
        nombre = int(texte.replace(" ", ""))
        print(f"'{texte}' devient {nombre}")
    except ValueError:
        print(f"'{texte}' n'est pas un nombre entier")

Exercice : Convertir sans planter

Écris convertir(texte) qui enlève tous les espaces du texte, puis renvoie le nombre entier correspondant. Si le texte ne contient pas un nombre entier valable, la fonction renvoie None au lieu de planter.

Voir une solution
def convertir(texte):
    """Renvoie le nombre entier écrit dans texte, ou None s'il n'y en a pas."""
    try:
        return int(texte.replace(" ", ""))
    except ValueError:
        return None

Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice

Le plan de la lecture propre

On a tous les outils. Voici ce que fera la nouvelle fonction de lecture, pour chaque ligne après l'en-tête :

  1. Ligne vide (seulement des espaces ou rien) : on l'ignore avec continue.
  2. On découpe aux virgules. S'il n'y a pas exactement 5 morceaux : on rejette la ligne.
  3. On convertit la quantité et le prix avec convertir. Si l'un des deux vaut None : on rejette.
  4. Sinon, on nettoie le produit (nettoyer_texte), la date et le vendeur (.strip()), et on ajoute la vente.

La fonction renverra deux listes : les ventes valables, et les lignes rejetées, pour pouvoir les montrer à Mama Brigitte.

Pour le vendeur, on garde les majuscules ("Paul") : un nom propre s'écrit avec sa majuscule. On enlève juste les espaces.

Exercice : La brique de lecture propre

Les deux fonctions de nettoyage sont données. Complète lire_ventes_propres(nom_fichier) en suivant le plan, puis observe le résultat sur le fichier d'avril.

Voir une solution
def nettoyer_texte(texte):
    """Enlève les espaces autour et met en minuscules."""
    return texte.strip().lower()


def convertir(texte):
    """Renvoie le nombre entier écrit dans texte, ou None s'il n'y en a pas."""
    try:
        return int(texte.replace(" ", ""))
    except ValueError:
        return None


def lire_ventes_propres(nom_fichier):
    """Lit un CSV de ventes, nettoie chaque ligne et met de côté les lignes invalides."""
    with open(nom_fichier, encoding="utf-8") as f:
        lignes = f.read().splitlines()
    ventes = []
    rejetees = []
    for ligne in lignes[1:]:
        if ligne.strip() == "":
            continue
        morceaux = ligne.split(",")
        if len(morceaux) != 5:
            rejetees.append(ligne)
            continue
        date, produit, quantite, prix, vendeur = morceaux
        quantite = convertir(quantite)
        prix = convertir(prix)
        if quantite is None or prix is None:
            rejetees.append(ligne)
            continue
        ventes.append({
            "date": date.strip(),
            "produit": nettoyer_texte(produit),
            "quantite": quantite,
            "prix": prix,
            "vendeur": vendeur.strip(),
        })
    return ventes, rejetees


ventes, rejetees = lire_ventes_propres("ventes_brutes.csv")
print(len(ventes), "ventes valables")
print(len(rejetees), "lignes rejetées :")
for ligne in rejetees:
    print("  ", ligne)

Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice

Exercice : Défi : le taux de rejet

Un bon analyste dit toujours quelle part des données il a dû écarter. Si 3 lignes sur 10 sont rejetées, le résultat est beaucoup moins fiable que si c'est 3 sur 10 000.

La lecture propre est donnée. Calcule dans taux le pourcentage de lignes rejetées parmi les lignes non vides (valables + rejetées), arrondi à l'entier. Affiche Lignes rejetées : 30 %.

Voir une solution
def nettoyer_texte(texte):
    """Enlève les espaces autour et met en minuscules."""
    return texte.strip().lower()


def convertir(texte):
    """Renvoie le nombre entier écrit dans texte, ou None s'il n'y en a pas."""
    try:
        return int(texte.replace(" ", ""))
    except ValueError:
        return None


def lire_ventes_propres(nom_fichier):
    """Lit un CSV de ventes, nettoie chaque ligne et met de côté les lignes invalides."""
    with open(nom_fichier, encoding="utf-8") as f:
        lignes = f.read().splitlines()
    ventes = []
    rejetees = []
    for ligne in lignes[1:]:
        if ligne.strip() == "":
            continue
        morceaux = ligne.split(",")
        if len(morceaux) != 5:
            rejetees.append(ligne)
            continue
        date, produit, quantite, prix, vendeur = morceaux
        quantite = convertir(quantite)
        prix = convertir(prix)
        if quantite is None or prix is None:
            rejetees.append(ligne)
            continue
        ventes.append({
            "date": date.strip(),
            "produit": nettoyer_texte(produit),
            "quantite": quantite,
            "prix": prix,
            "vendeur": vendeur.strip(),
        })
    return ventes, rejetees


ventes, rejetees = lire_ventes_propres("ventes_brutes.csv")
taux = round(len(rejetees) / (len(ventes) + len(rejetees)) * 100)
print(f"Lignes rejetées : {taux} %")

Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice

Teste-toi

Qu'affiche ce code ? python try: n = int("abc") print("A") except ValueError: print("B") print("C")
  1. A puis C
  2. B puis C (bonne réponse)
  3. A, B puis C
  4. B seulement
Pourquoi éviter except: sans nom d'exception ?
  1. C'est une erreur de syntaxe
  2. Il attrape aussi les vraies fautes du programme, qui deviennent invisibles (bonne réponse)
  3. Il ralentit beaucoup le programme
  4. Il n'attrape aucune erreur