import pandas as pd import streamlit as st st.set_page_config(page_title="Seuils individualisés - Zebris", layout="wide") st.title("Générateur de seuils individualisés") st.caption("Poids + volume horaire d'entraînement → seuils personnalisés de référence") st.subheader("Entrées athlète") c1, c2 = st.columns(2) with c1: nom = st.text_input("Nom de l'athlète", value="Lucas Martin") poids_kg = st.number_input("Poids (kg)", min_value=30.0, max_value=150.0, value=72.0, step=0.1) with c2: sport = st.text_input("Sport", value="Course sur route") volume_horaire = st.number_input("Volume horaire / semaine", min_value=0.5, max_value=30.0, value=5.0, step=0.5) poids_n = poids_kg * 9.81 # Définition de la catégorie de charge if volume_horaire <= 3: charge = "faible" elif volume_horaire <= 6: charge = "modérée" else: charge = "élevée" st.info(f"Catégorie de charge retenue : **{charge}**") # Seuils selon la charge if charge == "faible": force_bw_low = 0.75 force_bw_high = 1.00 pression_low = 20 pression_high = 26 cadence_low = 160 cadence_high = 172 contact_low = 70 contact_high = 74 flight_low = 26 flight_high = 30 asym_low = 6 asym_high = 10 rotation_low = 6 rotation_high = 10 elif charge == "modérée": force_bw_low = 0.70 force_bw_high = 0.95 pression_low = 20 pression_high = 25 cadence_low = 165 cadence_high = 176 contact_low = 69 contact_high = 73 flight_low = 27 flight_high = 31 asym_low = 5 asym_high = 9 rotation_low = 5 rotation_high = 9 else: force_bw_low = 0.65 force_bw_high = 0.90 pression_low = 20 pression_high = 24 cadence_low = 168 cadence_high = 180 contact_low = 68 contact_high = 72 flight_low = 28 flight_high = 32 asym_low = 4 asym_high = 8 rotation_low = 4 rotation_high = 8 # Conversion en Newton force_n_low = force_bw_low * poids_n force_n_high = force_bw_high * poids_n st.subheader("Résumé athlète") r1, r2, r3 = st.columns(3) with r1: st.metric("Poids", f"{poids_kg:.1f} kg") with r2: st.metric("Poids en Newton", f"{poids_n:.1f} N") with r3: st.metric("Charge", charge) st.subheader("Seuils individualisés") impact_df = pd.DataFrame({ "Variable": [ "Force talon normalisée", "Force talon convertie", "Pression talon", ], "Zone basse / faible": [ f"< {force_bw_low:.2f} BW", f"< {force_n_low:.1f} N", f"< {pression_low} N/cm²", ], "Zone attendue": [ f"{force_bw_low:.2f} à {force_bw_high:.2f} BW", f"{force_n_low:.1f} à {force_n_high:.1f} N", f"{pression_low} à {pression_high} N/cm²", ], "Zone haute / élevée": [ f"> {force_bw_high:.2f} BW", f"> {force_n_high:.1f} N", f"> {pression_high} N/cm²", ], }) dynamique_df = pd.DataFrame({ "Variable": [ "Cadence", "Temps de contact", "Temps de vol", ], "Zone basse / faible": [ f"< {cadence_low} pas/min", f"< {contact_low} %", f"< {flight_low} %", ], "Zone attendue": [ f"{cadence_low} à {cadence_high} pas/min", f"{contact_low} à {contact_high} %", f"{flight_low} à {flight_high} %", ], "Zone haute / élevée": [ f"> {cadence_high} pas/min", f"> {contact_high} %", f"> {flight_high} %", ], }) symetrie_df = pd.DataFrame({ "Variable": [ "Asymétrie force talon", "Asymétrie force avant-pied", "Asymétrie COP", "Différence rotation G/D", ], "Zone faible": [ f"< {asym_low} %", f"< {asym_low} %", f"< {asym_low} %", f"< {rotation_low}°", ], "Zone modérée": [ f"{asym_low} à {asym_high} %", f"{asym_low} à {asym_high} %", f"{asym_low} à {asym_high} %", f"{rotation_low} à {rotation_high}°", ], "Zone marquée": [ f"> {asym_high} %", f"> {asym_high} %", f"> {asym_high} %", f"> {rotation_high}°", ], }) tab1, tab2, tab3 = st.tabs(["Impact", "Dynamique", "Symétrie"]) with tab1: st.dataframe(impact_df, hide_index=True, use_container_width=True) with tab2: st.dataframe(dynamique_df, hide_index=True, use_container_width=True) with tab3: st.dataframe(symetrie_df, hide_index=True, use_container_width=True) st.subheader("Lecture méthodologique") st.write( "Ces seuils sont individualisés à partir du poids et du volume horaire d’entraînement hebdomadaire. " "Ils constituent une base de référence personnelle de l’athlète avant toute analyse ou recommandation." )