Presentation / app.py
LewisBabong's picture
Update app.py
181c458 verified
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)