autonomie / app.py
l0d0v1c's picture
Upload 9 files
bab47c0 verified
#!/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"""
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
.node {{
stroke: #fff;
stroke-width: 2px;
cursor: pointer;
}}
.link {{
stroke: #999;
stroke-opacity: 0.6;
stroke-width: 2px;
}}
.node-label {{
font: 12px sans-serif;
pointer-events: none;
text-anchor: middle;
fill: #333;
}}
.tooltip {{
position: absolute;
padding: 8px;
background: rgba(0, 0, 0, 0.8);
color: white;
border-radius: 4px;
pointer-events: none;
font-size: 12px;
}}
</style>
</head>
<body>
<div id="network"></div>
<div class="tooltip" style="opacity: 0;"></div>
<script>
// Dimensions
const width = 800;
const height = 600;
// Données du réseau
const nodes = {nodes};
const links = {links};
// Créer le SVG
const svg = d3.select("#network")
.append("svg")
.attr("width", width)
.attr("height", height);
// Tooltip
const tooltip = d3.select(".tooltip");
// Simulation de force
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(100))
.force("charge", d3.forceManyBody().strength(-300))
.force("center", d3.forceCenter(width / 2, height / 2));
// Créer les liens
const link = svg.append("g")
.selectAll("line")
.data(links)
.enter().append("line")
.attr("class", "link");
// Créer les nœuds
const node = svg.append("g")
.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 15)
.attr("fill", d => d.color)
.on("mouseover", function(event, d) {{
tooltip.transition().duration(200).style("opacity", .9);
tooltip.html(d.title)
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 28) + "px");
}})
.on("mouseout", function() {{
tooltip.transition().duration(500).style("opacity", 0);
}})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// Étiquettes des nœuds
const labels = svg.append("g")
.selectAll("text")
.data(nodes)
.enter().append("text")
.attr("class", "node-label")
.text(d => d.id.replace('_', ' '));
// Mise à jour de la simulation
simulation.on("tick", () => {{
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
labels
.attr("x", d => d.x)
.attr("y", d => d.y + 25);
}});
// Fonctions de drag
function dragstarted(event, d) {{
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}}
function dragged(event, d) {{
d.fx = event.x;
d.fy = event.y;
}}
function dragended(event, d) {{
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}}
</script>
</body>
</html>
"""
# 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()