Spaces:
Paused
Paused
| import streamlit as st | |
| import pandas as pd | |
| import plotly.express as px | |
| # ------------------------------------------------------------------ | |
| # Configuration de la page | |
| # ------------------------------------------------------------------ | |
| st.set_page_config( | |
| page_title="Mon Tracker COVID perso", | |
| page_icon="📊", | |
| layout="wide", | |
| ) | |
| # Image locale en haut du dashboard | |
| #st.image("Covid.png", use_column_width=True) | |
| # Titre et intro | |
| st.title("Covid Tracker 🦠") | |
| st.markdown( | |
| "Dashboard de suivi des vagues de COVID-19 à travers le monde, " | |
| "basé sur les données de l'European Centre for Disease Prevention and Control (ECDC)." | |
| ) | |
| # ------------------------------------------------------------------ | |
| # Chargement des données (mis en cache pour la rapidité) | |
| # ------------------------------------------------------------------ | |
| def load_data(): | |
| df = pd.read_csv("covid_data.csv") | |
| df["dateRep"] = pd.to_datetime(df["dateRep"], format="%d/%m/%Y") | |
| return df | |
| df = load_data() | |
| # ------------------------------------------------------------------ | |
| # Barre latérale : tous les filtres ensemble | |
| # ------------------------------------------------------------------ | |
| st.sidebar.header("Filtres") | |
| # Filtre par continent | |
| continents = sorted(df["continentExp"].unique()) | |
| selected_continent = st.sidebar.selectbox("Continent", continents) | |
| # Filtre par pays | |
| countries = sorted(df[df["continentExp"] == selected_continent]["countriesAndTerritories"].unique()) | |
| selected_countries = st.sidebar.multiselect( | |
| "Pays à afficher", | |
| options=countries, | |
| default=countries[:4] if len(countries) >= 4 else countries, | |
| ) | |
| # Filtre par plage de dates | |
| min_date = df["dateRep"].min().date() | |
| max_date = df["dateRep"].max().date() | |
| date_range = st.sidebar.date_input( | |
| "Plage de dates", | |
| value=(min_date, max_date), | |
| min_value=min_date, | |
| max_value=max_date, | |
| ) | |
| # Slider pour le top N pays | |
| top_n = st.sidebar.slider("Afficher le top N pays", min_value=3, max_value=20, value=10) | |
| # Échelle logarithmique | |
| log_scale = st.sidebar.checkbox("Échelle logarithmique") | |
| # ------------------------------------------------------------------ | |
| # Filtrage du dataframe selon les choix utilisateur | |
| # ------------------------------------------------------------------ | |
| filtered = df[df["continentExp"] == selected_continent] | |
| filtered = filtered[filtered["countriesAndTerritories"].isin(selected_countries)] | |
| if len(date_range) == 2: | |
| start, end = date_range | |
| filtered = filtered[ | |
| (filtered["dateRep"] >= pd.to_datetime(start)) | |
| & (filtered["dateRep"] <= pd.to_datetime(end)) | |
| ] | |
| # ------------------------------------------------------------------ | |
| # Onglets pour organiser le dashboard | |
| # ------------------------------------------------------------------ | |
| tab1, tab2, tab3 = st.tabs(["Vue d'ensemble", "Par pays", "Comparaisons"]) | |
| # ----- ONGLET 1 : Vue d'ensemble ----- | |
| with tab1: | |
| st.subheader("Indicateurs globaux") | |
| col1, col2, col3 = st.columns(3) | |
| total_cases = int(filtered["cases"].sum()) | |
| total_deaths = int(filtered["deaths"].sum()) | |
| mortality_rate = (total_deaths / total_cases * 100) if total_cases > 0 else 0 | |
| col1.metric("Cas totaux", f"{total_cases:,}".replace(",", " ")) | |
| col2.metric("Décès totaux", f"{total_deaths:,}".replace(",", " ")) | |
| col3.metric("Taux de mortalité", f"{mortality_rate:.2f} %") | |
| # Courbe des cas dans le temps | |
| st.subheader("Évolution des cas dans le temps") | |
| fig_cases = px.line( | |
| filtered, | |
| x="dateRep", | |
| y="cases", | |
| color="countriesAndTerritories", | |
| labels={"dateRep": "Date", "cases": "Cas quotidiens", | |
| "countriesAndTerritories": "Pays"}, | |
| log_y=log_scale, | |
| ) | |
| st.plotly_chart(fig_cases, use_container_width=True) | |
| # Courbe des décès dans le temps | |
| st.subheader("Évolution des décès dans le temps") | |
| fig_deaths = px.line( | |
| filtered, | |
| x="dateRep", | |
| y="deaths", | |
| color="countriesAndTerritories", | |
| labels={"dateRep": "Date", "deaths": "Décès quotidiens", | |
| "countriesAndTerritories": "Pays"}, | |
| log_y=log_scale, | |
| ) | |
| st.plotly_chart(fig_deaths, use_container_width=True) | |
| # ----- ONGLET 2 : Par pays ----- | |
| with tab2: | |
| st.subheader("Récapitulatif par pays") | |
| summary = ( | |
| filtered.groupby("countriesAndTerritories") | |
| .agg( | |
| cas_totaux=("cases", "sum"), | |
| deces_totaux=("deaths", "sum"), | |
| cas_moyens_par_jour=("cases", "mean"), | |
| ) | |
| .round(0) | |
| .sort_values("cas_totaux", ascending=False) | |
| ) | |
| st.dataframe(summary, use_container_width=True) | |
| # Bouton pour télécharger le résumé en CSV | |
| csv = summary.to_csv().encode("utf-8") | |
| st.download_button( | |
| label="📥 Télécharger le résumé en CSV", | |
| data=csv, | |
| file_name="covid_summary.csv", | |
| mime="text/csv", | |
| ) | |
| # Données brutes (déplié sur demande) | |
| with st.expander("Voir les données brutes filtrées"): | |
| st.dataframe(filtered) | |
| # ----- ONGLET 3 : Comparaisons ----- | |
| with tab3: | |
| # Carte mondiale colorée par cas totaux | |
| st.subheader("Carte mondiale des cas") | |
| map_data = ( | |
| filtered.groupby(["countryterritoryCode", "countriesAndTerritories"]) | |
| .agg(total_cases=("cases", "sum")) | |
| .reset_index() | |
| ) | |
| fig_map = px.choropleth( | |
| map_data, | |
| locations="countryterritoryCode", | |
| color="total_cases", | |
| hover_name="countriesAndTerritories", | |
| color_continuous_scale="Reds", | |
| ) | |
| st.plotly_chart(fig_map, use_container_width=True) | |
| # Cas cumulés par pays en aires empilées | |
| st.subheader("Cas cumulés par pays") | |
| fig_area = px.area( | |
| filtered, | |
| x="dateRep", | |
| y="cases", | |
| color="countriesAndTerritories", | |
| labels={"dateRep": "Date", "cases": "Cas"}, | |
| ) | |
| st.plotly_chart(fig_area, use_container_width=True) | |
| # Cas par jour de la semaine | |
| st.subheader("Cas par jour de la semaine") | |
| filtered_copy = filtered.copy() | |
| filtered_copy["weekday"] = filtered_copy["dateRep"].dt.day_name() | |
| weekday_avg = filtered_copy.groupby("weekday")["cases"].mean().reindex( | |
| ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] | |
| ) | |
| st.bar_chart(weekday_avg) | |
| # Moyenne mobile 7 jours | |
| st.subheader("Tendance lissée (moyenne mobile 7 jours)") | |
| filtered_sorted = filtered.sort_values(["countriesAndTerritories", "dateRep"]) | |
| filtered_sorted["cases_7day_avg"] = ( | |
| filtered_sorted.groupby("countriesAndTerritories")["cases"] | |
| .transform(lambda x: x.rolling(7, min_periods=1).mean()) | |
| ) | |
| fig_smooth = px.line( | |
| filtered_sorted, | |
| x="dateRep", | |
| y="cases_7day_avg", | |
| color="countriesAndTerritories", | |
| ) | |
| st.plotly_chart(fig_smooth, use_container_width=True) | |
| # Taux d'incidence pour 100 000 habitants | |
| st.subheader("Taux d'incidence (cas pour 100 000 habitants)") | |
| filtered_copy["incidence_per_100k"] = ( | |
| filtered_copy["cases"] / filtered_copy["popData2020"] * 100_000 | |
| ) | |
| fig_incidence = px.line( | |
| filtered_copy, | |
| x="dateRep", | |
| y="incidence_per_100k", | |
| color="countriesAndTerritories", | |
| ) | |
| st.plotly_chart(fig_incidence, use_container_width=True) |