File size: 7,947 Bytes
f7d9075
 
 
 
 
 
 
 
 
b40f96c
f7d9075
 
 
 
 
 
 
 
b40f96c
f7d9075
 
 
 
 
 
 
 
b40f96c
f7d9075
b40f96c
f7d9075
 
 
 
 
 
 
 
 
 
 
 
b40f96c
f7d9075
 
 
 
 
 
b40f96c
f7d9075
 
 
 
 
b40f96c
f7d9075
 
 
 
 
 
b40f96c
f7d9075
 
 
 
 
 
 
 
 
 
 
 
b40f96c
f7d9075
 
 
 
 
 
b40f96c
f7d9075
b40f96c
f7d9075
 
b40f96c
 
 
 
f7d9075
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b40f96c
 
f7d9075
 
 
 
 
 
 
b40f96c
f7d9075
 
 
b40f96c
f7d9075
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b40f96c
 
 
 
 
f7d9075
 
b40f96c
f7d9075
 
 
b40f96c
 
 
 
 
f7d9075
b40f96c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f7d9075
b40f96c
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204

import streamlit as st
import pandas as pd
import numpy as np
import pickle
from pathlib import Path
import os
import boto3
import statsmodels.api as sm
import plotly.express as px

from dotenv import load_dotenv
from io import BytesIO

# Nettoyage du cache au démarrage
st.cache_data.clear()
st.cache_resource.clear()

# Configuration page pleine largeur 
st.set_page_config(
    page_title="Simulation Geodechets",
    page_icon="♻️",
    layout="wide"
)

# Titre de l'app
st.title("Application Geodechets")
st.write("### Simulation de production de déchets par département")

# Chargement des secrets AWS
load_dotenv("secrets.env")
AWS_KEY = os.getenv("AWS_ACCESS_KEY_ID")
AWS_SECRET = os.getenv("AWS_SECRET_ACCESS_KEY")

# Connexion S3
s3 = boto3.client(
    's3',
    aws_access_key_id=AWS_KEY,
    aws_secret_access_key=AWS_SECRET,
    region_name="eu-west-3"
)

# Fonction pour charger CSV depuis S3 
@st.cache_data
def load_data_from_s3(bucket_name, key):
    obj = s3.get_object(Bucket=bucket_name, Key=key)
    df = pd.read_csv(BytesIO(obj['Body'].read()))
    return df

# Fonction pour charger Pickle depuis S3
@st.cache_resource
def load_pickle_from_s3(bucket_name, key):
    obj = s3.get_object(Bucket=bucket_name, Key=key)
    return pickle.load(BytesIO(obj['Body'].read()))

# Chargement des données CSV 
try:
    df = load_data_from_s3("mygeodechet", "df_reduced.csv")
except Exception as e:
    st.error(f"Erreur lors du chargement des données : {e}")
    df = None

# Chargement des modèles Pickle
model_files = {
    "Déblais et gravats": "models/model_ols_Déblais_gravats.pkl",
    "Déchets verts": "models/model_ols_Déchets_verts.pkl",
    "Encombrants": "models/model_ols_Encombrants.pkl",
    "Matériaux recyclables": "models/model_ols_Matériaux_recyclables.pkl",
    "Total autres déchets": "models/model_ols_Total_autres_dechets.pkl"
}

models = {}
for label, path in model_files.items():
    try:
        models[label] = load_pickle_from_s3("mygeodechet", path)
        # st.success(f"✅ Modèle '{label}' chargé depuis S3 !")
    except Exception as e:
        st.error(f"Erreur lors du chargement du modèle '{label}' : {e}")
        models[label] = None

# Sélection du département
if df is not None:
    departement = st.selectbox("Choix du département :", sorted(df["Département"].unique()))
    df_dep = df[df["Département"] == departement].copy()
    st.dataframe(df_dep.head(7), use_container_width=True)

# -------------------------------------------------------------------------------------------------
# Ajustement des paramètres sur 4 colonnes, avant simultaion, pour l'année 2021 
# selection année 2021
df_2021 = df_dep[df_dep["année"] == 2021]

# liste des variables à slider
numeric_cols = [
    'pop_globale', 'densité',
    'tranche_age_0-24', 'tranche_age_25-59', 'tranche_age_60+',
    'csp1_agriculteurs', 'csp2_artisans_commerçant_chef_entreprises',
    'csp3_cadres_professions_intellectuelles', 'csp4_professions_intermédiaires',
    'csp5_employés', 'csp6_ouvriers', 'csp7_retraités', 'csp8_sans_activité',
    'nbre_entreprises', 'nbre_entreprises_agricole', 'nbre_entreprises_industrie',
    'nbre_entreprises_service', 'nb_salaries_secteur_agricole',
    'nb_salaries_secteur_industrie', 'nb_salaries_secteur_service'
    ]
  
