Aller au contenu

Exercices - Traitement de données en tables


On dispose du fichier villes.csv suivant :

nom,population,departement,altitude
Lyon,522250,69,175
Marseille,870018,13,12
Lille,236234,59,27
Toulouse,493465,31,141
Bordeaux,257804,33,6

a. Combien d'enregistrements contient cette table ? Combien d'attributs ?

b. Écrire un programme Python qui lit ce fichier et stocke les données dans un tableau de dictionnaires, en convertissant population et altitude en entiers.

c. Afficher le nom et la population de chaque ville.

Solution

a. 5 enregistrements (lignes de données) et 4 attributs (colonnes : nom, population, departement, altitude).

b.

import csv
table = []
with open("villes.csv", "r", encoding="utf-8") as fichier:
lecteur = csv.DictReader(fichier)
for ligne in lecteur:
enregistrement = dict(ligne)
enregistrement["population"] = int(enregistrement["population"])
enregistrement["altitude"] = int(enregistrement["altitude"])
table.append(enregistrement)

c.

for ville in table:
print(f"{ville['nom']} : {ville['population']} habitants")

On dispose du tableau suivant :

films = [
{"titre": "Le Fabuleux Destin d'Amélie Poulain", "annee": 2001, "realisateur": "Jean-Pierre Jeunet"},
{"titre": "Intouchables", "annee": 2011, "realisateur": "Olivier Nakache"},
{"titre": "La Haine", "annee": 1995, "realisateur": "Mathieu Kassovitz"}
]

a. Écrire un programme qui enregistre ces données dans un fichier films.csv.

b. Ouvrir le fichier produit avec un éditeur de texte et vérifier son contenu.

Solution

a.

import csv
films = [
{"titre": "Le Fabuleux Destin d'Amélie Poulain", "annee": 2001, "realisateur": "Jean-Pierre Jeunet"},
{"titre": "Intouchables", "annee": 2011, "realisateur": "Olivier Nakache"},
{"titre": "La Haine", "annee": 1995, "realisateur": "Mathieu Kassovitz"}
]
with open("films.csv", "w", encoding="utf-8", newline="") as fichier:
colonnes = ["titre", "annee", "realisateur"]
ecrivain = csv.DictWriter(fichier, fieldnames=colonnes)
ecrivain.writeheader()
for film in films:
ecrivain.writerow(film)

b. Le fichier contient :

titre,annee,realisateur
Le Fabuleux Destin d'Amélie Poulain,2001,Jean-Pierre Jeunet
Intouchables,2011,Olivier Nakache
La Haine,1995,Mathieu Kassovitz

On travaille avec la table des villes de l'exercice 1.

a. Écrire une instruction qui renvoie la liste des villes de plus de 300 000 habitants.

b. Écrire une instruction qui renvoie la liste des villes dont l'altitude est inférieure à 50 mètres.

c. Écrire une instruction qui renvoie la liste des villes du département 13 ou 69.

Solution

a.

grandes = [v for v in table if v["population"] > 300000]
for v in grandes:
print(v["nom"]) # Lyon, Marseille, Toulouse

b.

basses = [v for v in table if v["altitude"] < 50]
for v in basses:
print(f"{v['nom']} ({v['altitude']} m)")
# Marseille (12 m), Lille (27 m), Bordeaux (6 m)

c.

selection = [v for v in table if v["departement"] in ["13", "69"]]
for v in selection:
print(v["nom"]) # Lyon, Marseille

a. À partir de la table des villes, créer un nouveau tableau ne contenant que les attributs nom et population.

b. Calculer la population totale des 5 villes.

c. Calculer l'altitude moyenne.

d. Trouver la ville la plus peuplée et la moins peuplée.

Solution

a.

extrait = [{"nom": v["nom"], "population": v["population"]} for v in table]

b.

total = sum(v["population"] for v in table)
print(f"Population totale : {total}") # 2 379 771

c.

altitudes = [v["altitude"] for v in table]
moyenne = sum(altitudes) / len(altitudes)
print(f"Altitude moyenne : {moyenne} m") # 72.2 m

