import gradio as gr import numpy as np import joblib import sys import os import pandas as pd from models.train_linear_model import MultipleLinearRegressionGradientDescent from models.train_logistic_model import LogisticRegressionGradientDescent country_cols = ['Country_Afghanistan','Country_Albania', 'Country_Algeria', 'Country_Angola', 'Country_Antigua and Barbuda', 'Country_Argentina', 'Country_Armenia', 'Country_Australia', 'Country_Austria', 'Country_Azerbaijan', 'Country_Bahamas', 'Country_Bahrain', 'Country_Bangladesh', 'Country_Barbados', 'Country_Belarus', 'Country_Belgium', 'Country_Belize', 'Country_Benin', 'Country_Bhutan', 'Country_Bolivia (Plurinational State of)', 'Country_Bosnia and Herzegovina', 'Country_Botswana', 'Country_Brazil', 'Country_Brunei Darussalam', 'Country_Bulgaria', 'Country_Burkina Faso', 'Country_Burundi', 'Country_Cabo Verde', 'Country_Cambodia', 'Country_Cameroon', 'Country_Canada', 'Country_Central African Republic', 'Country_Chad', 'Country_Chile', 'Country_China', 'Country_Colombia', 'Country_Comoros', 'Country_Congo', 'Country_Costa Rica', 'Country_Croatia', 'Country_Cuba', 'Country_Cyprus', 'Country_Czechia', "Country_Côte d'Ivoire", "Country_Democratic People's Republic of Korea", 'Country_Democratic Republic of the Congo', 'Country_Denmark', 'Country_Djibouti', 'Country_Dominican Republic', 'Country_Ecuador', 'Country_Egypt', 'Country_El Salvador', 'Country_Equatorial Guinea', 'Country_Eritrea', 'Country_Estonia', 'Country_Ethiopia', 'Country_Fiji', 'Country_Finland', 'Country_France', 'Country_Gabon', 'Country_Gambia', 'Country_Georgia', 'Country_Germany', 'Country_Ghana', 'Country_Greece', 'Country_Grenada', 'Country_Guatemala', 'Country_Guinea', 'Country_Guinea-Bissau', 'Country_Guyana', 'Country_Haiti', 'Country_Honduras', 'Country_Hungary', 'Country_Iceland', 'Country_India', 'Country_Indonesia', 'Country_Iran (Islamic Republic of)', 'Country_Iraq', 'Country_Ireland', 'Country_Israel', 'Country_Italy', 'Country_Jamaica', 'Country_Japan', 'Country_Jordan', 'Country_Kazakhstan', 'Country_Kenya', 'Country_Kiribati', 'Country_Kuwait', 'Country_Kyrgyzstan', "Country_Lao People's Democratic Republic", 'Country_Latvia', 'Country_Lebanon', 'Country_Lesotho', 'Country_Liberia', 'Country_Libya', 'Country_Lithuania', 'Country_Luxembourg', 'Country_Madagascar', 'Country_Malawi', 'Country_Malaysia', 'Country_Maldives', 'Country_Mali', 'Country_Malta', 'Country_Mauritania', 'Country_Mauritius', 'Country_Mexico', 'Country_Micronesia (Federated States of)', 'Country_Mongolia', 'Country_Montenegro', 'Country_Morocco', 'Country_Mozambique', 'Country_Myanmar', 'Country_Namibia', 'Country_Nepal', 'Country_Netherlands', 'Country_New Zealand', 'Country_Nicaragua', 'Country_Niger', 'Country_Nigeria', 'Country_Norway', 'Country_Oman', 'Country_Pakistan', 'Country_Panama', 'Country_Papua New Guinea', 'Country_Paraguay', 'Country_Peru', 'Country_Philippines', 'Country_Poland', 'Country_Portugal', 'Country_Qatar', 'Country_Republic of Korea', 'Country_Republic of Moldova', 'Country_Romania', 'Country_Russian Federation', 'Country_Rwanda', 'Country_Saint Lucia', 'Country_Saint Vincent and the Grenadines', 'Country_Samoa', 'Country_Sao Tome and Principe', 'Country_Saudi Arabia', 'Country_Senegal', 'Country_Serbia', 'Country_Seychelles', 'Country_Sierra Leone', 'Country_Singapore', 'Country_Slovakia', 'Country_Slovenia', 'Country_Solomon Islands', 'Country_Somalia', 'Country_South Africa', 'Country_South Sudan', 'Country_Spain', 'Country_Sri Lanka', 'Country_Sudan', 'Country_Suriname', 'Country_Swaziland', 'Country_Sweden', 'Country_Switzerland', 'Country_Syrian Arab Republic', 'Country_Tajikistan', 'Country_Thailand', 'Country_The former Yugoslav republic of Macedonia', 'Country_Timor-Leste', 'Country_Togo', 'Country_Tonga', 'Country_Trinidad and Tobago', 'Country_Tunisia', 'Country_Turkey', 'Country_Turkmenistan', 'Country_Uganda', 'Country_Ukraine', 'Country_United Arab Emirates', 'Country_United Kingdom of Great Britain and Northern Ireland', 'Country_United Republic of Tanzania', 'Country_United States of America', 'Country_Uruguay', 'Country_Uzbekistan', 'Country_Vanuatu', 'Country_Venezuela (Bolivarian Republic of)', 'Country_Viet Nam', 'Country_Yemen', 'Country_Zambia', 'Country_Zimbabwe'] # Cargar modelos y scalers linear_model = joblib.load("notebook/linear_regression/linear_model.pkl") logistic_model = joblib.load("notebook/logistic_regression/logistic_model.pkl") scaler_lin = joblib.load("notebook/linear_regression/scaler_linear.pkl") feature_order = joblib.load("notebook/linear_regression/feature_order.pkl") scaler_log = joblib.load("notebook/logistic_regression/scaler_logistic.pkl") def validate_lineal_inputs(country, year, adult_mortality, expenditure_perce, measles, five_deaths, polio, total_exp, hiv, thinness, income_composition, schooling, status_dev): errors = [] if not country: errors.append("❌ Debe seleccionar un país") if year < 1900 or year > 2100: errors.append("❌ Año debe estar entre 1900 y 2100") if adult_mortality < 0 or adult_mortality > 450: errors.append("❌ Mortalidad adulta debe estar entre 0 y 450") if expenditure_perce < 0 or expenditure_perce > 7000: errors.append("❌ Gasto porcentual debe estar entre 0 y 7000") if measles < 0: errors.append("❌ Casos de sarampión no puede ser negativo") if five_deaths < 0: errors.append("❌ Muertes menores de 5 años no puede ser negativo") if polio < 0 or polio > 100: errors.append("❌ Tasa de polio debe estar entre 0 y 100") if total_exp < 0 or total_exp > 100: errors.append("❌ Gasto total debe estar entre 0 y 100") if hiv < 0 or hiv > 15: errors.append("❌ Tasa de HIV/SIDA debe estar entre 0 y 15") if thinness < 0 or thinness > 20: errors.append("❌ Delgadez 10-19 años debe estar entre 0 y 20") if income_composition < 0.2 or income_composition > 1: errors.append("❌ Composición de ingresos debe estar entre 0.2 y 1") if schooling < 0 or schooling > 20: errors.append("❌ Escolaridad debe estar entre 0 y 20") if status_dev not in [0, 1]: errors.append("❌ Estado de desarrollo debe ser 0 (NO) o 1 (SÍ)") return "\n".join(errors) if errors else None def validate_logistic_inputs(age, bmi, former_smoke, never_smoke, smokes, glucose): errors = [] if age < 0 or age > 120: errors.append("❌ Edad debe estar entre 0 y 120 años") if bmi < 10 or bmi > 100: errors.append("❌ BMI debe estar entre 10 y 100") if former_smoke not in [0, 1]: errors.append("❌ Ex-fumador debe ser 0 (NO) o 1 (SÍ)") if never_smoke not in [0, 1]: errors.append("❌ Nunca fumó debe ser 0 (NO) o 1 (SÍ)") if smokes not in [0, 1]: errors.append("❌ Fumador actual debe ser 0 (NO) o 1 (SÍ)") tobacco_combo = (former_smoke, never_smoke, smokes) valid_combinations = [ (1, 0, 0), # Exfumador (0, 1, 0), # Nunca fumó (0, 0, 1) # Fumador actual ] if tobacco_combo not in valid_combinations: errors.append("❌ Las opciones de tabaquismo no son coherentes. Solo una debe marcarse como 'Sí'.") if glucose < 55 or glucose > 270: errors.append("❌ Nivel de glucosa debe estar entre 55 y 270 mg/dL") return "\n".join(errors) if errors else None def predecir_lineal(pais, *args): # Validar inputs primero validation_error = validate_lineal_inputs(pais, *args) if validation_error: return validation_error base_names = [ "Year", "Adult Mortality", "percentage expenditure", "Measles", "under-five deaths", "Polio", "Total expenditure", "HIV/AIDS", "thinness 10-19 years", "Income composition of resources", "Schooling", "Status_Developing" ] try: base_inputs = list(args) base_dict = dict(zip(base_names, base_inputs)) # Codificar país country_dict = {col: 0 for col in country_cols} if pais in country_dict: country_dict[pais] = 1 # Combinar todo en un solo dict full_input_dict = {**base_dict, **country_dict} # Reordenar según feature_order x_ordered = np.array([[full_input_dict[col] for col in feature_order]]) # Escalar y predecir x_scaled = scaler_lin.transform(x_ordered) pred = linear_model.predict(x_scaled) return f"📈 Esperanza de vida estimada: {pred[0]:.2f} años" except Exception as e: return f"⚠️ Error al procesar la solicitud: {str(e)}" def predecir_logistica(*args): # Validar inputs primero validation_error = validate_logistic_inputs(*args) if validation_error: return validation_error input_names = ['age', 'bmi', 'smoke_formerly smoked', 'smoke_never smoked', 'smoke_smokes', 'avg_glucose_level'] try: input_values = list(args) input_dict = dict(zip(input_names, input_values)) # Reordenar y convertir a DataFrame X_df = pd.DataFrame([input_dict], columns=input_names) # Escalar X_scaled = scaler_log.transform(X_df) # Predecir pred = logistic_model.predict(X_scaled) return "🟥 Riesgo de accidente cerebrovascular: ALTO" if pred[0] == 1 else "🟩 Riesgo de accidente cerebrovascular: BAJO" except Exception as e: return f"⚠️ Error al procesar la solicitud: {str(e)}" # --- Interfaz con pantallas independientes --- with gr.Blocks(title="App de Regresión", theme=gr.themes.Soft( primary_hue="pink", secondary_hue="pink", neutral_hue="gray" ), css=""" .custom-header { color: #d14d8b !important; text-align: center; padding: 10px; border-radius: 8px; } .custom-button { background: #f8c8dc !important; color: #333 !important; border: 1px solid #f8c8dc !important; } .custom-button:hover { background: #f5b5d1 !important; } .gr-box { border: 1px solid #f8c8dc !important; border-radius: 8px !important; } .error-message { color: #ff0000 !important; font-weight: bold; } """) as demo: current_page = gr.State("home") # --- Pantalla de inicio --- with gr.Column(visible=True) as pantalla_inicio: gr.Markdown("# Bienvenido a la App del Proyecto #1 de Análisis Numérico", elem_classes=["custom-header"]) gr.Markdown("En este proyecto tenemos dos modelos de machine learning entrenados por medio de gradiente descendiente, uno de regresión lineal y otro de regresión logistica") with gr.Row(): btn_go_lineal = gr.Button("Regresión Lineal", elem_classes=["custom-button"]) btn_go_log = gr.Button("Regresión Logística", elem_classes=["custom-button"]) # --- Pantalla Lineal --- with gr.Column(visible=False) as pantalla_lineal: gr.Markdown("## Regresión Lineal", elem_classes=["custom-header"]) with gr.Row(): with gr.Column(scale=3): country_dropdown = gr.Dropdown(choices=country_cols, label="País") year_input = gr.Number(label="Año: [2000-2015]", minimum=1900, maximum=2100) adult_mortality = gr.Number(label="Mortalidad adulta [1-450]", minimum=0, maximum=450) expenditure_perce = gr.Number(label="Gasto porcentual [0.005-7000]", minimum=0, maximum=7000) measles = gr.Number(label="Sarampión [0–40000]", minimum=0,maximum=40000) five_deaths = gr.Number(label="Muertes de menores de cinco años [0-100]", minimum=0,maximum=100) polio_input = gr.Number(label="Polio (% inmunizados) [0-100]", minimum=0, maximum=100) total_exp = gr.Number(label="Gasto total (% PIB) [0-17.9]", minimum=0, maximum=18) hiv_input = gr.Number(label="VIH/SIDA [0.1-15]", minimum=0, maximum=15) thiness_input = gr.Number(label="Delgadez 10-19 años [0-20]", minimum=0, maximum=20) compos_input = gr.Number(label="Composición de ingresos de los recursos [0.2-1]", minimum=0.2, maximum=1) schooling = gr.Number(label="Enseñanza [0-20] años", minimum=0, maximum=20) dev_input = gr.Dropdown(choices=[("No", 0),("Si", 1)], label="En desarrollo",value=0) base_inputs = [ year_input, adult_mortality, expenditure_perce, measles, five_deaths, polio_input, total_exp, hiv_input, thiness_input, compos_input, schooling, dev_input ] with gr.Column(scale=2): btn_lineal = gr.Button("Predecir", elem_classes=["custom-button"]) out_lineal = gr.Textbox(label="Resultado", lines=3) btn_back1 = gr.Button("Volver al inicio", elem_classes=["custom-button"]) # --- Pantalla Logística --- with gr.Column(visible=False) as pantalla_log: gr.Markdown("## Regresión Logística", elem_classes=["custom-header"]) with gr.Row(): with gr.Column(scale=3): age_input = gr.Number(label="Edad", minimum=0, maximum=120) bmi_input = gr.Number(label="IMC (Índice de masa corporal) [10-70]", minimum=10, maximum=70) former_smoke = gr.Dropdown(choices=[("No", 0),("Si", 1)], label="Ex-fumador",value=0) never_smoke = gr.Dropdown(choices=[("No", 0),("Si", 1)], label="Nunca ha fumado",value=0) smokes = gr.Dropdown(choices=[("No", 0),("Si", 1)], label="Fuma",value=0) glucose_input = gr.Number(label="Nivel promedio de glucosa [55-270]", minimum=54, maximum=270) inputs_log = [ age_input, bmi_input, former_smoke, never_smoke, smokes, glucose_input ] with gr.Column(scale=2): btn_log = gr.Button("Predecir", elem_classes=["custom-button"]) out_log = gr.Textbox(label="Resultado", lines=3) btn_back2 = gr.Button("Volver al inicio", elem_classes=["custom-button"]) # --- Lógica de navegación --- def show_page(page): return { pantalla_inicio: gr.update(visible=(page == "home")), pantalla_lineal: gr.update(visible=(page == "lineal")), pantalla_log: gr.update(visible=(page == "log")), current_page: page } btn_go_lineal.click(lambda: show_page("lineal"), outputs=[pantalla_inicio, pantalla_lineal, pantalla_log, current_page]) btn_go_log.click(lambda: show_page("log"), outputs=[pantalla_inicio, pantalla_lineal, pantalla_log, current_page]) btn_back1.click(lambda: show_page("home"), outputs=[pantalla_inicio, pantalla_lineal, pantalla_log, current_page]) btn_back2.click(lambda: show_page("home"), outputs=[pantalla_inicio, pantalla_lineal, pantalla_log, current_page]) # --- Eventos de predicción --- btn_lineal.click( predecir_lineal, inputs=[country_dropdown] + base_inputs, outputs=out_lineal ) btn_log.click( predecir_logistica, inputs=inputs_log, outputs=out_log ) if __name__ == "__main__": demo.launch()