Spaces:
Sleeping
Sleeping
| # import altair as alt | |
| # import numpy as np | |
| # import pandas as pd | |
| # import streamlit as st | |
| # """ | |
| # # Welcome to Streamlit! | |
| # Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:. | |
| # If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community | |
| # forums](https://discuss.streamlit.io). | |
| # In the meantime, below is an example of what you can do with just a few lines of code: | |
| # """ | |
| # num_points = st.slider("Number of points in spiral", 1, 10000, 1100) | |
| # num_turns = st.slider("Number of turns in spiral", 1, 300, 31) | |
| # indices = np.linspace(0, 1, num_points) | |
| # theta = 2 * np.pi * num_turns * indices | |
| # radius = indices | |
| # x = radius * np.cos(theta) | |
| # y = radius * np.sin(theta) | |
| # df = pd.DataFrame({ | |
| # "x": x, | |
| # "y": y, | |
| # "idx": indices, | |
| # "rand": np.random.randn(num_points), | |
| # }) | |
| # st.altair_chart(alt.Chart(df, height=700, width=700) | |
| # .mark_point(filled=True) | |
| # .encode( | |
| # x=alt.X("x", axis=None), | |
| # y=alt.Y("y", axis=None), | |
| # color=alt.Color("idx", legend=None, scale=alt.Scale()), | |
| # size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])), | |
| # )) | |
| # Import des bibliothéques | |
| import streamlit as st | |
| import pandas as pd | |
| import pickle | |
| import matplotlib.pyplot as plt | |
| from dotenv import load_dotenv | |
| import os | |
| import io | |
| from io import StringIO | |
| import boto3 | |
| import numpy as np | |
| import shap | |
| from sklearn.linear_model import LinearRegression | |
| from langchain_mistralai import ChatMistralAI | |
| from langchain_core.output_parsers import StrOutputParser | |
| # Charge les variables d'environnement | |
| load_dotenv("secrets.env") | |
| # Initialise le client S3 | |
| s3 = boto3.client( | |
| 's3', | |
| aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"), | |
| aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"), | |
| region_name="eu-west-3" | |
| ) | |
| # 1. Charge df_dummies_2019.csv → Pour les VALEURS PAR DÉFAUT des sliders | |
| obj_dummies = s3.get_object(Bucket="mygeodechet", Key="df_dummies_2019.csv") | |
| dummies_data = obj_dummies['Body'].read() | |
| df = pd.read_csv(io.BytesIO(dummies_data)).drop(columns=["Unnamed: 0"], errors="ignore") | |
| # 2. Charge df_reduced.csv → Pour les PRÉDICTIONS (input des modèles) | |
| obj_reduced = s3.get_object(Bucket="mygeodechet", Key="df_reduced.csv") | |
| reduced_data = obj_reduced['Body'].read() | |
| observed_df = pd.read_csv(io.BytesIO(reduced_data)).drop(columns=["Unnamed: 0"], errors="ignore") | |
| # # 1. Charge le CSV (df_dummies_2019.csv) | |
| # obj_csv = s3.get_object(Bucket="mygeodechet", Key="df_dummies_2019.csv") | |
| # csv_data = obj_csv['Body'].read() # Lit le contenu binaire | |
| # df = pd.read_csv(io.BytesIO(csv_data)).drop(columns=["Unnamed: 0"], errors="ignore") | |
| # # 2. Charge l'Excel (data_wip_v5.xlsx) | |
| # obj_excel = s3.get_object(Bucket="mygeodechet", Key="data_wip_v5.xlsx") | |
| # excel_data = obj_excel['Body'].read() # Lit le contenu binaire | |
| # observed_df = pd.read_excel(io.BytesIO(excel_data)) # Utilise BytesIO | |
| # # Chargement des données | |
| # df = pd.read_csv("https://mygeodechet.s3.eu-west-3.amazonaws.com/df_dummies_2019.csv").drop(columns=["Unnamed: 0"], errors="ignore") | |
| # observed_df = pd.read_excel("https://mygeodechet.s3.eu-west-3.amazonaws.com/data_wip_v5.xlsx") | |
| # liste des départements présents dans les colonnes du df, sans le préfixe "Département_". | |
| departements = [col.replace("Département_", "") for col in df.columns if col.startswith("Département_")] | |
| # Mise en page | |
| st.set_page_config(layout="wide") | |
| st.markdown("<h1 style='text-align: center;'>♻️ Simulateur de production de déchets par département</h1>", unsafe_allow_html=True) | |
| # Titre + Choix département alignés | |
| top_col1, top_col2 = st.columns([1, 2]) | |
| with top_col1: | |
| st.markdown("<h3 style='text-align: center;'>📍 Choix du département</h3>", unsafe_allow_html=True) | |
| with top_col2: | |
| st.markdown("<h3 style='text-align: center;'>📈 Comparaison entre valeurs observées et prédites</h3>", unsafe_allow_html=True) | |
| # Séparation en colonnes | |
| top_input_col, chart_col = st.columns([1, 2]) | |
| with top_input_col: | |
| selected_dept = st.selectbox("Sélectionner un département", sorted(departements), index=sorted(departements).index("Ain") if "Ain" in departements else 0) | |
| row_default = df[df[f"Département_{selected_dept}"] == 1].iloc[0] | |
| default_dict = row_default.to_dict() | |
| st.subheader("⚙️ Paramètres modifiables") | |
| form_input = {} | |
| categories = { | |
| "📊 Population": [ | |
| "densité", "pop_globale", | |
| "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é" | |
| ], | |
| "🏭 Activité économique": [ | |
| "nb_salaries_secteur_agricole", "nb_salaries_secteur_industrie", "nb_salaries_secteur_service", | |
| "nbre_entreprises", "nbre_entreprises_agricole", "nbre_entreprises_industrie", "nbre_entreprises_service" | |
| ], | |
| "🗑️ Déchets": [ | |
| "tonnage_dechet_produit", | |
| "Total_autres_dechets", | |
| "Déblais_gravats", | |
| "Déchets_verts", | |
| "Encombrants", | |
| "Matériaux_recyclables" | |
| ] | |
| } | |
| for category_name, variables in categories.items(): | |
| with st.expander(category_name, expanded=True): | |
| for var in variables: | |
| if var in default_dict: | |
| col_slider, col_input = st.columns([2, 1]) | |
| with col_slider: | |
| slider_value = st.slider( | |
| f"🔧 {var}", | |
| min_value=float(default_dict[var]) * 0, | |
| max_value=float(default_dict[var]) * 1.5, | |
| value=float(default_dict[var]), | |
| step=1.0, | |
| key=f"slider_{var}" | |
| ) | |
| with col_input: | |
| text_val = st.text_input(f"{var} (manuel)", value=str(slider_value), key=f"text_{var}") | |
| try: | |
| form_input[var] = float(text_val) | |
| except ValueError: | |
| form_input[var] = slider_value | |
| input_df = pd.DataFrame([form_input]) | |
| input_df_complete = row_default.to_frame().T.copy() | |
| for col in input_df.columns: | |
| if col in input_df_complete.columns: | |
| input_df_complete.at[input_df_complete.index[0], col] = input_df.at[0, col] | |
| with chart_col: | |
| st.markdown("<div style='margin-top: 30px;'></div>", unsafe_allow_html=True) | |
| btn_col = st.columns([3, 2, 3])[1] | |
| with btn_col: | |
| run_eval = st.button("🔍 Lancer l'évaluation") | |
| st.markdown("<div style='margin-top: 40px;'></div>", unsafe_allow_html=True) | |
| model_paths = { | |
| "Déblais et Gravats": "src/model_paths/model_ols_Déblais_gravats.pkl", | |
| "Déchets verts": "src/model_paths/model_ols_Déchets_verts.pkl", | |
| "Encombrants": "src/model_paths/model_ols_Encombrants.pkl", | |
| "Matériaux recyclables": "src/model_paths/model_ols_Matériaux_recyclables.pkl", | |
| "Total autres déchets": "src/model_paths/model_ols_Total_autres_dechets.pkl" | |
| } | |
| col_mapping = { | |
| "Déblais et Gravats": "Déblais_gravats", | |
| "Déchets verts": "Déchets_verts", | |
| "Encombrants": "Encombrants", | |
| "Matériaux recyclables": "Matériaux_recyclables", | |
| "Total autres déchets": "Total_autres_dechets" | |
| } | |
| valeurs_observees = [] | |
| valeurs_predites = [] | |
| labels = [] | |
| if run_eval: | |
| for typologie, path in model_paths.items(): | |
| try: | |
| with open(path, "rb") as f: | |
| model = pickle.load(f) | |
| # expected_cols = model.model.exog_names | |
| # if "const" in expected_cols and "const" not in input_df_complete.columns: | |
| # input_df_complete["const"] = 1.0 | |
| # prediction = max(0, model.predict(input_df_complete[expected_cols]).iloc[0]) | |
| prediction = max(0, model.predict(input_df_complete).iloc[0]) | |
| valeurs_predites.append(prediction) | |
| labels.append(typologie) | |
| filtered = observed_df[ | |
| (observed_df["Département"] == selected_dept) & (observed_df["année"] == 2019) | |
| ] | |
| excel_col = col_mapping.get(typologie) | |
| if not filtered.empty and excel_col in filtered.columns: | |
| valeurs_observees.append(filtered[excel_col].values[0]) | |
| else: | |
| valeurs_observees.append(0.0) | |
| except Exception as e: | |
| st.error(f"Erreur avec le modèle {typologie}") | |
| st.exception(e) | |
| if valeurs_observees and valeurs_predites: | |
| x = np.arange(len(labels)) | |
| width = 0.4 | |
| fig, ax = plt.subplots(figsize=(10, 6)) | |
| bars1 = ax.bar(x - width / 2, valeurs_observees, width, label='Observé (2019)', color='steelblue') | |
| bar_colors = [(1, 0, 0, 0.6) if pred > obs else (0, 0.6, 0, 0.6) | |
| for pred, obs in zip(valeurs_predites, valeurs_observees)] | |
| bars2 = ax.bar(x + width / 2, valeurs_predites, width, label='Prévision', color=bar_colors) | |
| for i in range(len(labels)): | |
| ax.text(x[i] - width / 2, valeurs_observees[i] + max(valeurs_observees) * 0.01, f"{valeurs_observees[i]:,.0f}", | |
| ha='center', va='bottom', fontsize=9) | |
| ax.text(x[i] + width / 2, valeurs_predites[i] + max(valeurs_predites) * 0.01, f"{valeurs_predites[i]:,.0f}", | |
| ha='center', va='bottom', fontsize=9) | |
| ax.set_ylabel("Tonnes") | |
| ax.set_title("Comparaison Observé vs Prédit") | |
| ax.set_xticks(x) | |
| ax.set_xticklabels(labels, rotation=45, ha='right') | |
| ax.legend() | |
| st.pyplot(fig) |