d.

plus_peuplee = max(table, key=lambda v: v["population"])
moins_peuplee = min(table, key=lambda v: v["population"])
print(f"Plus peuplée : {plus_peuplee['nom']}") # Marseille
print(f"Moins peuplée : {moins_peuplee['nom']}") # Lille

a. Trier la table des villes par population croissante. Afficher le résultat.

b. Trier par nom alphabétique.

c. Trier par altitude décroissante.

d. On ajoute des villes ayant la même altitude. Le tri est-il stable ? Vérifier.

Solution

a.

par_pop = sorted(table, key=lambda v: v["population"])
for v in par_pop:
print(f"{v['nom']} : {v['population']}")
# Lille, Toulouse, Bordeaux, Lyon, Marseille

b.

par_nom = sorted(table, key=lambda v: v["nom"])
for v in par_nom:
print(v["nom"])
# Bordeaux, Lille, Lyon, Marseille, Toulouse

c.

par_alt_desc = sorted(table, key=lambda v: v["altitude"], reverse=True)
for v in par_alt_desc:
print(f"{v['nom']} ({v['altitude']} m)")
# Lyon (175), Toulouse (141), Lille (27), Marseille (12), Bordeaux (6)

d. Oui, le tri de Python (Timsort) est stable : si deux villes ont la même altitude, elles restent dans leur ordre d'apparition d'origine.


On dispose de la table suivante :

notes = [
{"nom": "Dupont", "matiere": "NSI", "note": 15},
{"nom": "Martin", "matiere": "NSI", "note": 12},
{"nom": "Dupont", "matiere": "Maths", "note": 14},
{"nom": "Durand", "matiere": "NSI", "note": "18"},
{"nom": "Martin", "matiere": "NSI", "note": 16},
{"nom": "Petit", "matiere": "NSI", "note": -2}
]

a. Identifier les doublons sur l'attribut nom dans la matière NSI.

b. Repérer les problèmes de cohérence dans cette table (types, valeurs aberrantes).

c. Écrire une fonction qui nettoie la table : convertir les notes en entiers et supprimer les notes hors de [0, 20].

Solution

