Devmind.
Menu

Apprendre, à votre rythme

Tous les cursus sont accessibles sans compte.

Chapitre 11 / Pratique

Données fil rouge : colis synthétiques

Obtenir et décrire un CSV versionné pour les ateliers du cursus.

◷ 30 minutesTravail sur votre machine

Un même jeu de données pour plusieurs questions

Le jeu de colis bruts contient 325 lignes : 320 commandes synthétiques de 80 clients et 5 doublons exacts. Il a des poids en grammes ou kilogrammes et quelques poids manquants. Aucune donnée personnelle réelle n'y figure. Les résultats obtenus sur ces données fabriquées servent à apprendre une méthode, pas à mesurer un transporteur réel.

Copiez le générateur ci-dessous dans generer_colis.py. Il utilise uniquement la bibliothèque standard. Le script et le CSV qu'il produit sont également versionnés avec ce cours, mais vous pouvez créer votre copie sans accès au dépôt GitLab. Gardez le CSV brut intact :

"""Génère le jeu de colis synthétique du cursus (bibliothèque standard seule).

Usage : python generer_colis.py data/colis_bruts.csv
"""

import csv
import random
import sys
from datetime import date, timedelta
from pathlib import Path


COLONNES = [
    "commande_id", "client_id", "date_commande", "distance_km",
    "poids_valeur", "unite_poids", "transporteur", "urgence",
    "prix_eur", "delai_jours", "retard",
]


def generer_lignes():
    hasard = random.Random(42)
    lignes = []
    transporteurs = ["Alpha", "Beta", "Gamma"]
    for client in range(80):
        for commande in range(4):
            numero = client * 4 + commande
            distance = hasard.randint(20, 600)
            poids_kg = round(hasard.uniform(0.5, 12), 2)
            transporteur = hasard.choice(transporteurs)
            urgence = hasard.random() < 0.3
            unite = "g" if hasard.random() < 0.2 else "kg"
            poids_saisi = (
                f"{poids_kg * 1000:.0f}" if unite == "g" else f"{poids_kg:.2f}"
            )
            if hasard.random() < 0.06:
                poids_saisi = ""

            prix = 4 + distance * 0.05 + poids_kg * 1.4 + 5 * urgence
            prix += {"Alpha": 0, "Beta": 2, "Gamma": -1}[transporteur]
            prix += hasard.gauss(0, 1.5)
            delai = 1 + distance / 220 + poids_kg / 10 - 0.7 * urgence
            delai += {"Alpha": 0, "Beta": 0.5, "Gamma": -0.3}[transporteur]
            delai = round(max(0.5, delai + hasard.gauss(0, 0.5)), 1)

            lignes.append({
                "commande_id": f"C{numero + 1:04d}",
                "client_id": f"U{client + 1:03d}",
                "date_commande": str(date(2025, 1, 1) + timedelta(days=numero)),
                "distance_km": distance,
                "poids_valeur": poids_saisi,
                "unite_poids": unite,
                "transporteur": transporteur,
                "urgence": "oui" if urgence else "non",
                "prix_eur": f"{prix:.2f}",
                "delai_jours": f"{delai:.1f}",
                "retard": int(delai > 3.5),
            })

    lignes.extend(lignes[index].copy() for index in (3, 70, 140, 210, 300))
    assert len(lignes) == 325
    assert len({ligne["commande_id"] for ligne in lignes}) == 320
    assert any(not ligne["poids_valeur"] for ligne in lignes)
    assert {ligne["retard"] for ligne in lignes} == {0, 1}
    return lignes


def main():
    sortie = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("colis_bruts.csv")
    sortie.parent.mkdir(parents=True, exist_ok=True)
    with sortie.open("w", newline="", encoding="utf-8") as fichier:
        writer = csv.DictWriter(fichier, fieldnames=COLONNES)
        writer.writeheader()
        writer.writerows(generer_lignes())
    print(f"{sortie} : 325 lignes, dont 5 doublons exacts")


if __name__ == "__main__":
    main()
python generer_colis.py data/colis_bruts.csv

Depuis le dossier de votre projet, vérifiez la lecture avec la bibliothèque standard :

import csv
with open("data/colis_bruts.csv", newline="", encoding="utf-8") as fichier:
    lignes = list(csv.DictReader(fichier))
print(len(lignes), len({ligne["commande_id"] for ligne in lignes}))
# 325 lignes, 320 identifiants distincts

Le contrat des colonnes

commande_id identifie une commande ; client_id sert à garder les commandes d'un même client dans le même côté du découpage train/test. date_commande est connue à la commande. distance_km, poids_valeur, unite_poids, transporteur et urgence décrivent ce qui est connu avant livraison. Après conversion, poids_kg remplace poids_valeur et unite_poids.

Trois questions guideront les ateliers :

  1. Régression : prédire prix_eur à partir des caractéristiques connues à la commande. Ne mettez pas prix_eur dans les entrées.
  2. Classification : prédire retard (0 ou 1). delai_jours est connu après livraison et définit ici retard ; l'utiliser comme entrée serait une fuite directe. Écartez aussi prix_eur pour garder un contrat simple.
  3. Exploration sans cible : décrire les profils de commandes avec distance, poids et urgence. Un groupe trouvé par k-means n'a pas automatiquement un sens métier.

Le jeu est petit et construit selon une recette connue. Il permet de reproduire un raisonnement et de repérer les erreurs de protocole. Notez dans votre carnet la version du dépôt utilisée, puis commencez le diagnostic des données avant tout entraînement.

Pas de correction en ligneConfrontez votre code aux résultats attendus du cours.