add computing from osm history
This commit is contained in:
parent
da60f964ab
commit
66bbce5e85
13 changed files with 3921 additions and 0 deletions
395
counting_osm_objects/generate_graph.py
Executable file
395
counting_osm_objects/generate_graph.py
Executable file
|
@ -0,0 +1,395 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Script pour générer un graphique montrant l'évolution du nombre d'objets OSM
|
||||
à partir d'un fichier CSV
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.dates as mdates
|
||||
from datetime import datetime
|
||||
import argparse
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""Parse command line arguments."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Génère un graphique à partir des données CSV d'objets OSM."
|
||||
)
|
||||
parser.add_argument(
|
||||
"csv_file", help="Chemin vers le fichier CSV contenant les données"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output", "-o", help="Chemin de sortie pour le graphique (PNG)", default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"--insee", "-i", help="Code INSEE de la commune à analyser", default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"--period",
|
||||
"-p",
|
||||
help="Période à analyser (annual, monthly, daily)",
|
||||
default="monthly",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def load_data(csv_file, insee_code=None, period="monthly"):
|
||||
"""
|
||||
Charge les données depuis le fichier CSV.
|
||||
|
||||
Args:
|
||||
csv_file: Chemin vers le fichier CSV
|
||||
insee_code: Code INSEE de la commune à filtrer (optionnel)
|
||||
period: Période à analyser (annual, monthly, daily)
|
||||
|
||||
Returns:
|
||||
DataFrame pandas contenant les données filtrées
|
||||
"""
|
||||
# Charger le CSV avec gestion des erreurs pour les lignes mal formatées
|
||||
try:
|
||||
df = pd.read_csv(csv_file, error_bad_lines=False, warn_bad_lines=True)
|
||||
except TypeError: # Pour les versions plus récentes de pandas
|
||||
df = pd.read_csv(csv_file, on_bad_lines="skip")
|
||||
|
||||
# Vérifier si le CSV a la structure attendue
|
||||
if "date" in df.columns:
|
||||
# Format de CSV avec colonne 'date' directement
|
||||
try:
|
||||
df["date"] = pd.to_datetime(df["date"])
|
||||
except:
|
||||
# Si la conversion échoue, essayer différents formats
|
||||
try:
|
||||
if df["date"].iloc[0].count("-") == 2: # Format YYYY-MM-DD
|
||||
df["date"] = pd.to_datetime(df["date"], format="%Y-%m-%d")
|
||||
elif df["date"].iloc[0].count("-") == 1: # Format YYYY-MM
|
||||
df["date"] = pd.to_datetime(df["date"], format="%Y-%m")
|
||||
else:
|
||||
df["date"] = pd.to_datetime(df["date"])
|
||||
except:
|
||||
print("Erreur: Impossible de convertir la colonne 'date'.")
|
||||
sys.exit(1)
|
||||
elif "periode" in df.columns:
|
||||
# Ancien format avec colonne 'periode'
|
||||
# Filtrer par période
|
||||
df = df[df["periode"] == period]
|
||||
|
||||
# Filtrer par code INSEE si spécifié
|
||||
if insee_code and "code_insee" in df.columns:
|
||||
df = df[df["code_insee"] == insee_code]
|
||||
|
||||
# Convertir les dates en objets datetime
|
||||
if period == "annual" and "annee" in df.columns:
|
||||
df["date"] = pd.to_datetime(df["annee"].astype(str), format="%Y")
|
||||
elif period == "monthly":
|
||||
# Vérifier si la première colonne contient déjà un format de date mensuel (YYYY-MM)
|
||||
first_col = df.columns[0]
|
||||
first_val = str(df.iloc[0, 0]) if not df.empty else ""
|
||||
|
||||
if first_col == "annee_mois" or (len(first_val) >= 7 and "-" in first_val):
|
||||
# Si la première colonne est 'annee_mois' ou contient une date au format YYYY-MM
|
||||
df["date"] = pd.to_datetime(df.iloc[:, 0].astype(str), format="%Y-%m")
|
||||
elif "annee" in df.columns:
|
||||
# Sinon, utiliser la colonne 'annee' et ajouter un mois fictif (janvier)
|
||||
df["date"] = pd.to_datetime(
|
||||
df["annee"].astype(str) + "-01", format="%Y-%m"
|
||||
)
|
||||
elif period == "daily":
|
||||
# Vérifier si la première colonne contient déjà un format de date quotidien (YYYY-MM-DD)
|
||||
first_col = df.columns[0]
|
||||
first_val = str(df.iloc[0, 0]) if not df.empty else ""
|
||||
|
||||
if first_col == "jour" or (
|
||||
len(first_val) >= 10 and first_val.count("-") == 2
|
||||
):
|
||||
# Si la première colonne est 'jour' ou contient une date au format YYYY-MM-DD
|
||||
df["date"] = pd.to_datetime(
|
||||
df.iloc[:, 0].astype(str), format="%Y-%m-%d"
|
||||
)
|
||||
elif "annee" in df.columns:
|
||||
# Sinon, utiliser la colonne 'annee' et ajouter un jour fictif (1er janvier)
|
||||
df["date"] = pd.to_datetime(
|
||||
df["annee"].astype(str) + "-01-01", format="%Y-%m-%d"
|
||||
)
|
||||
else:
|
||||
# Si aucune colonne de date n'est trouvée, essayer d'utiliser la première colonne
|
||||
try:
|
||||
df["date"] = pd.to_datetime(df.iloc[:, 0])
|
||||
except:
|
||||
print("Erreur: Impossible de trouver ou convertir une colonne de date.")
|
||||
sys.exit(1)
|
||||
|
||||
# Filtrer par code INSEE si spécifié et si la colonne 'zone' contient des codes INSEE
|
||||
if insee_code and "zone" in df.columns and not "code_insee" in df.columns:
|
||||
# Vérifier si la zone contient le code INSEE
|
||||
if any(
|
||||
zone.endswith(insee_code) for zone in df["zone"] if isinstance(zone, str)
|
||||
):
|
||||
df = df[df["zone"].str.endswith(insee_code)]
|
||||
|
||||
# Trier par date
|
||||
df = df.sort_values("date")
|
||||
|
||||
return df
|
||||
|
||||
|
||||
def generate_graph(df, output_path=None):
|
||||
"""
|
||||
Génère un graphique montrant l'évolution du nombre d'objets dans le temps.
|
||||
|
||||
Args:
|
||||
df: DataFrame pandas contenant les données
|
||||
output_path: Chemin de sortie pour le graphique (optionnel)
|
||||
"""
|
||||
# Créer une figure avec une taille adaptée
|
||||
plt.figure(figsize=(12, 8))
|
||||
|
||||
# Déterminer la colonne pour les types d'objets (type_objet ou theme)
|
||||
type_column = "type_objet" if "type_objet" in df.columns else "theme"
|
||||
label_objet = "objet"
|
||||
# Obtenir la liste des types d'objets uniques
|
||||
if type_column in df.columns:
|
||||
object_types = df[type_column].unique()
|
||||
|
||||
# Créer un graphique pour chaque type d'objet
|
||||
for obj_type in object_types:
|
||||
# Filtrer les données pour ce type d'objet
|
||||
obj_data = df[df[type_column] == obj_type]
|
||||
|
||||
# Filtrer les valeurs nulles
|
||||
obj_data_filtered = obj_data[obj_data["nombre_total"].notna()]
|
||||
|
||||
if not obj_data_filtered.empty:
|
||||
# Tracer la ligne pour le nombre total d'objets (sans marqueurs, avec courbe lissée)
|
||||
line, = plt.plot(
|
||||
obj_data_filtered["date"],
|
||||
obj_data_filtered["nombre_total"],
|
||||
linestyle="-",
|
||||
label=f"{obj_type}",
|
||||
)
|
||||
label_objet = obj_type
|
||||
|
||||
# Ajouter un remplissage pastel sous la courbe
|
||||
plt.fill_between(
|
||||
obj_data_filtered["date"],
|
||||
obj_data_filtered["nombre_total"],
|
||||
alpha=0.3,
|
||||
color=line.get_color()
|
||||
)
|
||||
else:
|
||||
# Si aucune colonne de type n'est trouvée, tracer simplement le nombre total
|
||||
# Filtrer les valeurs nulles
|
||||
df_filtered = df[df["nombre_total"].notna()]
|
||||
|
||||
if not df_filtered.empty:
|
||||
# Tracer la ligne pour le nombre total d'objets (sans marqueurs, avec courbe lissée)
|
||||
line, = plt.plot(
|
||||
df_filtered["date"],
|
||||
df_filtered["nombre_total"],
|
||||
linestyle="-",
|
||||
label="",
|
||||
)
|
||||
|
||||
# Ajouter un remplissage pastel sous la courbe
|
||||
plt.fill_between(
|
||||
df_filtered["date"],
|
||||
df_filtered["nombre_total"],
|
||||
alpha=0.3,
|
||||
color=line.get_color()
|
||||
)
|
||||
|
||||
# Configurer les axes et les légendes
|
||||
plt.xlabel("Date")
|
||||
plt.ylabel("Nombre d'objets")
|
||||
|
||||
plt.grid(True, linestyle="--", alpha=0.7)
|
||||
|
||||
# Formater l'axe des x pour afficher les dates correctement
|
||||
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m"))
|
||||
plt.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=6))
|
||||
plt.gcf().autofmt_xdate()
|
||||
|
||||
# Ajouter une légende
|
||||
plt.legend()
|
||||
|
||||
zone_label = ""
|
||||
# Ajouter des informations sur la commune
|
||||
if "code_insee" in df.columns and len(df["code_insee"].unique()) == 1:
|
||||
insee_code = df["code_insee"].iloc[0]
|
||||
plt.figtext(0.02, 0.02, f"Commune: {insee_code}", fontsize=10)
|
||||
zone_label = insee_code
|
||||
elif "zone" in df.columns and len(df["zone"].unique()) == 1:
|
||||
zone = df["zone"].iloc[0]
|
||||
plt.figtext(0.02, 0.02, f"Zone: {zone}", fontsize=10)
|
||||
zone_label = zone
|
||||
|
||||
plt.title(f"{zone_label} : {label_objet} dans le temps")
|
||||
|
||||
# Ajouter la date de génération
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
plt.figtext(0.98, 0.02, f"Généré le: {now}", fontsize=8, ha="right")
|
||||
|
||||
# Ajuster la mise en page
|
||||
plt.tight_layout()
|
||||
|
||||
# Sauvegarder ou afficher le graphique
|
||||
if output_path:
|
||||
plt.savefig(output_path, dpi=300, bbox_inches="tight")
|
||||
print(f"Graphique sauvegardé: {output_path}")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
|
||||
def generate_completion_graph(df, output_path=None):
|
||||
"""
|
||||
Génère un graphique montrant l'évolution du taux de complétion des attributs dans le temps.
|
||||
|
||||
Args:
|
||||
df: DataFrame pandas contenant les données
|
||||
output_path: Chemin de sortie pour le graphique (optionnel)
|
||||
"""
|
||||
# Vérifier si la colonne de pourcentage de complétion existe
|
||||
if "pourcentage_completion" not in df.columns:
|
||||
print(
|
||||
"Avertissement: La colonne 'pourcentage_completion' n'existe pas dans le CSV. Le graphique de complétion ne sera pas généré."
|
||||
)
|
||||
return
|
||||
|
||||
# Créer une figure avec une taille adaptée
|
||||
plt.figure(figsize=(12, 8))
|
||||
|
||||
# Déterminer la colonne pour les types d'objets (type_objet ou theme)
|
||||
type_column = "type_objet" if "type_objet" in df.columns else "theme"
|
||||
|
||||
# Obtenir la liste des types d'objets uniques
|
||||
if type_column in df.columns:
|
||||
object_types = df[type_column].unique()
|
||||
|
||||
# Créer un graphique pour chaque type d'objet
|
||||
for obj_type in object_types:
|
||||
# Filtrer les données pour ce type d'objet
|
||||
obj_data = df[df[type_column] == obj_type]
|
||||
|
||||
# Filtrer les valeurs nulles
|
||||
obj_data_filtered = obj_data[obj_data["pourcentage_completion"].notna()]
|
||||
|
||||
if not obj_data_filtered.empty:
|
||||
# Tracer la ligne pour le taux de complétion (sans marqueurs, avec courbe lissée)
|
||||
line, = plt.plot(
|
||||
obj_data_filtered["date"],
|
||||
obj_data_filtered["pourcentage_completion"],
|
||||
linestyle="-",
|
||||
label=f"{obj_type} - Complétion (%)",
|
||||
)
|
||||
|
||||
# Ajouter un remplissage pastel sous la courbe
|
||||
plt.fill_between(
|
||||
obj_data_filtered["date"],
|
||||
obj_data_filtered["pourcentage_completion"],
|
||||
alpha=0.3,
|
||||
color=line.get_color()
|
||||
)
|
||||
else:
|
||||
# Si aucune colonne de type n'est trouvée, tracer simplement le taux de complétion global
|
||||
# Filtrer les valeurs nulles
|
||||
df_filtered = df[df["pourcentage_completion"].notna()]
|
||||
|
||||
if not df_filtered.empty:
|
||||
# Tracer la ligne pour le taux de complétion (sans marqueurs, avec courbe lissée)
|
||||
line, = plt.plot(
|
||||
df_filtered["date"],
|
||||
df_filtered["pourcentage_completion"],
|
||||
linestyle="-",
|
||||
label="Complétion (%)",
|
||||
)
|
||||
|
||||
# Ajouter un remplissage pastel sous la courbe
|
||||
plt.fill_between(
|
||||
df_filtered["date"],
|
||||
df_filtered["pourcentage_completion"],
|
||||
alpha=0.3,
|
||||
color=line.get_color()
|
||||
)
|
||||
|
||||
# Configurer les axes et les légendes
|
||||
plt.xlabel("Date")
|
||||
plt.ylabel("Complétion (%)")
|
||||
plt.title("Évolution du taux de complétion dans le temps")
|
||||
plt.grid(True, linestyle="--", alpha=0.7)
|
||||
|
||||
# Définir les limites de l'axe y entre 0 et 100%
|
||||
plt.ylim(0, 100)
|
||||
|
||||
# Formater l'axe des x pour afficher les dates correctement
|
||||
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m"))
|
||||
plt.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=6))
|
||||
plt.gcf().autofmt_xdate()
|
||||
|
||||
# Ajouter une légende
|
||||
plt.legend()
|
||||
|
||||
# Ajouter des informations sur la commune
|
||||
if "code_insee" in df.columns and len(df["code_insee"].unique()) == 1:
|
||||
insee_code = df["code_insee"].iloc[0]
|
||||
plt.figtext(0.02, 0.02, f"Commune: {insee_code}", fontsize=10)
|
||||
elif "zone" in df.columns and len(df["zone"].unique()) == 1:
|
||||
zone = df["zone"].iloc[0]
|
||||
plt.figtext(0.02, 0.02, f"Zone: {zone}", fontsize=10)
|
||||
|
||||
# Ajouter la date de génération
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
plt.figtext(0.98, 0.02, f"Généré le: {now}", fontsize=8, ha="right")
|
||||
|
||||
# Ajuster la mise en page
|
||||
plt.tight_layout()
|
||||
|
||||
# Sauvegarder ou afficher le graphique
|
||||
if output_path:
|
||||
# Modifier le nom du fichier pour indiquer qu'il s'agit du taux de complétion
|
||||
base, ext = os.path.splitext(output_path)
|
||||
completion_path = f"{base}_completion{ext}"
|
||||
plt.savefig(completion_path, dpi=300, bbox_inches="tight")
|
||||
print(f"Graphique de complétion sauvegardé: {completion_path}")
|
||||
else:
|
||||
plt.show()
|
||||
|
||||
|
||||
def main():
|
||||
"""Fonction principale."""
|
||||
# Analyser les arguments de la ligne de commande
|
||||
args = parse_args()
|
||||
|
||||
# Vérifier que le fichier CSV existe
|
||||
if not os.path.isfile(args.csv_file):
|
||||
print(f"Erreur: Le fichier {args.csv_file} n'existe pas.")
|
||||
sys.exit(1)
|
||||
|
||||
# Charger les données
|
||||
df = load_data(args.csv_file, args.insee, args.period)
|
||||
|
||||
# Vérifier qu'il y a des données
|
||||
if df.empty:
|
||||
print(f"Aucune donnée trouvée pour la période {args.period}.")
|
||||
sys.exit(1)
|
||||
|
||||
# Déterminer le chemin de sortie si non spécifié
|
||||
if not args.output:
|
||||
# Utiliser le même nom que le fichier CSV mais avec l'extension .png
|
||||
base_name = os.path.splitext(args.csv_file)[0]
|
||||
output_path = f"{base_name}_{args.period}_graph.png"
|
||||
else:
|
||||
output_path = args.output
|
||||
|
||||
# Générer les graphiques
|
||||
generate_graph(df, output_path)
|
||||
generate_completion_graph(df, output_path)
|
||||
|
||||
print("Graphiques générés avec succès!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Add table
Add a link
Reference in a new issue