Notebook 2
Análisis Exploratorio de Datos (EDA) en Python: Regresión Lineal y Logística
Autor: P. Madariaga (2026)
Institución: Universidad Andrés Bello, Santiago.
Este notebook presenta el desarrollo práctico de dos enfoques de modelación estadística utilizando el dataset de pacientes de la comunidad Pima en Python:
- Regresión Logística: Para predecir la presencia o ausencia de diabetes (variable binaria).
- Regresión Lineal Múltiple: Para predecir los niveles de glucosa (variable continua).
#pip install pandas numpy matplotlib seaborn scikit-learn statsmodels notebook ipykernel
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score
import statsmodels.api as sm# URL del repositorio con los datos crudos
url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv"
# Definición de las variables clínicas y objetivo
column_names = [
'pregnant', # Número de embarazos
'glucose', # Concentración de glucosa plasmática a las 2 horas
'pressure', # Presión arterial diastólica (mm Hg)
'triceps', # Grosor del pliegue cutáneo del tríceps (mm)
'insulin', # Insulina sérica de 2 horas (mu U/ml)
'mass', # Índice de masa corporal (peso en kg / (altura en m)^2)
'pedigree', # Función de pedigrí de diabetes (historial familiar)
'age', # Edad (años)
'diabetes' # Variable objetivo (0 = No presenta, 1 = Presenta)
]
# Cargar los datos en un DataFrame de pandas
pima = pd.read_csv(url, names=column_names)
# Transformación de la variable objetivo a tipo categórico descriptivo ('neg', 'pos')
pima['diabetes'] = pima['diabetes'].map({0: 'neg', 1: 'pos'}).astype('category')
print("Dataset estructurado e inicializado correctamente.")
Dataset estructurado e inicializado correctamente.
# 1. Crear una copia del DataFrame original para mantener el histórico intacto
pima_imputada = pima.copy()
# 2. Reemplazar los ceros inválidos por NaN en el nuevo dataset
invalid_zero_cols = ['glucose', 'pressure', 'triceps', 'insulin', 'mass']
for col in invalid_zero_cols:
pima_imputada[col] = pima_imputada[col].replace(0, np.nan)
# 3. Imputación robusta utilizando la mediana del nuevo dataset
for col in invalid_zero_cols:
median_value = pima_imputada[col].median()
pima_imputada[col] = pima_imputada[col].fillna(median_value)
# Ahora ya tienes disponible 'pima_imputada' para las siguientes celdas del notebook
print(pima_imputada.head()) pregnant glucose pressure triceps insulin mass pedigree age diabetes
0 6 148.0 72.0 35.0 125.0 33.6 0.627 50 pos
1 1 85.0 66.0 29.0 125.0 26.6 0.351 31 neg
2 8 183.0 64.0 29.0 125.0 23.3 0.672 32 pos
3 1 89.0 66.0 23.0 94.0 28.1 0.167 21 neg
4 0 137.0 40.0 35.0 168.0 43.1 2.288 33 pos
# Nota: Se asume que el DataFrame original 'pima_imputada' ya está cargado.
# Si tu variable objetivo tiene valores "neg" y "pos", la mapeamos a 0 y 1 para los algoritmos de Python.
if 'diabetes' in pima_imputada.columns and pima_imputada['diabetes'].dtype == 'object':
pima_imputada['diabetes'] = pima_imputada['diabetes'].map({'neg': 0, 'pos': 1})
# Separar características (X) y variable objetivo (y)
X = pima_imputada.drop(columns=['diabetes'])
y = pima_imputada['diabetes']
# 1. Normalizar todas las características continuas
scaler = StandardScaler()
X_scaled = pd.DataFrame(scaler.fit_transform(X), columns=X.columns)
# Visualizar las primeras filas del dataset normalizado
X_scaled.head() pregnant glucose pressure triceps insulin mass pedigree \
0 0.639947 0.866045 -0.031990 0.670643 -0.181541 0.166619 0.468492
1 -0.844885 -1.205066 -0.528319 -0.012301 -0.181541 -0.852200 -0.365061
2 1.233880 2.016662 -0.693761 -0.012301 -0.181541 -1.332500 0.604397
3 -0.844885 -1.073567 -0.528319 -0.695245 -0.540642 -0.633881 -0.920763
4 -1.141852 0.504422 -2.679076 0.670643 0.316566 1.549303 5.484909
age
0 1.425995
1 -0.190672
2 -0.105584
3 -1.041549
4 -0.020496 2. División del Conjunto de Datos (Train / Test)
Dividimos el conjunto de datos asignando un 80% para el entrenamiento y un 20% para la prueba, garantizando la reproducibilidad mediante una semilla aleatoria (random_state=42).
# Dividir los datos en entrenamiento y prueba (80% entrenamiento, 20% prueba)
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.20, random_state=42, stratify=y)
print(f"Registros de entrenamiento: {X_train.shape[0]}")
print(f"Registros de prueba: {X_test.shape[0]}")Registros de entrenamiento: 614
Registros de prueba: 154
3. Modelo de Regresión Logística y Evaluación
La Fórmula General de la Regresión Logística
En un problema de clasificación con múltiples variables predictoras (), la ecuación general para el logit (que representa el logaritmo natural de la razón de probabilidades u odds) se define como:
Donde:
- : Es la probabilidad de que ocurra el evento de interés (en este estudio, tener diabetes: ).
- : Es el intercepto (o sesgo), que representa el valor del logit cuando todas las variables predictoras equivalen a cero.
- : Son los coeficientes (o pesos) estimados para cada variable independiente ().
Ajustamos el modelo de clasificación y obtenemos sus métricas a través de la matriz de confusión generada en el conjunto de prueba.
# Crear y entrenar el modelo de regresión logística
log_model = LogisticRegression(C=float('inf'))
log_model.fit(X_train, y_train)
# Hacer predicciones (clase directa empleando el umbral por defecto de 0.5)
y_pred = log_model.predict(X_test)
# Evaluar el modelo con una matriz de confusión
matriz_confusion = confusion_matrix(y_test, y_pred)
tn, fp, fn, tp = matriz_confusion.ravel()
print("Matriz de Confusión:")
print(pd.DataFrame(matriz_confusion, index=['Realidad: neg', 'Realidad: pos'], columns=['Predicción: neg', 'Predicción: pos']))
df_cm = pd.DataFrame(
matriz_confusion,
index=['Realidad: neg', 'Realidad: pos'],
columns=['Predicción: neg', 'Predicción: pos']
)
# Graficar el Heatmap de la Matriz de Confusión
plt.figure(figsize=(6, 4.5))
sns.heatmap(
df_cm,
annot=True, # Incluye los números dentro de los cuadrantes
fmt='d', # Formato de números enteros
cmap='Blues', # Paleta de colores degradados en azul
cbar=True, # Muestra la barra de escala de color lateral
square=False # Mantiene proporciones rectangulares estilizadas
)
plt.title('Mapa de Calor - Matriz de Confusión', fontsize=14, pad=15)
plt.ylabel('Clase Real', fontsize=12)
plt.xlabel('Clase Predicha', fontsize=12)
plt.tight_layout()
plt.show()
# Imprimir métricas de evaluación
print(f"\nExactitud: {accuracy_score(y_test, y_pred):.4f}")
print(f"Precisión: {precision_score(y_test, y_pred, pos_label='pos'):.4f}")
print(f"Sensibilidad (Recall): {recall_score(y_test, y_pred, pos_label='pos'):.4f}")
print(f"F1-Score: {f1_score(y_test, y_pred, pos_label='pos'):.4f}")
Matriz de Confusión:
Predicción: neg Predicción: pos
Realidad: neg 82 18
Realidad: pos 27 27
Exactitud: 0.7078
Precisión: 0.6000
Sensibilidad (Recall): 0.5000
F1-Score: 0.5455
Explicación
1. Matriz de Confusión
Es una tabla de doble entrada que permite evaluar el desempeño de un modelo de clasificación al contrastar los valores reales frente a las predicciones generadas por el algoritmo.
Análisis de los resultados actuales:
- Verdaderos Negativos (82): Pacientes que realmente estaban sanos (
neg) y el modelo los clasificó correctamente como sanos. - Falsos Positivos (18): Pacientes sanos que el modelo clasificó erróneamente como enfermos (
pos). - Falsos Negativos (27): Pacientes que sí tienen diabetes, pero el modelo no los detectó (los clasificó como
neg). En el contexto médico, este representa el error más crítico. - Verdaderos Positivos (27): Pacientes que tienen diabetes y el modelo los identificó correctamente.
2. Exactitud (Accuracy) — 0.7078 (70.78%)
Definición: Es la proporción de predicciones correctas (tanto de la clase positiva como de la negativa) respecto al total de casos evaluados.
Interpretación: El modelo acierta en general el 70.78% de las veces al clasificar a los pacientes. Aunque parece un porcentaje aceptable, en medicina la exactitud puede ser engañosa si las clases están desbalanceadas (por ejemplo, si hay muchos más pacientes sanos que enfermos en la muestra).
3. Precisión (Precision) — 0.6000 (60.00%)
Definición: Es el porcentaje de aciertos positivos calculados únicamente sobre el total de casos que el modelo predijo como positivos. Mide la calidad o la "fiabilidad" de la alarma del modelo.
Interpretación: Cada vez que el modelo genera una alerta diagnosticando a un paciente con diabetes (Predicción: pos), tiene un 60.00% de probabilidad de que sea un caso real. El 40% restante corresponde a falsas alarmas (falsos positivos).
4. Sensibilidad o Exhaustividad (Recall / Sensitivity) — 0.5000 (50.00%)
Definición: Es la proporción de casos positivos reales que el modelo fue capaz de identificar y capturar correctamente. Mide la capacidad del algoritmo para detectar la enfermedad en la población afectada.
Interpretación: De todos los pacientes que realmente tienen diabetes en el conjunto de prueba, el modelo solo logra detectar al 50.00%. La otra mitad de los enfermos (27 personas) no son diagnosticados (falsos negativos). En un entorno clínico, este indicador requiere un valor significativamente alto para asegurar que ningún paciente se quede sin tratamiento.
5. F1-Score — 0.5455 (54.55%)
Definición: Es la media armónica entre la Precisión y la Sensibilidad. Proporciona una métrica de balance única, siendo de gran utilidad para analizar el desempeño general del clasificador cuando existe desbalance de clases.
Interpretación: Con un valor de 54.55%, el F1-Score confirma que el rendimiento del modelo en la clase con diabetes es moderado-bajo, viéndose afectado directamente por la baja Sensibilidad (el alto volumen de pacientes enfermos omitidos).
Conclusión Diagnóstica
El modelo actual muestra un comportamiento aceptable para identificar a los pacientes sanos, pero exhibe una deficiencia importante al diagnosticar a los pacientes con diabetes (Sensibilidad del 50%). Para optimizar estos indicadores en fases posteriores, se podría evaluar el ajuste del umbral de decisión de la regresión logística (bajarlo por debajo de 0.5) para priorizar la captura de casos positivos, o aplicar técnicas de balanceo de datos.
4. Extensión: Modelo de Regresión Lineal Múltiple
La Fórmula General de la Regresión Lineal Múltiple
A diferencia de la regresión logística (que predice probabilidades), la regresión lineal múltiple busca predecir el valor exacto de una variable continua () combinando linealmente un conjunto de variables predictoras ().
La ecuación matemática general se define como:
Donde:
- : Es la variable dependiente o respuesta continua que deseamos predecir (en nuestro caso,
glucose). - : Es el intercepto, que representa el valor esperado de cuando todas las variables predictoras son iguales a cero.
- : Son los coeficientes parciales de regresión (o pesos) para cada variable. Indican el cambio estimado en por cada unidad de cambio en su respectiva variable , asumiendo que todas las demás variables se mantienen constantes.
- : Es el término de error aleatorio o residuo (la diferencia entre el valor real y el valor predicho por el modelo).
Para obtener un resumen estadístico detallado con los valores (p-values) equivalentes a la función summary(lm(...)) de R, utilizaremos la librería statsmodels. Definiremos glucose como la variable dependiente continua.
# Reconfigurar variables para la regresión lineal utilizando los datos de entrenamiento escalados
# Nota: Como X_train contiene 'glucose', la removemos de las predictoras y la usamos como objetivo.
X_train_linear = X_train.drop(columns=['glucose'])
y_train_linear = X_train['glucose']
# Statsmodels requiere añadir explícitamente una columna de constantes para el intercepto (beta_0)
X_train_linear_const = sm.add_constant(X_train_linear)
# Ajustar el modelo de regresión lineal múltiple
modelo_lineal = sm.OLS(y_train_linear, X_train_linear_const).fit()
# Ver los resultados detallados del modelo (Coeficientes, R-squared, p-values)
print(modelo_lineal.summary()) OLS Regression Results
==============================================================================
Dep. Variable: glucose R-squared: 0.287
Model: OLS Adj. R-squared: 0.279
Method: Least Squares F-statistic: 34.90
Date: Wed, 08 Jul 2026 Prob (F-statistic): 5.85e-41
Time: 16:06:58 Log-Likelihood: -758.31
No. Observations: 614 AIC: 1533.
Df Residuals: 606 BIC: 1568.
Df Model: 7
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 0.0138 0.034 0.408 0.683 -0.053 0.080
pregnant -0.0247 0.041 -0.605 0.545 -0.105 0.056
pressure 0.0895 0.037 2.445 0.015 0.018 0.161
triceps 0.0429 0.040 1.063 0.288 -0.036 0.122
insulin 0.4138 0.039 10.626 0.000 0.337 0.490
mass 0.0855 0.042 2.026 0.043 0.003 0.168
pedigree 0.0404 0.035 1.153 0.249 -0.028 0.109
age 0.2132 0.042 5.124 0.000 0.131 0.295
==============================================================================
Omnibus: 28.328 Durbin-Watson: 1.927
Prob(Omnibus): 0.000 Jarque-Bera (JB): 31.041
Skew: 0.542 Prob(JB): 1.82e-07
Kurtosis: 3.191 Cond. No. 2.25
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
Explicación:
1. Ajuste e Información Global del Modelo (Parte Superior)
Este bloque evalúa el rendimiento general del modelo y explica qué tan bien se ajusta a la variabilidad de la glucosa en la muestra de datos.
- Dep. Variable (
glucose): Identifica la variable dependiente (o continua) que el algoritmo está intentando estimar o predecir. - No. Observations (
614): El tamaño de la muestra utilizada para el entrenamiento. El modelo se construyó optimizando los parámetros sobre 614 registros de pacientes. - R-squared (
0.287) / Adj. R-squared (0.279): El coeficiente de determinación indica que aproximadamente el 28.7% de la variabilidad de los niveles de glucosa es explicada linealmente por el conjunto de variables predictoras seleccionadas. El valor ajustado (27.9%) penaliza la inclusión de variables irrelevantes, confirmando un poder predictivo moderado. - F-statistic (
34.90) y Prob (F-statistic) (5.85e-41): Evalúa la significancia global del modelo. Dado que la probabilidad estadística es prácticamente cero (), se rechaza firmemente la hipótesis nula de que todos los coeficientes son cero, demostrando que el modelo en su conjunto es altamente significativo.
2. Tabla de Coeficientes y Significancia (Parte Central)
Mide el impacto individual y la relevancia estadística de cada variable clínica sobre los niveles de glucosa. Al trabajar con variables normalizadas, el valor del coeficiente (coef) actúa como un indicador directo de la importancia relativa de cada predictor.
- const (
0.0138/ ): Representa el intercepto (). Debido a que su valor , no cuenta con significancia estadística (no es diferente de cero). Esto es un comportamiento esperado bajo datos estandarizados, donde un paciente promedio en todas sus variables tendrá una glucosa predictiva muy cercana a la media poblacional. - insulin (
0.4138/ ): Es el predictor más fuerte y estadísticamente significativo. Por cada desviación estándar que aumente el nivel de insulina, la glucosa estimada se incrementa en unidades estandarizadas. - age (
0.2132/ ): El segundo factor en nivel de importancia y altamente significativo. Revela una relación directa donde a mayor edad, mayor es el nivel de glucosa estimado por el modelo. - pressure (
0.0895/ ) y mass (0.0855/ ): Ambos parámetros exhiben impactos positivos y resultan estadísticamente significativos (). Un incremento en la presión arterial o en el Índice de Masa Corporal (IMC) eleva moderadamente los valores estimados de glucosa. - pregnant, triceps y pedigree (): Con valores de 0.545, 0.288 y 0.249 respectivamente, se determina que no poseen significancia estadística. Ante la presencia de variables robustas como la insulina, el IMC y la edad, estos tres factores no aportan información linealmente relevante para la predicción.
3. Diagnósticos del Modelo y Residuos (Parte Inferior)
Estadísticos diseñados para validar el cumplimiento de los supuestos fundamentales que rigen la validez matemática de la regresión lineal por mínimos cuadrados.
- Durbin-Watson (
1.927): Evalúa el supuesto de independencia midiendo la autocorrelación de los residuos. Un resultado cercano a 2.0 indica que no existen problemas de autocorrelación, garantizando que los errores entre observaciones son independientes. - Omnibus (
28.328) / Prob(Omnibus) (0.000) y Jarque-Bera (31.041) / Prob(JB) (1.82e-07): Pruebas de hipótesis enfocadas en analizar la normalidad de los errores. Como sus probabilidades asociadas son menores a , indican que los residuos no siguen una distribución perfectamente normal. - Skew (
0.542): Muestra el grado de asimetría de los errores, indicando en este caso una ligera cola o desviación hacia el extremo derecho de la distribución (valores positivos). - Cond. No. (
2.25): Mide el índice numérico de condición para evaluar la multicolinealidad. Un valor por debajo de 20 es óptimo, confirmando que no hay presencia de multicolinealidad extrema entre las variables independientes y que el modelo es estable.
Bibliografía
Abrir PDF en nueva ventanaCargando PDF…