Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import plotly.express as px | |
| import re # Pour utiliser les expressions régulières | |
| # Configuration de la page | |
| st.set_page_config(page_title="Tableau de Bord Épidémie", layout="wide") | |
| # Fonction pour charger un fichier uploadé (CSV ou Excel) | |
| def load_uploaded_file(uploaded_file): | |
| if uploaded_file is not None: | |
| try: | |
| if uploaded_file.name.endswith(".csv"): | |
| df = pd.read_csv(uploaded_file, sep="\t") | |
| elif uploaded_file.name.endswith((".xlsx", ".xls")): | |
| df = pd.read_excel(uploaded_file) | |
| else: | |
| st.error("Type de fichier non supporté. Veuillez uploader un fichier CSV ou Excel.") | |
| return None | |
| return df | |
| except Exception as e: | |
| st.error(f"Erreur lors du chargement du fichier : {e}") | |
| return None | |
| return None | |
| # Interface pour uploader des fichiers dans la sidebar | |
| st.sidebar.header("📎 Importer vos Bases Excel") | |
| uploaded_file1 = st.sidebar.file_uploader("📂 Charger un premier fichier", type=["csv", "xlsx", "xls"]) | |
| uploaded_file2 = st.sidebar.file_uploader("📂 Charger un deuxième fichier", type=["csv", "xlsx", "xls"]) | |
| # Vérifier que les deux fichiers sont chargés | |
| if uploaded_file1 is None or uploaded_file2 is None: | |
| st.warning("Veuillez envoyer les deux bases pour afficher les données.") | |
| st.stop() | |
| # Charger les fichiers | |
| df_a = load_uploaded_file(uploaded_file1) | |
| df_b = load_uploaded_file(uploaded_file2) | |
| # Identifier quel fichier correspond à quelle structure | |
| # Celui contenant la colonne "periodname" correspond à df2 (la base avec "organisationunitname") | |
| if "periodname" in df_a.columns: | |
| df2, df1 = df_a.copy(), df_b.copy() | |
| else: | |
| df2, df1 = df_b.copy(), df_a.copy() | |
| # Pour df1, on s'assure que la colonne DistrictofResidence est en majuscules et nettoyée | |
| if "DistrictofResidence" in df1.columns: | |
| df1["DistrictofResidence"] = df1["DistrictofResidence"].str.upper().str.strip() | |
| # Harmonisation des colonnes dans df2 | |
| df2.rename(columns={ | |
| "organisationunitname": "DistrictofResidence", | |
| "periodname": "Semaine_Epi", | |
| "MAPE16_H_19.Rougeole": "MAPE" | |
| }, inplace=True) | |
| # Nettoyer la colonne DistrictofResidence dans df2 : | |
| # - Suppression du mot-clé "District" ou "district" en début de chaîne (avec insensibilité à la casse) | |
| # - Transformation en majuscules et suppression des espaces inutiles | |
| df2["DistrictofResidence"] = df2["DistrictofResidence"].str.replace(r'^district\s*', '', | |
| flags=re.IGNORECASE, regex=True).str.upper().str.strip() | |
| # Conversion de la colonne MAPE en numérique | |
| df2["MAPE"] = pd.to_numeric(df2["MAPE"], errors='coerce') | |
| # Normalisation de la colonne "Semaine_Epi" pour extraire le chiffre | |
| df2["Semaine_Epi"] = df2["Semaine_Epi"].astype(str).str.extract(r'(\d+)')[0].astype(int) | |
| # Fusion des données sur "DistrictofResidence" et "Semaine_Epi" | |
| df = pd.merge(df1, df2, on=["DistrictofResidence", "Semaine_Epi"], how="outer") | |
| # Titre de l'application | |
| st.markdown("<h1 style='text-align: center; color: #2C3E50;'>📊 Tableau de Bord de l'Épidémie</h1>", | |
| unsafe_allow_html=True) | |
| # Filtres dans la sidebar | |
| st.sidebar.header("Filtres") | |
| districts = st.sidebar.multiselect("🏠 Sélectionner un district", sorted(df["DistrictofResidence"].dropna().unique())) | |
| weeks = st.sidebar.multiselect("📅 Sélectionner une semaine", sorted(df["Semaine_Epi"].unique())) | |
| # Filtrage des données | |
| filtered_df = df.copy() | |
| if districts: | |
| filtered_df = filtered_df[filtered_df["DistrictofResidence"].isin(districts)] | |
| if weeks: | |
| filtered_df = filtered_df[filtered_df["Semaine_Epi"].isin(weeks)] | |
| # Statistiques clés | |
| st.subheader("📌 Statistiques Clés") | |
| st.markdown("<hr>", unsafe_allow_html=True) | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.metric("🧪 Total IgM+", filtered_df["Igm+"].sum()) | |
| st.metric("📄 Total Line List", filtered_df["Line list"].sum()) | |
| with col2: | |
| st.metric("❌ Total Rejetés", filtered_df["REJETE"].sum()) | |
| st.metric("🔵 Total MAPE", filtered_df["MAPE"].sum()) | |
| with col3: | |
| st.metric("🟠 Total Compatibles", filtered_df["Compatible"].sum()) | |
| # Assurer que les semaines sont de 1 à 10 | |
| all_weeks = pd.DataFrame({"Semaine_Epi": range(1, 11)}) | |
| time_series = filtered_df.groupby("Semaine_Epi")[ | |
| ["Igm+", "REJETE", "Compatible", "Line list", "MAPE"]].sum().reset_index() | |
| time_series = all_weeks.merge(time_series, on="Semaine_Epi", how="left").fillna(0) | |
| # Création du graphique avec affichage des valeurs sur chaque segment | |
| fig = px.bar( | |
| time_series, | |
| x="Semaine_Epi", | |
| y=["Igm+", "REJETE", "Compatible", "Line list", "MAPE"], | |
| title="📊 Évolution des différents cas", | |
| color_discrete_map={ | |
| "Igm+": "red", | |
| "REJETE": "green", | |
| "Compatible": "orange", | |
| "Line list": "yellow", | |
| "MAPE": "blue" | |
| }, | |
| barmode="stack", | |
| text_auto=True # Affiche les valeurs sur chaque bande | |
| ) | |
| fig.update_layout( | |
| xaxis=dict(tickmode='array', tickvals=list(range(1, 11)), ticktext=[str(i) for i in range(1, 11)]) | |
| ) | |
| # Affichage du graphique | |
| st.plotly_chart(fig, use_container_width=True) | |
| # Possibilité de télécharger le graphique sous forme d'image PNG | |
| # Pour cela, Plotly nécessite l'installation de kaleido : pip install -U kaleido | |
| img_bytes = fig.to_image(format="png") | |
| st.download_button( | |
| label="Télécharger le graphique", | |
| data=img_bytes, | |
| file_name="graphique.png", | |
| mime="image/png" | |
| ) | |
| # Affichage des données filtrées dans un tableau | |
| st.subheader("📋 Données Filtrées") | |
| st.markdown("<hr>", unsafe_allow_html=True) | |
| st.dataframe(filtered_df, use_container_width=True) | |