a. En filtrant sur la matière NSI :

  • Dupont n'apparait qu'une fois en NSI (l'autre est en Maths).
  • Martin apparait deux fois en NSI (notes 12 et 16) : c'est un doublon.

b. Problèmes :

  • Ligne 3 (Durand) : la note "18" est une chaîne au lieu d'un entier.
  • Ligne 5 (Petit) : la note -2 est aberrante (hors de [0, 20]).

c.

def nettoyer_notes(table):
resultat = []
for ligne in table:
copie = dict(ligne)
try:
copie["note"] = int(copie["note"])
except (ValueError, TypeError):
continue
if 0 <= copie["note"] <= 20:
resultat.append(copie)
return resultat
propre = nettoyer_notes(notes)
for n in propre:
print(n)

On dispose de deux tables :

commandes = [
{"id_commande": 1, "id_client": 10, "produit": "Livre", "prix": 15},
{"id_commande": 2, "id_client": 20, "produit": "Stylo", "prix": 3},
{"id_commande": 3, "id_client": 10, "produit": "Cahier", "prix": 5},
{"id_commande": 4, "id_client": 30, "produit": "Gomme", "prix": 2}
]
clients = [
{"id_client": 10, "nom": "Dupont", "ville": "Lyon"},
{"id_client": 20, "nom": "Martin", "ville": "Paris"},
{"id_client": 30, "nom": "Durand", "ville": "Lille"}
]

a. Quelle est la clé de jointure ? Identifier la clé primaire et la clé étrangère.

b. Effectuer la jointure et afficher, pour chaque commande, le nom du client, le produit et le prix.

c. Après la jointure, filtrer les commandes des clients de Lyon.

Solution

a. La clé de jointure est "id_client". C'est la clé primaire de la table clients et la clé étrangère de la table commandes.

b.

def jointure(table1, table2, cle):
resultat = []
for l1 in table1:
for l2 in table2:
if l1[cle] == l2[cle]:
fusion = {}
fusion.update(l1)
fusion.update(l2)
resultat.append(fusion)
return resultat
complet = jointure(commandes, clients, "id_client")
for c in complet:
print(f"{c['nom']} a commandé {c['produit']} ({c['prix']} euros)")
# Dupont a commandé Livre (15 euros)
# Martin a commandé Stylo (3 euros)
# Dupont a commandé Cahier (5 euros)
# Durand a commandé Gomme (2 euros)

c.

lyon = [c for c in complet if c["ville"] == "Lyon"]
for c in lyon:
print(f"{c['produit']} - {c['prix']} euros")
# Livre - 15 euros
# Cahier - 5 euros

On ajoute un client sans commande et une commande avec un client inconnu :

clients.append({"id_client": 40, "nom": "Morel", "ville": "Bordeaux"})
commandes.append({"id_commande": 5, "id_client": 99, "produit": "Règle", "prix": 4})

a. Effectuer la jointure. Morel et la commande 5 apparaissent-ils dans le résultat ?

b. Expliquer pourquoi.

Solution

a. Non, ni Morel ni la commande 5 n'apparaissent dans le résultat.

b. La jointure ne conserve que les lignes ayant une correspondance dans les deux tables :

  • Morel (id_client = 40) n'a aucune commande → pas de correspondance dans commandes.
  • La commande 5 (id_client = 99) n'a aucun client correspondant → pas de correspondance dans clients.

On reprend les tables commandes et clients de l'exercice 7.

a. Après jointure, calculer le montant total des commandes par client.

b. Trouver le client qui a dépensé le plus.

c. Calculer le montant total de toutes les commandes.

Solution

a.

complet = jointure(commandes, clients, "id_client")
totaux = {}
for c in complet:
nom = c["nom"]
if nom in totaux:
totaux[nom] += c["prix"]
else:
totaux[nom] = c["prix"]
for nom, total in totaux.items():
print(f"{nom} : {total} euros")
# Dupont : 20 euros
# Martin : 3 euros
# Durand : 2 euros

b.

meilleur_client = max(totaux, key=lambda n: totaux[n])
print(f"Meilleur client : {meilleur_client} ({totaux[meilleur_client]} euros)")
# Meilleur client : Dupont (20 euros)

c.

total_general = sum(c["prix"] for c in complet)
print(f"Total : {total_general} euros") # 25 euros

On dispose de la table suivante (déjà en mémoire) :

pays = [
{"nom": "France", "capitale": "Paris", "population": 67, "continent": "Europe"},
{"nom": "Japon", "capitale": "Tokyo", "population": 126, "continent": "Asie"},
{"nom": "Brésil", "capitale": "Brasilia", "population": 213, "continent": "Amérique"},
{"nom": "Allemagne", "capitale": "Berlin", "population": 83, "continent": "Europe"},
{"nom": "Inde", "capitale": "New Delhi", "population": 1408, "continent": "Asie"},
{"nom": "Canada", "capitale": "Ottawa", "population": 38, "continent": "Amérique"},
{"nom": "Italie", "capitale": "Rome", "population": 60, "continent": "Europe"}
]

Les populations sont en millions.

a. Filtrer les pays d'Europe. Combien sont-ils ?

b. Trier tous les pays par population décroissante.

c. Calculer la population totale par continent.

d. Trouver le pays le plus peuplé de chaque continent.

Solution

a.

europe = [p for p in pays if p["continent"] == "Europe"]
print(f"Pays d'Europe : {len(europe)}") # 3
for p in europe:
print(p["nom"]) # France, Allemagne, Italie

b.

par_pop = sorted(pays, key=lambda p: p["population"], reverse=True)
for p in par_pop:
print(f"{p['nom']} : {p['population']} M")
# Inde, Brésil, Japon, Allemagne, France, Italie, Canada

c.

pop_continent = {}
for p in pays:
c = p["continent"]
if c in pop_continent:
pop_continent[c] += p["population"]
else:
pop_continent[c] = p["population"]
for c, pop in pop_continent.items():
print(f"{c} : {pop} M")
# Europe : 210 M
# Asie : 1534 M
# Amérique : 251 M

d.

continents = set(p["continent"] for p in pays)
for c in continents:
pays_du_continent = [p for p in pays if p["continent"] == c]
plus_peuple = max(pays_du_continent, key=lambda p: p["population"])
print(f"{c} : {plus_peuple['nom']} ({plus_peuple['population']} M)")
# Europe : Allemagne (83 M)
# Asie : Inde (1408 M)
# Amérique : Brésil (213 M)



Activité 1 - Explorer un jeu de données réel (open data)

Section intitulée « Activité 1 - Explorer un jeu de données réel (open data) »

Créer le fichier communes.csv suivant (extrait simplifié) :

nom,code_postal,population,superficie,region
Paris,75000,2161000,105,Ile-de-France
Marseille,13000,870731,241,Provence-Alpes-Cote d'Azur
Lyon,69000,522969,48,Auvergne-Rhone-Alpes
Toulouse,31000,493465,118,Occitanie
Nice,06000,342669,72,Provence-Alpes-Cote d'Azur
Nantes,44000,318808,65,Pays de la Loire
Montpellier,34000,290053,57,Occitanie
Strasbourg,67000,287228,78,Grand Est
Bordeaux,33000,257804,50,Nouvelle-Aquitaine
Lille,59000,236234,35,Hauts-de-France

a. Importer les données et convertir les types numériques.

b. Calculer la densité de population (habitants par km²) pour chaque commune et l'ajouter comme nouvel attribut.

c. Afficher les 5 communes les plus denses, triées par densité décroissante.

d. Calculer la population totale et la population moyenne des communes du fichier.

e. Regrouper les communes par région et calculer la population totale de chaque région.

f. Exporter les résultats (avec la densité) dans un nouveau fichier communes_enrichi.csv.

Correction

a.

import csv
table = []
with open("communes.csv", "r", encoding="utf-8") as f:
lecteur = csv.DictReader(f)
for ligne in lecteur:
e = dict(ligne)
e["population"] = int(e["population"])
e["superficie"] = int(e["superficie"])
table.append(e)

b.

for commune in table:
commune["densite"] = round(commune["population"] / commune["superficie"])

c.

par_densite = sorted(table, key=lambda c: c["densite"], reverse=True)
print("Top 5 des communes les plus denses :")
for c in par_densite[:5]:
print(f" {c['nom']} : {c['densite']} hab/km2")
# Paris : 20581, Lyon : 10895, Lille : 6749, Montpellier : 5088, Nantes : 4905

d.

pop_totale = sum(c["population"] for c in table)
pop_moyenne = pop_totale / len(table)
print(f"Population totale : {pop_totale}") # 5 780 961
print(f"Population moyenne : {pop_moyenne:.0f}") # 578 096

e.

par_region = {}
for c in table:
r = c["region"]
if r in par_region:
par_region[r] += c["population"]
else:
par_region[r] = c["population"]
for region, pop in sorted(par_region.items(), key=lambda x: x[1], reverse=True):
print(f" {region} : {pop}")

f.

with open("communes_enrichi.csv", "w", encoding="utf-8", newline="") as f:
colonnes = ["nom", "code_postal", "population", "superficie", "region", "densite"]
ecrivain = csv.DictWriter(f, fieldnames=colonnes)
ecrivain.writeheader()
for c in table:
ecrivain.writerow(c)

Activité 2 - Le bulletin de notes (fusion de tables)

Section intitulée « Activité 2 - Le bulletin de notes (fusion de tables) »

Créer les trois fichiers suivants :

eleves.csv :

id_eleve,nom,prenom,classe
1,Dupont,Marie,1NSI-A
2,Martin,Léo,1NSI-A
3,Durand,Emma,1NSI-B
4,Petit,Hugo,1NSI-B
5,Leroy,Alice,1NSI-A

notes.csv :

id_eleve,matiere,note
1,NSI,15
1,Maths,14
1,Anglais,12
2,NSI,18
2,Maths,10
2,Anglais,16
3,NSI,12
3,Maths,17
3,Anglais,14
4,NSI,8
4,Maths,9
4,Anglais,11
5,NSI,16
5,Maths,15
5,Anglais,17

matieres.csv :

matiere,coefficient
NSI,4
Maths,4
Anglais,3

a. Importer les trois fichiers.

b. Fusionner notes et eleves sur id_eleve, puis fusionner le résultat avec matieres sur matiere.

c. Calculer la moyenne pondérée de chaque élève.

d. Afficher le bulletin complet sous forme de tableau lisible.

e. Déterminer le premier et le dernier de la classe (par moyenne pondérée).

Correction
import csv
def charger_csv(nom_fichier):
table = []
with open(nom_fichier, "r", encoding="utf-8") as f:
for ligne in csv.DictReader(f):
table.append(dict(ligne))
return table
def jointure(t1, t2, cle):
resultat = []
for l1 in t1:
for l2 in t2:
if l1[cle] == l2[cle]:
fusion = {}
fusion.update(l1)
fusion.update(l2)
resultat.append(fusion)
return resultat
# a. Importer
eleves = charger_csv("eleves.csv")
notes = charger_csv("notes.csv")
matieres = charger_csv("matieres.csv")
# Conversions
for n in notes:
n["id_eleve"] = int(n["id_eleve"])
n["note"] = float(n["note"])
for e in eleves:
e["id_eleve"] = int(e["id_eleve"])
for m in matieres:
m["coefficient"] = int(m["coefficient"])
# b. Fusions
etape1 = jointure(notes, eleves, "id_eleve")
complet = jointure(etape1, matieres, "matiere")
# c. Moyenne pondérée par élève
moyennes = {}
for ligne in complet:
nom = f"{ligne['prenom']} {ligne['nom']}"
if nom not in moyennes:
moyennes[nom] = {"somme_ponderee": 0, "somme_coefs": 0}
moyennes[nom]["somme_ponderee"] += ligne["note"] * ligne["coefficient"]
moyennes[nom]["somme_coefs"] += ligne["coefficient"]
# d. Affichage
print(f"{'Élève':<20} {'Moyenne':>8}")
print("-" * 30)
for nom, data in moyennes.items():
moy = data["somme_ponderee"] / data["somme_coefs"]
print(f"{nom:<20} {moy:>8.2f}")
# e. Premier et dernier
classement = sorted(moyennes.items(),
key=lambda x: x[1]["somme_ponderee"] / x[1]["somme_coefs"],
reverse=True)
premier = classement[0]
dernier = classement[-1]
moy_p = premier[1]["somme_ponderee"] / premier[1]["somme_coefs"]
moy_d = dernier[1]["somme_ponderee"] / dernier[1]["somme_coefs"]
print(f"\nPremier : {premier[0]} ({moy_p:.2f})")
print(f"Dernier : {dernier[0]} ({moy_d:.2f})")

Activité 3 - Détective de données (cohérence et nettoyage)

Section intitulée « Activité 3 - Détective de données (cohérence et nettoyage) »

Créer le fichier inscriptions.csv suivant (volontairement plein d'erreurs) :

nom,prenom,age,email,classe
Dupont,Marie,16,[email protected],1NSI-A
Martin,Léo,17,[email protected],1NSI-A
Durand,Emma,seize,[email protected],1NSI-B
,Hugo,15,[email protected],1NSI-B
Petit,Hugo,15,[email protected],1NSI-B
Martin,Léo,17,[email protected],1NSI-A
Leroy,Alice,-3,[email protected],1NSI-A
Bernard,Tom,16,,1NSI-B

a. Importer le fichier. Combien de lignes contient-il ?

b. Identifier tous les problèmes :

  • Valeurs manquantes (nom vide, email vide)
  • Types incorrects (age = "seize")
  • Valeurs aberrantes (age = -3)
  • Doublons (lignes identiques)

c. Écrire une fonction nettoyer(table) qui :

  1. Supprime les doublons exacts
  2. Signale les lignes avec des valeurs manquantes
  3. Signale les lignes avec un age non numérique ou hors [10, 20]

d. Exporter la table nettoyée dans inscriptions_propres.csv.

Correction
import csv
# a. Import
table = []
with open("inscriptions.csv", "r", encoding="utf-8") as f:
for ligne in csv.DictReader(f):
table.append(dict(ligne))
print(f"Nombre de lignes : {len(table)}") # 8
# b. Identification des problèmes
print("\n--- Analyse ---")
for i, ligne in enumerate(table):
problemes = []
# Valeurs manquantes
for cle, val in ligne.items():
if val.strip() == "":
problemes.append(f"{cle} manquant")
# Type de age
try:
age = int(ligne["age"])
if age < 10 or age > 20:
problemes.append(f"age aberrant ({age})")
except ValueError:
problemes.append(f"age non numerique ('{ligne['age']}')")
if problemes:
print(f"Ligne {i} ({ligne.get('nom', '?')}) : {', '.join(problemes)}")
# Doublons
for i in range(len(table)):
for j in range(i + 1, len(table)):
if table[i] == table[j]:
print(f"Doublon : lignes {i} et {j}")
# c. Nettoyage
def nettoyer(table):
propre = []
vus = []
for ligne in table:
# Suppression des doublons
cle_unique = (ligne["nom"], ligne["prenom"], ligne["email"])
if cle_unique in vus:
continue
vus.append(cle_unique)
# Valeurs manquantes
if any(v.strip() == "" for v in ligne.values()):
print(f" Ignoree (valeur manquante) : {ligne}")
continue
# Conversion et validation de l'age
try:
ligne["age"] = int(ligne["age"])
except ValueError:
print(f" Ignoree (age invalide) : {ligne}")
continue
if ligne["age"] < 10 or ligne["age"] > 20:
print(f" Ignoree (age aberrant) : {ligne}")
continue
propre.append(ligne)
return propre
print("\n--- Nettoyage ---")
propre = nettoyer(table)
print(f"\nLignes conservees : {len(propre)}")
# d. Export
with open("inscriptions_propres.csv", "w", encoding="utf-8", newline="") as f:
colonnes = ["nom", "prenom", "age", "email", "classe"]
ecrivain = csv.DictWriter(f, fieldnames=colonnes)
ecrivain.writeheader()
for ligne in propre:
ecrivain.writerow(ligne)

Projet final - Analyse d'un championnat sportif

Section intitulée « Projet final - Analyse d'un championnat sportif »

a. Créer un fichier equipes.csv contenant 6 équipes avec les colonnes : id_equipe, nom, ville, stade.

b. Créer un fichier matchs.csv contenant 10 matchs avec les colonnes : id_match, id_equipe_dom, id_equipe_ext, buts_dom, buts_ext.

c. Importer les deux fichiers.

d. Fusionner pour afficher, pour chaque match, les noms des équipes et le score (au lieu des identifiants).

e. Calculer pour chaque équipe : nombre de matchs joués, victoires, nuls, défaites, buts marqués, buts encaissés, différence de buts, points (3 pour une victoire, 1 pour un nul, 0 pour une défaite).

f. Afficher le classement trié par points décroissants (puis par différence de buts en cas d'égalité).

g. Calculer le nombre total de buts marqués dans le championnat.

h. Trouver le match avec le plus de buts et la plus grosse victoire.

i. Exporter le classement final dans un fichier classement.csv.

Correction
import csv
# Exemple de données
equipes = [
{"id_equipe": 1, "nom": "Lyon", "ville": "Lyon", "stade": "Groupama"},
{"id_equipe": 2, "nom": "Marseille", "ville": "Marseille", "stade": "Velodrome"},
{"id_equipe": 3, "nom": "Paris", "ville": "Paris", "stade": "Parc des Princes"},
{"id_equipe": 4, "nom": "Lille", "ville": "Lille", "stade": "Pierre Mauroy"},
{"id_equipe": 5, "nom": "Nantes", "ville": "Nantes", "stade": "Beaujoire"},
{"id_equipe": 6, "nom": "Toulouse", "ville": "Toulouse", "stade": "Stadium"}
]
matchs = [
{"id_match": 1, "id_dom": 1, "id_ext": 2, "buts_dom": 2, "buts_ext": 1},
{"id_match": 2, "id_dom": 3, "id_ext": 4, "buts_dom": 3, "buts_ext": 0},
{"id_match": 3, "id_dom": 5, "id_ext": 6, "buts_dom": 1, "buts_ext": 1},
{"id_match": 4, "id_dom": 2, "id_ext": 3, "buts_dom": 0, "buts_ext": 2},
{"id_match": 5, "id_dom": 4, "id_ext": 1, "buts_dom": 1, "buts_ext": 3},
{"id_match": 6, "id_dom": 6, "id_ext": 5, "buts_dom": 2, "buts_ext": 0},
{"id_match": 7, "id_dom": 1, "id_ext": 3, "buts_dom": 1, "buts_ext": 1},
{"id_match": 8, "id_dom": 2, "id_ext": 4, "buts_dom": 2, "buts_ext": 2},
{"id_match": 9, "id_dom": 5, "id_ext": 1, "buts_dom": 0, "buts_ext": 1},
{"id_match": 10, "id_dom": 6, "id_ext": 3, "buts_dom": 1, "buts_ext": 4}
]
# Dictionnaire id -> nom pour accès rapide
noms = {e["id_equipe"]: e["nom"] for e in equipes}
# Classement
stats = {}
for e in equipes:
stats[e["id_equipe"]] = {
"nom": e["nom"], "mj": 0, "v": 0, "n": 0, "d": 0,
"bp": 0, "bc": 0, "diff": 0, "pts": 0
}
for m in matchs:
dom = m["id_dom"]
ext = m["id_ext"]
bd = m["buts_dom"]
be = m["buts_ext"]
stats[dom]["mj"] += 1
stats[ext]["mj"] += 1
stats[dom]["bp"] += bd
stats[dom]["bc"] += be
stats[ext]["bp"] += be
stats[ext]["bc"] += bd
if bd > be:
stats[dom]["v"] += 1
stats[dom]["pts"] += 3
stats[ext]["d"] += 1
elif bd < be:
stats[ext]["v"] += 1
stats[ext]["pts"] += 3
stats[dom]["d"] += 1
else:
stats[dom]["n"] += 1
stats[ext]["n"] += 1
stats[dom]["pts"] += 1
stats[ext]["pts"] += 1
for s in stats.values():
s["diff"] = s["bp"] - s["bc"]
# Affichage du classement
classement = sorted(stats.values(),
key=lambda s: (s["pts"], s["diff"]),
reverse=True)
print(f"{'Pos':<4} {'Equipe':<12} {'MJ':<4} {'V':<4} {'N':<4} {'D':<4} "
f"{'BP':<4} {'BC':<4} {'Diff':<5} {'Pts':<4}")
print("-" * 55)
for i, s in enumerate(classement):
diff = f"+{s['diff']}" if s['diff'] > 0 else str(s['diff'])
print(f"{i+1:<4} {s['nom']:<12} {s['mj']:<4} {s['v']:<4} {s['n']:<4} "
f"{s['d']:<4} {s['bp']:<4} {s['bc']:<4} {diff:<5} {s['pts']:<4}")
# Statistiques
total_buts = sum(m["buts_dom"] + m["buts_ext"] for m in matchs)
print(f"\nTotal buts : {total_buts}")
plus_de_buts = max(matchs, key=lambda m: m["buts_dom"] + m["buts_ext"])
print(f"Match avec le plus de buts : {noms[plus_de_buts['id_dom']]} "
f"{plus_de_buts['buts_dom']}-{plus_de_buts['buts_ext']} "
f"{noms[plus_de_buts['id_ext']]}")