Données fil rouge : colis synthétiques
Obtenir et décrire un CSV versionné pour les ateliers du cursus.
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 :
- Régression : prédire
prix_eurà partir des caractéristiques connues à la commande. Ne mettez pasprix_eurdans les entrées. - Classification : prédire
retard(0 ou 1).delai_joursest connu après livraison et définit iciretard; l'utiliser comme entrée serait une fuite directe. Écartez aussiprix_eurpour garder un contrat simple. - 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.