#!/usr/bin/env python3 """ Interface web Streamlit pour le réseau bayésien d'autonomie Application déployée sur Hugging Face Spaces """ import streamlit as st import pandas as pd import plotly.graph_objects as go import plotly.express as px import json from bayesian_network_interface import AutonomyBayesianNetwork # Configuration de la page st.set_page_config( page_title="Réseau Bayésien - Autonomie", page_icon="🧠", layout="wide", initial_sidebar_state="expanded" ) def main(): """Interface principale Streamlit""" st.title("🧠 Réseau Bayésien pour l'Évaluation d'Autonomie") st.markdown("---") # Initialiser le réseau if 'network' not in st.session_state: with st.spinner("Chargement du réseau bayésien..."): try: st.session_state.network = AutonomyBayesianNetwork() # Debug: vérifier le chargement if hasattr(st.session_state.network, 'pgmpy_model') and st.session_state.network.pgmpy_model: st.session_state.loading_debug = f"✅ Réseau chargé: {len(list(st.session_state.network.pgmpy_model.nodes()))} nœuds" else: st.session_state.loading_debug = "❌ Erreur: pgmpy_model non chargé" except Exception as e: st.session_state.loading_debug = f"❌ Erreur de chargement: {str(e)}" st.session_state.network = None network = st.session_state.network # Sidebar pour la navigation st.sidebar.title("Navigation") page = st.sidebar.selectbox( "Choisir une page", ["🏠 Accueil", "📊 Structure du Réseau", "🌐 Visualisation D3.js", "🔍 Inférence", "💊 Analyse d'Intervention", "📈 Facteurs Influents", "📋 Recommandations"] ) if page == "🏠 Accueil": show_home_page(network) elif page == "📊 Structure du Réseau": show_structure_page(network) elif page == "🌐 Visualisation D3.js": show_d3_visualization_page(network) elif page == "🔍 Inférence": show_inference_page(network) elif page == "💊 Analyse d'Intervention": show_intervention_page(network) elif page == "📈 Facteurs Influents": show_factors_page(network) elif page == "📋 Recommandations": show_recommendations_page(network) def show_home_page(network): """Page d'accueil avec démonstration""" st.header("🏠 Bienvenue dans l'Interface d'Évaluation d'Autonomie") st.markdown(""" Cette application utilise un **réseau bayésien complet** avec **12 variables** pour évaluer l'autonomie des personnes âgées et générer des recommandations personnalisées. ### 🎯 **Fonctionnalités principales** - **Inférence probabiliste** : Calcul des probabilités d'autonomie - **Analyse d'interventions** : Impact des changements de comportement - **Recommandations personnalisées** : Conseils basés sur le profil individuel - **Facteurs influents** : Identification des variables les plus importantes - **Visualisation interactive** : Graphique D3.js du réseau """) st.subheader("🔍 Démonstration avec deux scénarios") col1, col2 = st.columns(2) with col1: st.markdown("**👤 Scénario 1: Personne Active**") evidence1 = { 'Age': 'age_60_69', 'Physical_Activity': 'high', 'BMI_Category': 'underweight_normal' } for key, value in evidence1.items(): st.write(f"• {key}: {value}") result1 = network.perform_inference_pgmpy(evidence1, ['Global_Autonomy']) if not result1.empty: autonomous_prob = result1[result1['State'] == 'autonomous']['Probability'].iloc[0] st.success(f"🎉 **Probabilité d'autonomie: {autonomous_prob:.1%}**") with col2: st.markdown("**👴 Scénario 2: Personne Sédentaire**") evidence2 = { 'Age': 'age_80_89', 'Physical_Activity': 'sedentary', 'BMI_Category': 'obese_severe' } for key, value in evidence2.items(): st.write(f"• {key}: {value}") result2 = network.perform_inference_pgmpy(evidence2, ['Global_Autonomy']) if not result2.empty: autonomous_prob = result2[result2['State'] == 'autonomous']['Probability'].iloc[0] st.warning(f"⚠️ **Probabilité d'autonomie: {autonomous_prob:.1%}**") # Graphique comparatif if not result1.empty and not result2.empty: st.markdown("---") st.subheader("📊 Comparaison des Scénarios") fig = go.Figure() # Données du scénario 1 states1 = result1['State'].tolist() probs1 = result1['Probability'].tolist() # Données du scénario 2 states2 = result2['State'].tolist() probs2 = result2['Probability'].tolist() fig.add_trace(go.Bar( name='Personne Active (60-69 ans)', x=states1, y=probs1, marker_color='lightgreen' )) fig.add_trace(go.Bar( name='Personne Sédentaire (80-89 ans)', x=states2, y=probs2, marker_color='lightcoral' )) fig.update_layout( title="Comparaison des Probabilités d'Autonomie", xaxis_title="État d'Autonomie", yaxis_title="Probabilité", barmode='group', height=500 ) st.plotly_chart(fig, use_container_width=True) st.markdown("---") st.info("💡 **Conseil**: Explorez les autres pages pour des analyses plus détaillées!") def show_structure_page(network): """Page structure du réseau""" st.header("📊 Structure du Réseau Bayésien") # Debug information st.write("🔍 **Debug Info:**") # Afficher les infos de chargement if hasattr(st.session_state, 'loading_debug'): st.write(f"- Chargement initial: {st.session_state.loading_debug}") if network is None: st.error("❌ Réseau non initialisé!") return # Afficher les messages de debug détaillés du chargement if hasattr(network, 'debug_messages'): st.write("**Messages de chargement détaillés:**") for msg in network.debug_messages: st.code(msg) st.write(f"- pgmpy_model exists: {network.pgmpy_model is not None}") if network.pgmpy_model: st.write(f"- pgmpy nodes: {len(list(network.pgmpy_model.nodes()))}") st.write(f"- pgmpy edges: {len(list(network.pgmpy_model.edges()))}") st.write(f"- pyagrum_model exists: {hasattr(network, 'pyagrum_model') and network.pyagrum_model is not None}") st.write(f"- actionable_vars: {len(getattr(network, 'actionable_vars', []))}") try: structure = network.get_network_structure() st.write(f"- Structure nodes: {len(structure['nodes'])}") st.write(f"- Structure edges: {len(structure['edges'])}") except Exception as e: st.error(f"Erreur lors de l'obtention de la structure: {e}") structure = {'nodes': [], 'edges': []} col1, col2, col3 = st.columns(3) with col1: st.metric("Nombre de nœuds", len(structure.get('nodes', []))) with col2: st.metric("Nombre d'arcs", len(structure.get('edges', []))) with col3: st.metric("Variables actionnables", len(network.actionable_vars)) # Afficher les variables par catégorie st.subheader("Variables par catégorie") categories = {} for node in structure['nodes']: cat = node['category'] if cat not in categories: categories[cat] = [] categories[cat].append(node['name']) for cat in ['non_actionable', 'actionable', 'intermediate', 'outcome']: if cat in categories: with st.expander(f"{cat.replace('_', ' ').title()} ({len(categories[cat])} variables)"): for var in categories[cat]: st.write(f"• {var}") def show_inference_page(network): """Page d'inférence""" st.header("🔍 Inférence Bayésienne") col1, col2 = st.columns(2) with col1: st.subheader("Variables Non-Actionnables") evidence = {} age = st.selectbox("Âge", ["", "age_60_69", "age_70_79", "age_80_89", "age_90_plus"]) if age: evidence['Age'] = age sex = st.selectbox("Sexe", ["", "male", "female"]) if sex: evidence['Sex'] = sex education = st.selectbox("Niveau d'Éducation", ["", "primary_or_below", "secondary", "higher_education"]) if education: evidence['Education_Level'] = education st.subheader("Variables Actionnables") activity = st.selectbox("Activité Physique", ["", "sedentary", "low", "moderate", "high"]) if activity: evidence['Physical_Activity'] = activity bmi = st.selectbox("Catégorie IMC", ["", "underweight_normal", "overweight", "obese_mild", "obese_severe"]) if bmi: evidence['BMI_Category'] = bmi smoking = st.selectbox("Statut Tabagique", ["", "never", "former", "current"]) if smoking: evidence['Smoking_Status'] = smoking social_eng = st.selectbox("Engagement Social", ["", "low", "moderate", "high"]) if social_eng: evidence['Social_Engagement'] = social_eng social_sup = st.selectbox("Support Social", ["", "poor", "moderate", "good"]) if social_sup: evidence['Social_Support'] = social_sup with col2: st.subheader("Variables à inférer") query_vars = st.multiselect( "Sélectionner les variables", network.outcome_vars + network.intermediate_vars, default=['Global_Autonomy'] ) if st.button("🔍 Effectuer l'inférence"): if query_vars: with st.spinner("Calcul en cours..."): result = network.perform_inference_pgmpy(evidence, query_vars) if not result.empty: st.subheader("📊 Résultats") for var in query_vars: var_data = result[result['Variable'] == var] if not var_data.empty: fig = px.bar(var_data, x='State', y='Probability', title=f"Distribution de probabilité pour {var}", color='Probability', color_continuous_scale='viridis') st.plotly_chart(fig, use_container_width=True) # Tableau des résultats st.dataframe(var_data[['State', 'Probability']].round(4)) else: st.error("❌ Aucun résultat disponible") else: st.warning("⚠️ Veuillez sélectionner au moins une variable à inférer") def show_intervention_page(network): """Page analyse d'intervention""" st.header("💊 Analyse d'Intervention") col1, col2 = st.columns(2) with col1: st.markdown("**Intervention à tester**") intervention = {} new_activity = st.selectbox("Nouvelle Activité Physique", ["Non modifié", "sedentary", "low", "moderate", "high"]) if new_activity != "Non modifié": intervention['Physical_Activity'] = new_activity new_bmi = st.selectbox("Nouvelle Catégorie IMC", ["Non modifié", "underweight_normal", "overweight", "obese_mild", "obese_severe"]) if new_bmi != "Non modifié": intervention['BMI_Category'] = new_bmi with col2: st.markdown("**Profil de base (optionnel)**") baseline_evidence = {} base_age = st.selectbox("Âge de référence", ["", "age_60_69", "age_70_79", "age_80_89", "age_90_plus"]) if base_age: baseline_evidence['Age'] = base_age if st.button("🧪 Analyser l'intervention"): if intervention: with st.spinner("Analyse en cours..."): baseline_result = network.perform_inference_pgmpy(baseline_evidence, ['Global_Autonomy']) # Combiner baseline et intervention intervention_evidence = baseline_evidence.copy() intervention_evidence.update(intervention) intervention_result = network.perform_inference_pgmpy(intervention_evidence, ['Global_Autonomy']) if not baseline_result.empty and not intervention_result.empty: st.subheader("📊 Comparaison Avant/Après") # Graphique comparatif fig = go.Figure() states = baseline_result['State'].tolist() baseline_probs = baseline_result['Probability'].tolist() intervention_probs = intervention_result['Probability'].tolist() fig.add_trace(go.Bar( name='Avant intervention', x=states, y=baseline_probs, marker_color='lightblue' )) fig.add_trace(go.Bar( name='Après intervention', x=states, y=intervention_probs, marker_color='darkgreen' )) fig.update_layout( title="Impact de l'intervention sur l'autonomie", xaxis_title="État d'Autonomie", yaxis_title="Probabilité", barmode='group', height=500 ) st.plotly_chart(fig, use_container_width=True) # Calcul de l'amélioration baseline_autonomous = baseline_result[baseline_result['State'] == 'autonomous']['Probability'].iloc[0] intervention_autonomous = intervention_result[intervention_result['State'] == 'autonomous']['Probability'].iloc[0] improvement = intervention_autonomous - baseline_autonomous if improvement > 0: st.success(f"✅ **Amélioration**: +{improvement:.1%} de probabilité d'autonomie") elif improvement < 0: st.warning(f"⚠️ **Détérioration**: {improvement:.1%} de probabilité d'autonomie") else: st.info("➡️ **Aucun changement** significatif") else: st.error("❌ Impossible d'effectuer l'analyse") else: st.warning("⚠️ Veuillez définir au moins une intervention") def show_factors_page(network): """Page facteurs influents""" st.header("📈 Facteurs les Plus Influents") if st.button("🔍 Analyser les facteurs influents"): with st.spinner("Calcul en cours..."): factors = network.get_most_influential_factors() if factors: st.subheader("🎯 Classement des Variables") # Graphique en barres variables = [factor[0] for factor in factors] influences = [factor[1] for factor in factors] fig = px.bar( x=variables, y=influences, title="Impact des Variables sur l'Autonomie", labels={'x': 'Variables', 'y': 'Influence (différence de probabilité)'} ) st.plotly_chart(fig, use_container_width=True) # Tableau détaillé df_factors = pd.DataFrame(factors, columns=['Variable', 'Influence']) df_factors['Influence %'] = (df_factors['Influence'] * 100).round(1) st.dataframe(df_factors) else: st.info("ℹ️ Aucun facteur influent identifié avec les données actuelles") def show_recommendations_page(network): """Page recommandations""" st.header("📋 Recommandations Personnalisées") st.subheader("Profil du Patient") profile = {} col1, col2 = st.columns(2) with col1: age = st.selectbox("Âge", ["age_60_69", "age_70_79", "age_80_89", "age_90_plus"]) profile['Age'] = age activity = st.selectbox("Activité Physique actuelle", ["sedentary", "low", "moderate", "high"]) profile['Physical_Activity'] = activity with col2: sex = st.selectbox("Sexe", ["male", "female"]) profile['Sex'] = sex education = st.selectbox("Niveau d'éducation", ["primary_or_below", "secondary", "higher_education"]) profile['Education_Level'] = education bmi = st.selectbox("Catégorie IMC actuelle", ["underweight_normal", "overweight", "obese_mild", "obese_severe"]) profile['BMI_Category'] = bmi if st.button("🎯 Générer les recommandations"): with st.spinner("Analyse du profil..."): # État actuel current_state = network.perform_inference_pgmpy(profile, ['Global_Autonomy']) if not current_state.empty: current_autonomous = current_state[current_state['State'] == 'autonomous']['Probability'].iloc[0] st.subheader("📊 État Actuel") st.metric("Probabilité d'autonomie", f"{current_autonomous:.1%}") # Générer les recommandations recommendations = network.generate_recommendations(profile) if recommendations: st.subheader("🎯 Recommandations Prioritaires") for i, rec in enumerate(recommendations): with st.expander(f"💡 Recommandation #{i+1} - Priorité {rec['priority']}"): st.write(f"**{rec['recommendation']}**") st.info(f"📈 **Amélioration attendue**: +{rec['expected_improvement']:.1f}%") # Barre de progression pour l'amélioration col_metric, col_bar = st.columns([1, 3]) with col_metric: st.metric("Impact", f"+{rec['expected_improvement']:.1f}%") with col_bar: progress = min(rec['expected_improvement'] / 20.0, 1.0) # Normaliser sur 20% st.progress(progress) st.markdown("---") else: st.info("ℹ️ Aucune recommandation spécifique disponible pour ce profil") else: st.error("❌ Impossible d'analyser le profil") def show_d3_visualization_page(network): """Page avec visualisation D3.js du réseau bayésien""" st.header("🌐 Visualisation Interactive D3.js") st.markdown(""" Cette page présente une visualisation interactive du réseau bayésien utilisant D3.js. Les nœuds sont colorés selon leur catégorie et les arcs montrent les dépendances causales. """) # Récupérer les données du réseau structure = network.get_network_structure() # Créer les données pour D3.js nodes = [] links = [] # Catégories de couleurs color_map = { 'non_actionable': '#FF6B6B', # Rouge 'actionable': '#4ECDC4', # Turquoise 'intermediate': '#45B7D1', # Bleu 'outcome': '#96CEB4' # Vert } # Préparer les nœuds for i, node in enumerate(structure['nodes']): nodes.append({ "id": node['name'], "group": node['category'], "color": color_map.get(node['category'], '#DDDDDD'), "title": f"{node['name']} ({node['category']})" }) # Préparer les liens for source, target in structure['edges']: links.append({ "source": source, "target": target }) # Code D3.js d3_code = f"""
""" # Afficher la visualisation D3.js st.components.v1.html(d3_code, height=650) # Légende st.subheader("📋 Légende") col1, col2, col3, col4 = st.columns(4) with col1: st.markdown("🔴 **Non-actionnables**") st.write("Variables démographiques") with col2: st.markdown("🔵 **Actionnables**") st.write("Variables modifiables") with col3: st.markdown("🟦 **Intermédiaires**") st.write("Variables de transition") with col4: st.markdown("🟢 **Résultat**") st.write("Variable d'autonomie") st.info("💡 **Interaction**: Cliquez et faites glisser les nœuds pour explorer la structure du réseau!") if __name__ == "__main__": main()