Machine Learning - Modelo de Regresión Logística
ANALITICA PARA LA TOMA DE DECISIONES
Practica 3
30 / Agosto / 2023
Docente: Ing. Manuela Londoño Ocampo
Realizado por: Javier Elohim Burgos Chaguezac
Facultad de Ingeniería - Departamento de Ingeniería Industrial
1). PREPARACIÓN DEL NOTEBOOK
import warnings
warnings.filterwarnings("ignore")
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
%matplotlib inline
2). REGRESIÓN LINEAL
Caso de estudio
Consideremos un caso de uso en el que tenemos que predecir los resultados de los exámenes de los estudiantes de un curso universitario, es decir, clasificar los resultados en términos de aprobado (1) o No aprobado (0) en función de las horas de estudio dedicadas por cada estudiante.
Se cuenta un conjunto de datos de estudiantes y su dedicación de estudio en términos de horas. A continuación se detallan las variables disponibles:
- Pass_or_Fail: Etiqueta que tiene dos valores: 1 o 0. Un valor de 1 indica aprobación de examen y un valor de 0 indica No aprobación del examen.
- Self_Study_Daily: Indica cuántas horas el estudiante estudia diariamente en casa para la presentación del examen
- Tuition_Monthly: Indica cuántas horas al mes el estudiante está tomando clases particulares de tutoría.
Documentación del modelo: https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html
Lectura de los datos
df_students = pd.read_csv("https://raw.githubusercontent.com/JavierBurgos-web/003_Machine_Learning_Regresion_Logistica/main/student-pass-fail-data.csv")
Exploración de los datos
Dataset
df_students.head(5)
Self_Study_Daily | Tution_Monthly | Pass_Or_Fail | |
---|---|---|---|
0 | 7 | 27 | 1 |
1 | 2 | 43 | 0 |
2 | 7 | 26 | 1 |
3 | 8 | 29 | 1 |
4 | 3 | 42 | 0 |
Tamaño del Dataset
df_students.shape
(1000, 3)
Información general del dataset
df_students.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 1000 entries, 0 to 999 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Self_Study_Daily 1000 non-null int64 1 Tution_Monthly 1000 non-null int64 2 Pass_Or_Fail 1000 non-null int64 dtypes: int64(3) memory usage: 23.6 KB
Estadísticas descriptivas de los datos
df_students.describe()
Self_Study_Daily | Tution_Monthly | Pass_Or_Fail | |
---|---|---|---|
count | 1000.000000 | 1000.000000 | 1000.000000 |
mean | 5.744000 | 31.230000 | 0.499000 |
std | 2.121076 | 5.976355 | 0.500249 |
min | 0.000000 | 20.000000 | 0.000000 |
25% | 4.000000 | 26.000000 | 0.000000 |
50% | 6.000000 | 30.000000 | 0.000000 |
75% | 7.000000 | 36.000000 | 1.000000 |
max | 10.000000 | 50.000000 | 1.000000 |
3). PREPARACIÓN INICIAL DE LOS DATOS
Arreglos
- Line 1. Renombrar a variables
df_students = df_students.rename(columns= {'Pass_Or_Fail': 'target', 'Self_Study_Daily': 'self_study', 'Tution_Monthly': 'tution_monthly'})
df_students.head(10)
self_study | tution_monthly | target | |
---|---|---|---|
0 | 7 | 27 | 1 |
1 | 2 | 43 | 0 |
2 | 7 | 26 | 1 |
3 | 8 | 29 | 1 |
4 | 3 | 42 | 0 |
5 | 5 | 31 | 0 |
6 | 1 | 44 | 0 |
7 | 7 | 26 | 1 |
8 | 8 | 27 | 1 |
9 | 4 | 36 | 0 |
Verificar nuevo tamaño del Dataset
df_students.shape
(1000, 3)
Diagramas
Balanceo de clases dentro de la variable objetivo.(No presenta sesgo)
df_students['target'].value_counts().plot(kind='bar', figsize=(3, 2.5))
plt.xlabel('Target')
plt.show()
Distribución de los datos por clase
plt.figure(figsize=(5, 2.5))
plt.scatter(df_students['self_study'][df_students.target == 0],
df_students['tution_monthly'][df_students.target == 0],
marker='o',
color = 'red',
label = 'No aprueba'
)
plt.scatter(df_students['self_study'][df_students.target == 1],
df_students['tution_monthly'][df_students.target == 1],
marker='D',
color = 'green',
label = 'Aprueba'
)
plt.xlabel('Dedicación propía del estudiante')
plt.ylabel('Horas de tutoria')
plt.legend()
plt.show()
4). MARCO DE VALIDACIÓN
Separaración. Variables de entrada de la Variable objetivo
y = df_students.target
x = df_students.drop(['target'], axis=1)
Separación. Datos de entrenamiento y Datos de validación
line 2
- x: Variables de entrada
- y: Variable objetivo, la cuál se trata de predecir
- test_size=0.2 Especifica que el 20% del conjunto de datos se utilizará como conjunto de validación, y el 80% restante se utilizará como conjunto de entrenamiento.
- random_state=123 Es una semilla para el generador de números aleatorios. Esto asegura que la división de datos sea reproducible, lo que significa que si ejecutas el código nuevamente con la misma semilla, obtendrás la misma división de datos.
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=123)
print("Tamaño del dataset de entrenamiento: ", x_train.shape)
print("Tamaño del dataset de validación: ", x_test.shape)
Tamaño del dataset de entrenamiento: (800, 2) Tamaño del dataset de validación: (200, 2)
Desempeño del modelo en el conjunto de entrenamiento
- Entrenamiento del modelo
- Ajustar el modelo a los datos
- Desempeño en el entrenamiento
from sklearn.metrics import accuracy_score
modelo = LogisticRegression()
modelo.fit(x_train, y_train)
y_train_pred = modelo.predict(x_train)
print('El modelo, en el conjunto de entrenamiento predice correctamente: %.3f' %accuracy_score(y_train, y_train_pred) )
El modelo, en el conjunto de entrenamiento predice correctamente: 0.975
5). EVALUACIÓN DEL MODELO DE REGRESIÓN LOGÍSTICA
Evaluación del modelo en el conjunto de validación o prueba
y_pred = modelo.predict(x_test)
print('El modelo, en el conjunto de validación predice correctamente: %.3f' %accuracy_score(y_test, y_pred) )
El modelo, en el conjunto de validación predice correctamente: 0.980
Matriz de confución
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
mc = confusion_matrix(y_test, y_pred)
mc_display = ConfusionMatrixDisplay(confusion_matrix=mc, display_labels=['No aprueba', 'Aprueba'])
mc_display.plot()
plt.show()
Lectura de la Matriz de confución
Asiertos y fallas en la predicción del modelo
mc =
\begin{bmatrix}
\text{tn: Aciertos en la predicción al decir 'No aprueba'} & \text{fp: Fallas en la predicción al decir 'Aprueba'} \\
\text{fn: Fallas en la predicción al decir 'No prueba'} & \text{tp: Aciertos en la predicción decir 'Aprueba'}
\end{bmatrix}
=
\begin{bmatrix}
103 & 4 \\
0 & 93
\end{bmatrix}
Curva ROC y área bajo la curva (AUC)
from sklearn.metrics import roc_curve, roc_auc_score
# Calcula las probabilidades de clase positiva
y_prob = modelo.predict_proba(x_test)[:, 1]
# Calcula la curva ROC
fpr, tpr, thresholds = roc_curve(y_test, y_prob)
# Grafica la curva ROC
plt.figure(figsize=(4, 3.5))
plt.plot(fpr, tpr, linewidth=2)
plt.plot([0, 1], [0, 1], 'k--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('Tasa de Falsos Positivos (1 - Especificidad)')
plt.ylabel('Tasa de Verdaderos Positivos (Sensibilidad)')
plt.title('Curva ROC')
plt.grid(True)
plt.show()
# Calcula el AUC (Área bajo la curva ROC)
auc = roc_auc_score(y_test, y_prob)
display(f'AUC: {auc}')
'AUC: 0.9906542056074766'
Interpretación AUC:
- AUC = 0.5 indica que el modelo no tiene capacidad discriminativa y es tan bueno como un clasificador aleatorio.
- AUC = 1 indica que el modelo tiene una capacidad de discriminación perfecta, es decir, puede distinguir perfectamente entre las clases positivas y negativas.
0.9906 Indica que el modelo tiene alta sensibilidad (TPR) lo que se interpreta en que es muy bueno para identificar correctamente a los estudiantes que aprobarán
Metricas
tn, fp, fn, tp = mc.ravel()
precision = tp/ (tp + fp)
recall = tp / (tp + fn)
especificidad = tn / (fp + tn)
f1_score = 2*(precision*recall)/(precision+recall)
print(f'Precision: {precision}')
print(f'Recall: {recall}')
print(f'Especificidad: {especificidad}')
print(f'F1: {f1_score}')
Precision: 0.9587628865979382 Recall: 1.0 Especificidad: 0.9626168224299065 F1: 0.9789473684210526
Lectura de las metricas
Precision: 0.9587 Alrededor del 95.87% de las predicciones positivas son correctas.
Recall: 1.0 Un valor de recall de 1.0 significa que el modelo no deja ningún caso positivo sin clasificar como positivo.
Especificidad: 0.9626 Un valor de especificidad de 0.9626 significa que el modelo es muy bueno para clasificar casos negativos.
F1: 0.9789 Un F1 Score de 0.9789 indica un buen equilibrio entre la precisión y el recall. El F1 Score es una métrica que combina la precisión y el recall en un solo valor. Está diseñado para encontrar un equilibrio entre la precisión y la exhaustividad del modelo.
En general, estos resultados indican que el modelo tiene un alto nivel de precisión y recall, lo que sugiere que es efectivo en la clasificación de ambas clases (positiva y negativa). También muestra un alto nivel de especificidad, lo que significa que es bueno en la clasificación de la clase negativa. En general, estos valores son indicativos de un modelo de clasificación sólido.
6). APLICACIÓN DEL MODELO
Ingreso de datos individual
Un estudiante con 10 horas diarias de estudio y 45 horas al mes de estudio con tutoria. ¿Aprueba?
self_study = 10
tution_monthly = 45
prediction = modelo.predict_proba(np.array([self_study, tution_monthly]).reshape(1, -1))
print("Probabilidades. 0: No_aprueba 1: Aprueba")
print("Orden en el modelo", modelo.classes_)
print("Probabilidades del estudiante", prediction)
Probabilidades. 0: No_aprueba 1: Aprueba Orden en el modelo [0 1] Probabilidades del estudiante [[0.99649842 0.00350158]]
Lectura del resultado: Con una probabilidad del 99.64% el estudiante no aprueba
Ingreso de datos en lista
Se presenta un archivo csv e cual contiene las horas que planean estudiar varios estudiantes. El archivo cuenta con dos columnas Horas estudio diario
y Horas estudio por tutoria
se quiere predecir si el estudiante aprobará o no.
Lectura de datos
df_notas = pd.read_csv('https://raw.githubusercontent.com/JavierBurgos-web/002_Machine_Learning_RLog/main/notas_para_evaluar.csv', delimiter=';')
df_notas.head(3)
Estudio_diario | Tutorias | |
---|---|---|
0 | 8 | 27 |
1 | 1 | 43 |
2 | 9 | 22 |
Preparación de los datos
df_notas.rename(columns={'Estudio_diario': 'self_study', 'Tutorias': 'tution_monthly'}, inplace=True)
df_notas.columns
Index(['self_study', 'tution_monthly'], dtype='object')
Ejecución del modelo
# Crear una lista vacía para las predicciones
predictions = []
# Iterar a través del DataFrame df_notas
for index, row in df_notas.iterrows():
self_study = row['self_study']
tution_monthly = row['tution_monthly']
prediction = modelo.predict_proba(np.array([self_study, tution_monthly]).reshape(1, -1))
# Aplanar la matriz 3D y agregarla a la lista
predictions.append(prediction.flatten())
# Convertir la lista de predicciones en un DataFrame
predictions_df = pd.DataFrame(predictions, columns=['Probabilidad_Clase_0', 'Probabilidad_Clase_1'])
predictions_df.round(3)
Probabilidad_Clase_0 | Probabilidad_Clase_1 | |
---|---|---|
0 | 0.012 | 0.988 |
1 | 1.000 | 0.000 |
2 | 0.000 | 1.000 |
3 | 0.999 | 0.001 |
4 | 1.000 | 0.000 |
5 | 1.000 | 0.000 |
6 | 0.000 | 1.000 |
7 | 1.000 | 0.000 |
8 | 0.999 | 0.001 |
9 | 1.000 | 0.000 |
Lectura de los resultados
- Se observa como el estudiante 0 tiene 98.8% de probabilidad de aprobar
- Se observa como el estudiante 1 tiene 100% de probabilidad de reprobar
Y así sucesivamente.