EHRley's picture
Update app.py
84b8e7d verified
Raw
History Blame Contribute Delete
6.39 kB
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)