Le même nettoyage, chaque lundi
Le patron envoie un nouvel export chaque lundi. Il a les mêmes défauts que la semaine passée : des répétitions, des mots là où il faut des nombres, des écritures bizarres.
Vous pourriez le nettoyer à la main dans un carnet à chaque fois. Ou écrire les étapes une fois, dans l’ordre, dans un script qui se lance de la même façon à chaque fois.
À la fin de cette leçon
Remarque
Je peux écrire une petite chaîne qui nettoie, relie et fait un rapport, et qui s’arrête quand un contrôle échoue.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Chaîne de traitement | des étapes lancées dans l’ordre, chacune utilisant le résultat de la précédente | |
| Étape | un pas de la chaîne | retirer les doublons |
| Contrôle | un test dans la chaîne qui peut l’arrêter | |
| Code de sortie | le nombre par lequel un script finit, 0 si tout va bien | sys.exit(1) |
| Reproductible | qui donne le même résultat à chaque lancement |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Des étapes dans un ordre fixe
Une chaîne de traitement est une liste d’étapes. Chacune prend un tableau et en rend un. L’ordre compte : on ne peut pas relier par la date avant que les dates soient propres.
Après chaque étape, la chaîne peut afficher combien de lignes restent. Une chute brutale est le premier signe qu’un pas s’est mal passé.
Exemple travaillé : les étapes du jour
| Étape | Ce qu’elle fait | Lignes attendues |
|---|---|---|
| lecture | Charge l’export sale | 367 |
| sans doublons | Retire les lignes présentes deux fois | 365 |
| vrais comptes | Garde les locations de 0 ou plus, en nombres | 363 |
| jointure | Ajoute la colonne des fériés en gardant chaque jour | 363 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Écrire de nouveau la liste des fériés
docker exec jupyter sh -c "printf '%s\n' 'date,ferie' '2025-01-01,Nouvel An' '2025-04-21,Lundi de Pâques' '2025-05-01,Fête du Travail' '2025-05-08,Victoire 1945' '2025-07-14,Fête nationale' '2025-08-15,Assomption' '2025-11-11,Armistice' '2025-12-25,Noël' > jours-feries.csv"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Le script tient en une vingtaine de lignes
Le bloc suivant montre le script. Lisez-le d’abord. La fonction etape affiche les lignes restantes et arrête tout le script avec le code 1 s’il y en a moins que minimum.
Le nombre minimum est donné au lancement : vous choisissez la sévérité du contrôle.
steps/traitement.py
import sys
import pandas as pd
from mess import make
minimum = int(sys.argv[1])
def etape(nom, tableau):
print(nom, "->", len(tableau), "lignes")
if len(tableau) < minimum:
print("ARRET :", nom, "laisse moins de", minimum, "lignes")
sys.exit(1)
return tableau
make()
tableau = etape("lecture", pd.read_csv("messy.csv"))
tableau = etape("sans doublons", tableau.drop_duplicates())
tableau["hires"] = pd.to_numeric(tableau["hires"], errors="coerce")
tableau = etape("vrais comptes", tableau[tableau["hires"] >= 0])
tableau["weather"] = tableau["weather"].str.strip().str.lower()
tableau = etape("jointure", tableau.merge(pd.read_csv("jours-feries.csv"), on="date", how="left"))
tableau.to_csv("rapport.csv", index=False)
print(tableau.groupby("weather")["hires"].mean().round(1).to_string())
Écrire la chaîne dans le labo
docker exec jupyter sh -c "printf '%s\n' 'import sys' 'import pandas as pd' 'from mess import make' '' 'minimum = int(sys.argv[1])' '' 'def etape(nom, tableau):' ' print(nom, \"->\", len(tableau), \"lignes\")' ' if len(tableau) < minimum:' ' print(\"ARRET :\", nom, \"laisse moins de\", minimum, \"lignes\")' ' sys.exit(1)' ' return tableau' '' 'make()' 'tableau = etape(\"lecture\", pd.read_csv(\"messy.csv\"))' 'tableau = etape(\"sans doublons\", tableau.drop_duplicates())' 'tableau[\"hires\"] = pd.to_numeric(tableau[\"hires\"], errors=\"coerce\")' 'tableau = etape(\"vrais comptes\", tableau[tableau[\"hires\"] >= 0])' 'tableau[\"weather\"] = tableau[\"weather\"].str.strip().str.lower()' 'tableau = etape(\"jointure\", tableau.merge(pd.read_csv(\"jours-feries.csv\"), on=\"date\", how=\"left\"))' 'tableau.to_csv(\"rapport.csv\", index=False)' 'print(tableau.groupby(\"weather\")[\"hires\"].mean().round(1).to_string())' > steps/traitement.py"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Lancer avec un minimum de 300 lignes
docker exec jupyter python3 steps/traitement.py 300Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Un contrôle qui arrête la chaîne
Un contrôle ne sert que s’il peut dire non. Ici, c’est un minimum de lignes restantes. Donnez-lui un minimum que les données ne peuvent pas tenir : la chaîne s’arrête à la première étape qui le brise.
Le script finit alors avec le code 1, et ce qui l’a lancé sait que cela a échoué.
Demander au moins 365 lignes
docker exec jupyter python3 steps/traitement.py 365 || echo "code de sortie $?"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Demander au moins 400 lignes
docker exec jupyter python3 steps/traitement.py 400 || echo "code de sortie $?"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Vérifier ce que la dernière exécution a écrit
docker exec jupyter python3 -c "import pandas as pd; r=pd.read_csv('rapport.csv'); print(r.shape, r['ferie'].notna().sum(), r['date'].is_unique, r['hires'].isna().sum())"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une erreur fréquente
Attention
Beaucoup pensent qu’une chaîne qui a fini sans erreur est juste. Elle a seulement passé les contrôles que quelqu’un a écrits. Les nombres de lignes ne disent rien sur l’écriture des mots de météo. Ajoutez un contrôle pour chaque défaut déjà vu.
Le motif d’une étape, calculé en Python
# Le motif d'une étape en Python simple : faire, annoncer, s'arrêter s'il reste trop peu de lignes.
lignes = [3, 8, 1, 9, 4, 2]
minimum = 3
def etape(nom, lignes, seuil):
gardees = []
for x in lignes:
if x > seuil:
gardees.append(x)
print(nom, "->", len(gardees), "lignes")
if len(gardees) < minimum:
print("ARRET :", nom)
return None
return gardees
lignes = etape("garder plus de 2", lignes, 2)
if lignes is not None:
lignes = etape("garder plus de 5", lignes, 5)
if lignes is None:
print("arrêté")
else:
print("terminé")
# Passez minimum à 2 et relancez.
Pour aller plus loin : ajouter un contrôle
Ajoutez à steps/traitement.py une étape qui s’arrête si un mot de météo n’est pas cloudy, rainy ou sunny. Utilisez set(tableau["weather"]) et comparez.
Essayez-la sur les données nettoyées, puis avant la ligne strip. Que repère ce contrôle que les nombres de lignes ont manqué ?