# titre
st.write("### Paramètres à ajuster")

col1, col2, col3, col4 = st.columns(4)
cols = [col1, col2, col3, col4]
user_inputs = {}

for i, feature in enumerate(numeric_cols):
    min_val = float(df_dep[feature].min())*0.5 # 50% de moins
    max_val = float(df_dep[feature].max()) *1.5 # 50% de plus
    mean_val = float(df_dep[feature].mean())

    target_col = cols[i % 4]   # distribue à travers les 4 colonnes
    with target_col:
            # slider avec step raisonnable
            step = max((max_val - min_val) / 50.0, 1.0)
            user_inputs[feature] = st.slider(feature, min_val, max_val, float(mean_val), step=step, key=f"sl_{feature}")

# -------------------------------------------------------------------------------------------------------
# Partie simulation 
st.markdown("---")
# st.markdown("### Lancez la simulation")
if st.button("Lancer la simulation"):
    # Préparer X_user
    X_user = pd.DataFrame([user_inputs])

    # Nettoyage / conversion : remplace virgules (si jamais), force numérique et fillna
    X_user = X_user.apply(lambda col: pd.to_numeric(col.astype(str).str.replace(",", "."), errors="coerce"))
    X_user = X_user.fillna(0.0)

    results = []

    for label, pipeline in models.items():
        if pipeline is None:
            st.error(f"Modèle manquant pour {label}")
            results.append({"Type de déchet": label, "Prédiction (tonnes)": "Erreur"})
            continue

        try:
            preprocessor = pipeline["preprocessor"]
            ols_model = pipeline["ols_model"]

            # Ajouter toutes les colonnes attendues par le preprocessor avec 0.0 si manquantes
            for feat in preprocessor.feature_names_in_:
                if feat not in X_user.columns:
                    X_user[feat] = 0.0

            # Réordonner et forcer float uniquement pour les colonnes du preprocessor
            X_sim = X_user[preprocessor.feature_names_in_].astype(float)

            # Transformation + constante
            X_transformed = preprocessor.transform(X_sim)
            X_const = sm.add_constant(X_transformed, has_constant='add')

            # Prédiction
            pred = ols_model.predict(X_const)[0]
            results.append({"Type de déchet": label, "Prédiction (tonnes)": round(float(pred), 2)})

        except Exception as e:
            st.error(f"Erreur modèle {label}: {e}")
            results.append({"Type de déchet": label, "Prédiction (tonnes)": "Erreur"})

# -----------------------------------------------------------------------------------------------------------------
# 2éme Partie

    # Affichage résultats en deux colonnes (résultats / graph) 
    res_col, graph_col = st.columns([1, 2])
    with res_col:
        df_results = pd.DataFrame(results)
        st.write("#### Résultats Prédictions")
        st.dataframe(df_results, use_container_width=True)

    with graph_col:
        st.write("#### Analyse comparative des déchets : 2021 (réel) vs modèle prédictif")

        # Filtrer uniquement les résultats numériques
        good = df_results[df_results["Prédiction (tonnes)"].apply(lambda v: isinstance(v, (int, float)))].copy()

        if not good.empty:
            # Créer un DataFrame avec les valeurs 2021
            df_2021_vals = pd.DataFrame({
                'Type de déchet': ['Déblais et gravats', 'Déchets verts', 'Encombrants',
                                'Matériaux recyclables', 'Total autres déchets'],
                '2021': [
                    float(df_2021['Déblais_gravats'].values[0]),
                    float(df_2021['Déchets_verts'].values[0]),
                    float(df_2021['Encombrants'].values[0]),
                    float(df_2021['Matériaux_recyclables'].values[0]),
                    float(df_2021['Total_autres_dechets'].values[0])
                ]
            })
            # Fusionner avec les prédictions
            df_plot = pd.merge(good, df_2021_vals, on='Type de déchet') # Le merge sert à mettre ces données côte à côte pour le même type de déchet.
            df_plot = df_plot.melt(id_vars='Type de déchet', value_vars=['2021', 'Prédiction (tonnes)'],
                                var_name='Année', value_name='Tonnes') # attend un format “long” pour faire des barres côte à côte par catégorie.

            # Graphique barres côte à côte
            fig = px.bar(df_plot, x='Type de déchet', y='Tonnes', color='Année', barmode='group', height=300)
            st.plotly_chart(fig, use_container_width=True)
        else:
            st.info("Aucun résultat numérique à afficher")