# ─────────────────────────────────────────────────────────────────────────────
# REGRESIÓN LOGÍSTICA — Predicción de aprobados/suspensos
# ─────────────────────────────────────────────────────────────────────────────
# La regresión LOGÍSTICA se usa cuando queremos predecir una CATEGORÍA (0 o 1),
# no un número continuo. Aquí queremos saber: ¿aprobará o suspenderá?
# ─────────────────────────────────────────────────────────────────────────────
import pandas as pd
from sklearn.linear_model import LogisticRegression # el modelo de clasificación
from sklearn.model_selection import train_test_split # para dividir los datos
# ── 1. DATASET ────────────────────────────────────────────────────────────────
# Cada lista representa una columna con los datos de 15 estudiantes.
# La columna 'aprobo' es la que queremos predecir: 1 = aprobó, 0 = suspendió.
datos = {
'horas_estudio': [1.5, 3.0, 2.0, 4.5, 1.0, 3.5, 2.5, 0.5, 4.0, 2.0, 3.0, 1.5, 5.0, 2.5, 1.0],
'asistencia': [72, 90, 65, 95, 60, 88, 78, 55, 92, 70, 85, 68, 98, 75, 62],
'nota_parcial': [4.5, 7.2, 5.0, 8.8, 3.9, 7.5, 6.1, 3.0, 8.2, 5.5, 7.0, 4.8, 9.5, 6.3, 4.0],
'horas_suenio': [6, 8, 7, 8, 5, 7, 6, 5, 8, 7, 8, 6, 9, 7, 6],
'aprobo': [0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0]
}
# Convertimos el diccionario en un DataFrame (tabla) de pandas.
# Es la estructura más cómoda para trabajar con datos en Python.
df = pd.DataFrame(datos)
# ── 2. SEPARAR X e y ──────────────────────────────────────────────────────────
# X = variables predictoras (las "pistas" que el modelo usará para aprender)
# y = variable objetivo (lo que queremos predecir)
#
# Nota: X lleva VARIAS columnas → usamos dobles corchetes df[[ ]]
# y lleva UNA sola columna → usamos un corchete df[ ]
X = df[['horas_estudio', 'asistencia', 'nota_parcial', 'horas_suenio']]
y = df['aprobo']
# ── 3. DIVIDIR EN ENTRENAMIENTO Y TEST ────────────────────────────────────────
# No podemos evaluar el modelo con los mismos datos con los que aprendió
# (sería como darle las respuestas del examen antes del examen).
# Guardamos un 20% de los datos para comprobar después si el modelo acierta.
#
# X_train, y_train → datos con los que el modelo va a aprender (80%)
# X_test, y_test → datos que guardamos para evaluar al final (20%)
# random_state=42 → fija el azar para que la división sea siempre la misma
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# ── 4. CREAR Y ENTRENAR EL MODELO ─────────────────────────────────────────────
# LogisticRegression() crea el modelo (aún no sabe nada).
modelo = LogisticRegression()
# .fit() es donde ocurre el aprendizaje: el modelo analiza X_train e y_train
# y ajusta sus parámetros internos para predecir lo mejor posible.
# IMPORTANTE: solo usamos los datos de ENTRENAMIENTO aquí, nunca X_test.
modelo.fit(X_train, y_train)
# ── 5. PREDECIR ESTUDIANTES NUEVOS ────────────────────────────────────────────
# Aquí probamos el modelo con estudiantes que NO estaban en el dataset.
# Cada fila es un estudiante: [horas_estudio, asistencia, nota_parcial, horas_suenio]
#
# ⚠ OJO: el tercer estudiante tiene horas_suenio=1 (parece un error en los datos,
# debería ser algo como 6, 7 u 8). El modelo igualmente hace una predicción,
# pero el resultado puede no ser realista.
estudiante_nuevo = [
[3.5, 82, 6.8, 7], # estudiante 1: estudia bastante, buena nota parcial
[2.0, 60, 6.0, 6], # estudiante 2: perfil intermedio
[4.0, 50, 5.0, 8], # estudiante 3: estudia mucho pero baja asistencia
]
# predict_proba() devuelve la PROBABILIDAD de pertenecer a cada clase.
# Para cada estudiante devuelve dos números: [P(suspende), P(aprueba)]
# Ambos suman siempre 1.0 (100%).
# Ejemplo: [0.30, 0.70] → 30% de suspender, 70% de aprobar
prob = modelo.predict_proba(estudiante_nuevo)
# predict() devuelve directamente la CLASE predicha: 0 (suspenso) o 1 (aprobado).
# Internamente aplica un umbral de 0.5: si P(aprueba) > 0.5 → predice 1.
clase = modelo.predict(estudiante_nuevo)
# Accedemos a la probabilidad de suspender del SEGUNDO estudiante (índice 1).
# prob tiene forma [estudiante][clase]: prob[1][0] = P(suspende) del estudiante 2.
print(prob[1][0])
# Imprime la clase predicha de los tres estudiantes: algo como [1 0 1]
print(clase)
# Recorremos todos los estudiantes e imprimimos sus probabilidades de forma legible.
# round(...*100) convierte 0.73 en 73 para mostrarlo como porcentaje entero.
for probabilidad in prob:
print(
f"Probabilidad de suspender {round(probabilidad[0] * 100)}% "
f"y de aprobar {round(probabilidad[1] * 100)}%"
)
# ── 6. EVALUAR EL MODELO SOBRE LOS DATOS DE TEST ──────────────────────────────
# Ahora usamos X_test (las características que el modelo NO vio al entrenar)
# para ver si predice correctamente. Comparamos con y_test (los valores reales).
# y_pred contiene la clase predicha (0 o 1) para cada fila de X_test
y_pred = modelo.predict(X_test)
# y_pred_proba contiene las probabilidades [P(0), P(1)] para cada fila de X_test
y_pred_proba = modelo.predict_proba(X_test)
# Clases predichas: [1 0 1 ...] → lo que el modelo cree que va a pasar
print(y_pred)
# Probabilidades: [[0.3, 0.7], [0.8, 0.2], ...] → con qué seguridad lo predice
print(y_pred_proba)
# Clases reales: los valores verdaderos de y_test que guardamos al principio.
# Comparando y_pred con y_test podemos calcular si el modelo acierta o falla.
print(y_test)