Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pandas as pd | |
| import numpy as np | |
| from sklearn.ensemble import RandomForestRegressor | |
| from sklearn.model_selection import train_test_split | |
| import matplotlib.pyplot as plt | |
| import matplotlib.gridspec as gridspec | |
| import os | |
| import traceback | |
| estado = { | |
| "modelo_tiempo": None, | |
| "modelo_distancia": None, | |
| "df": None, | |
| "X_test": None, "yT_test": None, "yD_test": None, | |
| "listo": False, | |
| "mensaje": " Cargando modelo... (puede tardar unos segundos)" | |
| } | |
| def cargar_y_entrenar(): | |
| file_path = "RegistroLanzamientos.xlsx" | |
| if not os.path.exists(file_path): | |
| estado["mensaje"] = " No se encontró 'RegistroLanzamientos.xlsx' en la raíz. Súbelo en la pestaña Files." | |
| return | |
| try: | |
| df = pd.read_excel(file_path) | |
| df.columns = ['Lanzamiento', 'Radio_cm', 'Peso_g', 'Pos_X', 'Pos_Y', 'Tiempo_s'] | |
| df['Distancia_cm'] = np.sqrt(df['Pos_X']**2 + df['Pos_Y']**2) | |
| X = df[['Radio_cm', 'Peso_g']] | |
| y_tiempo, y_distancia = df['Tiempo_s'], df['Distancia_cm'] | |
| idx_train, idx_test = train_test_split(df.index, test_size=0.2, random_state=42) | |
| X_train, X_test = X.loc[idx_train], X.loc[idx_test] | |
| yT_train, yT_test = y_tiempo.loc[idx_train], y_tiempo.loc[idx_test] | |
| yD_train, yD_test = y_distancia.loc[idx_train], y_distancia.loc[idx_test] | |
| modelo_tiempo = RandomForestRegressor(n_estimators=100, random_state=42) | |
| modelo_distancia = RandomForestRegressor(n_estimators=100, random_state=42) | |
| modelo_tiempo.fit(X_train, yT_train) | |
| modelo_distancia.fit(X_train, yD_train) | |
| estado.update({ | |
| "modelo_tiempo": modelo_tiempo, "modelo_distancia": modelo_distancia, | |
| "df": df, "X_test": X_test, "yT_test": yT_test, "yD_test": yD_test, | |
| "listo": True, "mensaje": " Modelo cargado y listo." | |
| }) | |
| except Exception as e: | |
| estado["mensaje"] = f" Error al entrenar:\n{traceback.format_exc()}" | |
| cargar_y_entrenar() | |
| def predecir(radio, peso): | |
| if not estado["listo"]: | |
| return estado["mensaje"], "", "" | |
| if radio <= 0 or peso <= 0: | |
| return " Valores deben ser > 0.", "", "" | |
| input_data = pd.DataFrame({'Radio_cm': [radio], 'Peso_g': [peso]}) | |
| pred_t = estado["modelo_tiempo"].predict(input_data)[0] | |
| pred_d = estado["modelo_distancia"].predict(input_data)[0] | |
| return " Predicción exitosa", f"{pred_t:.2f}", f"{pred_d:.1f}" | |
| def generar_grafico(): | |
| if not estado["listo"]: | |
| return None, estado["mensaje"] | |
| mT, mD = estado["modelo_tiempo"], estado["modelo_distancia"] | |
| X_test, yT_test, yD_test = estado["X_test"], estado["yT_test"], estado["yD_test"] | |
| df = estado["df"] | |
| pred_t = mT.predict(X_test) | |
| pred_d = mD.predict(X_test) | |
| r2_T = sum((yT_test - pred_t)**2) / sum((yT_test - yT_test.mean())**2) | |
| r2_D = sum((yD_test - pred_d)**2) / sum((yD_test - yD_test.mean())**2) | |
| fig = plt.figure(figsize=(16, 12)) | |
| gs = gridspec.GridSpec(2, 2, figure=fig, hspace=0.4, wspace=0.3) | |
| ax0 = fig.add_subplot(gs[0, 0]) | |
| ax0.scatter(yT_test, pred_t, color='#e74c3c', alpha=0.8) | |
| lim = [min(yT_test.min(), pred_t.min()), max(yT_test.max(), pred_t.max())] | |
| ax0.plot(lim, lim, '--', color='gray') | |
| ax0.set_xlabel('Tiempo real (s)'); ax0.set_ylabel('Tiempo predicho (s)') | |
| ax0.set_title('Tiempo de caída'); ax0.text(0.05, 0.95, f'R²={r2_T:.3f}', transform=ax0.transAxes, va='top') | |
| ax1 = fig.add_subplot(gs[0, 1]) | |
| ax1.scatter(yD_test, pred_d, color='#2980b9', alpha=0.8) | |
| lim2 = [min(yD_test.min(), pred_d.min()), max(yD_test.max(), pred_d.max())] | |
| ax1.plot(lim2, lim2, '--', color='gray') | |
| ax1.set_xlabel('Distancia real (cm)'); ax1.set_ylabel('Distancia predicha (cm)') | |
| ax1.set_title('Distancia al origen'); ax1.text(0.05, 0.95, f'R²={r2_D:.3f}', transform=ax1.transAxes, va='top') | |
| ax2 = fig.add_subplot(gs[1, 0]) | |
| imp = [mT.feature_importances_, mD.feature_importances_] | |
| x = np.arange(2); w = 0.35 | |
| ax2.bar(x-w/2, imp[0], w, label='Tiempo', color='#e74c3c') | |
| ax2.bar(x+w/2, imp[1], w, label='Distancia', color='#2980b9') | |
| ax2.set_xticks(x); ax2.set_xticklabels(['Radio (cm)', 'Peso (g)']) | |
| ax2.set_ylabel('Importancia'); ax2.legend(); ax2.set_title('Variables clave') | |
| ax3 = fig.add_subplot(gs[1, 1]) | |
| r_range = np.linspace(df['Radio_cm'].min(), df['Radio_cm'].max(), 50) | |
| p_fijo = df['Peso_g'].median() | |
| ax3.plot(r_range, mT.predict(pd.DataFrame({'Radio_cm': r_range, 'Peso_g': p_fijo})), label='Tiempo', color='#e74c3c') | |
| ax3b = ax3.twinx() | |
| ax3b.plot(r_range, mD.predict(pd.DataFrame({'Radio_cm': r_range, 'Peso_g': p_fijo})), label='Distancia', color='#2980b9', ls='--') | |
| ax3.set_xlabel('Radio (cm)'); ax3.set_ylabel('Tiempo (s)', color='#e74c3c') | |
| ax3b.set_ylabel('Distancia (cm)', color='#2980b9') | |
| ax3.set_title(f'Efecto del radio (peso fijo={p_fijo:.0f}g)') | |
| ax3.legend(loc='upper left'); ax3b.legend(loc='upper right') | |
| plt.tight_layout() | |
| return fig, f" Gráfico generado. R² Tiempo: {r2_T:.3f} | R² Distancia: {r2_D:.3f}" | |
| with gr.Blocks(title=" Simulador Paracaídas") as app: | |
| gr.Markdown(" Predicción de Caída de Paracaídas") | |
| gr.Markdown(f"**Estado:** `{estado['mensaje']}`") | |
| with gr.Row(): | |
| radio_inp = gr.Number(label="Radio del paracaídas (cm)", value=24.0, minimum=0.1) | |
| peso_inp = gr.Number(label="Peso del muñeco (g)", value=55.0, minimum=0.1) | |
| btn_pred = gr.Button(" Calcular", variant="primary") | |
| with gr.Row(): | |
| msg_out = gr.Textbox(label="Estado", interactive=False) | |
| t_out = gr.Number(label="Tiempo (s)") | |
| d_out = gr.Number(label="Distancia (cm)") | |
| btn_pred.click(predecir, inputs=[radio_inp, peso_inp], outputs=[msg_out, t_out, d_out]) | |
| gr.Markdown("---") | |
| btn_plot = gr.Button(" Ver Gráfico Completo", variant="secondary") | |
| plot_out = gr.Plot(label="Análisis del Modelo") | |
| plot_msg = gr.Textbox(label="Métricas", interactive=False) | |
| gr.Markdown("*Basado en: Ruiz Cruz, EH (2026). Pensamiento cientifico autentico mediante STEAM, u-learning y modelos predictivos en educacion tecnica.*") | |
| btn_plot.click(generar_grafico, inputs=[], outputs=[plot_out, plot_msg]) | |
| app.launch(server_name="0.0.0.0", server_port=7860, share=False) |