Este análisis explora la influencia de los hábitos de estudiantes en su rendimiento académico. El dataset cuenta con 1000 registros y más de 15 variables, incluyendo las horas de estudio, uso de redes sociales, cantidad de horas que ven Netflix, actividad física, edad, dieta y salud mental
Los datos fueron tomados de base abiertas de Kaggle: Analisis de Rendimiento Académico basado en Hábitos
#reticulate::py_install(“polars”)
#reticulate::py_install(“os”)
#reticulate::py_install(“pandas”)
#reticulate::py_install(“numpy”)
#reticulate::py_install(“matplotlib”)
#reticulate::py_install(“seaborn”)
#reticulate::py_install(“scikit-learn”)
#reticulate::py_install(“pyarrow”)
library(reticulate) py_config()
Analizando los resultados, vemos que existe una incidencia de los hábitos en las notas obtenidas. Principalmente, de la cantidad de horas es estudio, estado mental, actividad fisica y horas de sueño, así como el uso de redes sociales y Netflix u otras plataformas que afectan en este caso de forma inversa al resultado final. Luego de aplicar estrategias de seleccion de variables, vemos que los resultados no tienen una variación grande, quizás porque la base no cuenta con un número demasiado grande de variables o por la cantidad de registros del Dataset. En los siguientes capítulos se definen los pasos seguidos para poder realizar la carga, limpieza, análisis y modelado a partir de los datos de prueba.
import polars as pl
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LinearRegression
path = "C:/Users/Asus/Downloads/BIG DATA SUDAMERIS 1/student_habits_performance.csv"
columns=['student_id','age','gender','study_hours_per_day','social_media_hours',
'netflix_hours','part_time_job','attendance_percentage','sleep_hours','diet_quality',
'exercise_frequency','parental_education_level','internet_quality','mental_health_rating',
'extracurricular_participation','exam_score']
schemas_overrides = {"student_id":pl.Utf8,
"age":pl.Int32,
"gender":pl.Utf8,
"study_hours_per_day":pl.Float64,
"social_media_hours":pl.Float64,
"netflix_hours":pl.Float64,
"part_time_job":pl.Utf8,
"attendance_percentage":pl.Float64,
"sleep_hours":pl.Float64,
"diet_quality":pl.Utf8,
"exercise_frequency":pl.Int32,
"parental_education_level":pl.Utf8,
"internet_quality":pl.Utf8,
"mental_health_rating":pl.Int32,
"extracurricular_participation":pl.Utf8,
"exam_score":pl.Float64
}
dfs = pl.read_csv(path,
columns=columns,
dtypes=schemas_overrides,
ignore_errors=True,
null_values=["1154522A"],
encoding="latin1",
)
df=dfs.to_pandas()
print(df.head())
## student_id age ... extracurricular_participation exam_score
## 0 S1000 23 ... Yes 56.2
## 1 S1001 20 ... No 100.0
## 2 S1002 21 ... No 34.3
## 3 S1003 23 ... Yes 26.8
## 4 S1004 19 ... No 66.4
##
## [5 rows x 16 columns]
#cambio de nombre de columnas y creacion de nuevas columnas
df_nombres =['id','Edad','Genero','HorasEstudioDia','HorasRedes','HorasNetflix',
'TrabajoMedioTiempo','PorcentajeAsistencia',
'HorasSuenho','CalidadDieta','FrequenciaEjercicio',
'NivelEducativoPadres','CalidadInternet','RatingEstadoMental',
'ActividadExtra','Notas']
df.columns=df_nombres
# Definir los rangos de edad
bins = [0, 18, 21, 24] # Los límites de cada rango
labels = ['0-18', '19-21', '22-24'] # Etiquetas de cada rango
# Definir los rangos de edad
bins_s = [0,4,7,24 ] # Los límites de cada rango
labels_s = ['Bajo', 'Moderado', 'Alto'] # Etiquetas de cada rango
# Crear una nueva columna 'rango_edad' con los rangos definidos
df['RangoEdad'] = pd.cut(df['Edad'], bins=bins, labels=labels, right=False)
df['RangoSuenho'] = pd.cut(df['HorasSuenho'], bins=bins_s, labels=labels_s, right=False)
# Definir los rangos de las calificaciones
topes_nota = [0, 50, 70, 85, 100] # Los límites de cada rango
Etiquetas = ['Bajo', 'Promedio', 'Bueno', 'Excelente'] # Etiquetas de cada rango
# Crear una nueva columna 'rango_calificacion' con los rangos definidos
df['RangoNotas'] = pd.cut(df['Notas'], bins=topes_nota, labels=Etiquetas, right=True)
df
## id Edad Genero ... RangoEdad RangoSuenho RangoNotas
## 0 S1000 23 Female ... 22-24 Alto Promedio
## 1 S1001 20 Female ... 19-21 Moderado Excelente
## 2 S1002 21 Male ... 22-24 Alto Bajo
## 3 S1003 23 Female ... 22-24 Alto Bajo
## 4 S1004 19 Female ... 19-21 Moderado Promedio
## .. ... ... ... ... ... ... ...
## 995 S1995 21 Female ... 22-24 Alto Bueno
## 996 S1996 17 Female ... 0-18 Moderado Promedio
## 997 S1997 20 Male ... 19-21 Moderado Promedio
## 998 S1998 24 Male ... NaN Alto Promedio
## 999 S1999 19 Female ... 19-21 Alto Bueno
##
## [1000 rows x 19 columns]
#ANALISIS DATAFRAME
# Obtener el número total de filas del DataFrame
num_filas = df.shape[0]
# Imprimir el número de filas
print(f"Total de filas: {num_filas}")
#imprimir columnas(f)
## Total de filas: 1000
print(f"Columnas: {df.columns}")
## Columnas: Index(['id', 'Edad', 'Genero', 'HorasEstudioDia', 'HorasRedes', 'HorasNetflix',
## 'TrabajoMedioTiempo', 'PorcentajeAsistencia', 'HorasSuenho',
## 'CalidadDieta', 'FrequenciaEjercicio', 'NivelEducativoPadres',
## 'CalidadInternet', 'RatingEstadoMental', 'ActividadExtra', 'Notas',
## 'RangoEdad', 'RangoSuenho', 'RangoNotas'],
## dtype='object')
#Separamos la variables independientes de la variable dependiente
x=df.drop(columns=['Notas','id'])
y= df['Notas']
#tratamiento especial de columnas categoricas
categoricas = [
'Genero', 'TrabajoMedioTiempo', 'CalidadDieta',
'NivelEducativoPadres', 'CalidadInternet', 'ActividadExtra','RangoEdad', 'RangoSuenho'
]
numericas = ['Edad', 'HorasEstudioDia', 'HorasRedes', 'HorasNetflix',
'PorcentajeAsistencia', 'HorasSuenho', 'FrequenciaEjercicio',
'RatingEstadoMental']
x_codificadas = pd.get_dummies(x, columns=categoricas, drop_first=True)
# Normalizar numéricas
normalizar = StandardScaler()
x_codificadas[numericas] = normalizar.fit_transform(x_codificadas[numericas])
x_codificadas = x_codificadas.drop(columns=['RangoNotas'])
#reservamos 20% de los datos para prueba
X_train, X_test, y_train, y_test = train_test_split(x_codificadas, y, test_size=0.2,
random_state=3)
x_codificadas.columns
## Index(['Edad', 'HorasEstudioDia', 'HorasRedes', 'HorasNetflix',
## 'PorcentajeAsistencia', 'HorasSuenho', 'FrequenciaEjercicio',
## 'RatingEstadoMental', 'Genero_Male', 'Genero_Other',
## 'TrabajoMedioTiempo_Yes', 'CalidadDieta_Good', 'CalidadDieta_Poor',
## 'NivelEducativoPadres_High School', 'NivelEducativoPadres_Master',
## 'NivelEducativoPadres_None', 'CalidadInternet_Good',
## 'CalidadInternet_Poor', 'ActividadExtra_Yes', 'RangoEdad_19-21',
## 'RangoEdad_22-24', 'RangoSuenho_Moderado', 'RangoSuenho_Alto'],
## dtype='object')
x_codificadas.head()
## Edad HorasEstudioDia ... RangoSuenho_Moderado RangoSuenho_Alto
## 0 1.084551 -2.418068 ... 0 1
## 1 -0.215870 2.281707 ... 1 0
## 2 0.217604 -1.464491 ... 0 1
## 3 1.084551 -1.736942 ... 0 1
## 4 -0.649344 0.987566 ... 1 0
##
## [5 rows x 23 columns]
#Graficamos el dataset normalizado y el dataset original para poder ver la diferencia.
#Tomamos como ejemplo 2 variables categoricas:CalidadDieta y genero y dos variables
#numericas: HorasEstudioDia, HorasSuenho
var1 = 'HorasEstudioDia'
var2 = 'HorasSuenho'
var3 = 'Genero'
var4 = 'CalidadDieta'
#para categoricas se elige alguna de las columnas creadas: dummies crea una columna
#por cada elemento de la variable categórica
var5='Genero_Male'
var6='CalidadDieta_Moderado'
#Creamos los gráficos
plt.figure(figsize=(12, 10))
# var1 1: Horas de estudio
plt.subplot(2, 2, 1)
sns.kdeplot(df[var1], label='Original', fill=True, color='yellow')
sns.kdeplot(x_codificadas[var1], label='Normalizada', fill=True, color='red')
plt.title(f'Distribución: {var1}')
plt.legend()
# var2 : Horas de suenho
plt.subplot(2, 2, 2)
sns.kdeplot(df[var2], label='Original', fill=True, color='yellow')
sns.kdeplot(x_codificadas[var2], label='Normalizada', fill=True, color='red')
plt.title(f'Distribución: {var2}')
plt.legend()
# var3 : Genero
plt.subplot(2, 2, 3)
sns.countplot(x=df[var3], color='skyblue', label='Original')
plt.title(f'Categoría original: {var3}')
plt.legend(['Original'])
# var3 Genero Masculino (normalizada)
plt.subplot(2, 2, 4)
sns.histplot(x_codificadas[var5], bins=3, color='orange', label='Normalizada')
plt.title(f'Categoría Normalizada: {var5}')
plt.legend()
plt.tight_layout()
plt.show()
# 7. Análisis PCA
#correlacion entre variables
datos = x_codificadas
datos['Notas'] = df['Notas']
variables_numericas = datos.select_dtypes(include=['float64', 'int64','boolean']).columns
datos[variables_numericas].corr()['Notas'].sort_values(ascending=False)
## Notas 1.000000
## HorasEstudioDia 0.825419
## RatingEstadoMental 0.321523
## FrequenciaEjercicio 0.160107
## HorasSuenho 0.121683
## PorcentajeAsistencia 0.089836
## Edad -0.008907
## HorasRedes -0.166733
## HorasNetflix -0.171779
## Name: Notas, dtype: float64
El analisis de correlacion indica que la variable que más afecta es la cantidad de estudio, lo que suena bastante lógico y esperable, al correr PCA, veremos que variables no son necesarias.
# PCA para reducir dimensionalidad
pca = PCA(n_components=0.95) # Mantener 95% de certeza
X_train_pca = pca.fit_transform(X_train)
X_test_pca = pca.transform(X_test)
print(f"componentes PCA retenidos: {pca.n_components_}")
## componentes PCA retenidos: 16
# %Varianza
print("Porcentaje de varianza por cada componente:")
## Porcentaje de varianza por cada componente:
print(pca.explained_variance_ratio_)
# Ver el total de varianza explicada por los primeros n componentes
## [0.12020004 0.10804882 0.10460308 0.0944171 0.09132323 0.08897602
## 0.08728345 0.08518392 0.02704465 0.02561843 0.02414548 0.02272975
## 0.02104595 0.01992384 0.01591062 0.01451805]
print(f"Varianza total explicada por {pca.n_components_} componentes: {sum(pca.explained_variance_ratio_)}")
# (contribución de cada variable original a cada componente)
## Varianza total explicada por 16 componentes: 0.9509724248354532
Contrib = pca.components_
contrib_df = pd.DataFrame(Contrib, columns=X_train.columns,
index=[f'Componente {i+1}' for i in range(Contrib.shape[0])])
# Ver las primeras 5 filas de contribucion
print("\Contribucion de cada componente:")
## \Contribucion de cada componente:
print(contrib_df)
## Edad ... RangoSuenho_Alto
## Componente 1 0.008659 ... 0.365727
## Componente 2 -0.872469 ... -0.008058
## Componente 3 0.072728 ... -0.004286
## Componente 4 -0.012289 ... -0.034014
## Componente 5 0.143870 ... -0.041686
## Componente 6 -0.177793 ... 0.023618
## Componente 7 0.208449 ... -0.018357
## Componente 8 -0.007512 ... 0.048199
## Componente 9 -0.002963 ... -0.047505
## Componente 10 0.091126 ... -0.015436
## Componente 11 0.000124 ... 0.025726
## Componente 12 -0.084212 ... 0.036039
## Componente 13 -0.344650 ... 0.008301
## Componente 14 -0.002706 ... 0.034196
## Componente 15 -0.033082 ... -0.196302
## Componente 16 0.004312 ... 0.463538
##
## [16 rows x 23 columns]
# Regresion Linear sobre PCA:
model = LinearRegression()
model.fit(X_train_pca, y_train)
# Ver el intercepto
## LinearRegression()
print(f"Intercepto (b0): {model.intercept_:.3f}")
# Ver los coeficientes para cada componente PCA
## Intercepto (b0): 69.741
for i, coef in enumerate(model.coef_):
print(f"Coeficiente del Componente {i+1} (b{i+1}): {coef:.3f}")
#EN TERMINOS DE VARIABLES Y NO COMPONENTES:
## Coeficiente del Componente 1 (b1): 1.211
## Coeficiente del Componente 2 (b2): 0.342
## Coeficiente del Componente 3 (b3): 3.658
## Coeficiente del Componente 4 (b4): 5.553
## Coeficiente del Componente 5 (b5): -4.645
## Coeficiente del Componente 6 (b6): -7.902
## Coeficiente del Componente 7 (b7): -2.315
## Coeficiente del Componente 8 (b8): 11.281
## Coeficiente del Componente 9 (b9): 0.822
## Coeficiente del Componente 10 (b10): -0.390
## Coeficiente del Componente 11 (b11): -0.597
## Coeficiente del Componente 12 (b12): 0.454
## Coeficiente del Componente 13 (b13): -0.342
## Coeficiente del Componente 14 (b14): 0.261
## Coeficiente del Componente 15 (b15): 0.841
## Coeficiente del Componente 16 (b16): -0.545
cargas = pca.components_
# Paso 2: Coeficientes del modelo en el espacio PCA
coefs_modelo = model.coef_
# Paso 3: Multiplicar coeficientes del modelo por las cargas del PCA
efecto_original = np.dot(coefs_modelo, cargas)
# Mostrar en DataFrame
df_efecto = pd.DataFrame({
'Variable': X_train.columns,
'Peso_aproximado': efecto_original
}).sort_values(by='Peso_aproximado', key=abs, ascending=False)
print(df_efecto)
## Variable Peso_aproximado
## 1 HorasEstudioDia 14.186778
## 7 RatingEstadoMental 5.584123
## 2 HorasRedes -3.017093
## 6 FrequenciaEjercicio 2.831163
## 3 HorasNetflix -2.571149
## 5 HorasSuenho 2.370670
## 4 PorcentajeAsistencia 1.399506
## 11 CalidadDieta_Good -0.739657
## 12 CalidadDieta_Poor 0.434721
## 22 RangoSuenho_Alto 0.385454
## 16 CalidadInternet_Good -0.346018
## 20 RangoEdad_22-24 -0.276241
## 17 CalidadInternet_Poor -0.251367
## 14 NivelEducativoPadres_Master -0.211696
## 8 Genero_Male 0.179967
## 15 NivelEducativoPadres_None 0.154855
## 19 RangoEdad_19-21 -0.109985
## 0 Edad 0.089688
## 21 RangoSuenho_Moderado -0.070677
## 9 Genero_Other 0.067837
## 18 ActividadExtra_Yes 0.055044
## 13 NivelEducativoPadres_High School -0.054609
## 10 TrabajoMedioTiempo_Yes 0.054579
plt.figure(figsize=(10,6))
sns.barplot(x=df_efecto['Peso_aproximado'], y=df_efecto['Variable'], orient='h')
plt.title('Coeficientes del Modelo de Regresión Lineal')
plt.xlabel('Peso (coeficiente)')
plt.ylabel('Variable')
plt.grid(True)
plt.tight_layout()
plt.show()
score = model.score(X_test_pca, y_test)
print(f"R^2 en test: {score:.3f}")
## R^2 en test: 0.896
Se verifica, a partir del calculo de los interceptos, y la descomposicion de los componentes, cuales son las variables con mayor incidencia en la variable Objetivo, horas de suenho y estado mental serían las que se asocian de forma directa con la calificacion, mientras que la cantidad de horas en redes sociales o en Netflix, se asocia de forma inversa a la calificación final.
# predicciones sobre el conjunto de prueba
y_pred = model.predict(X_test_pca)
#DataFrame para comparar
df_comparacion = pd.DataFrame({
'Real': y_test.values,
'Predicho': y_pred
})
# 3. Gráfico de dispersión
plt.figure(figsize=(8,6))
sns.scatterplot(data=df_comparacion, x='Real', y='Predicho', alpha=0.6)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--',
label='Perfecta Predicción')
plt.title('Comparación: Notas reales vs Predichas')
plt.xlabel('Notas reales')
plt.ylabel('Notas predichas')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# Regresion Linear sobre valores:
model_sin_pca = LinearRegression()
model_sin_pca.fit(X_train, y_train)
# Ver el intercepto
## LinearRegression()
print(f"Intercepto (b0): {model_sin_pca.intercept_:.3f}")
# Ver los coeficientes para cada componente PCA
## Intercepto (b0): 69.764
for i, coef in enumerate(model_sin_pca.coef_):
print(f"Coeficiente de las variable {X_train.columns[i]} (b{i+1}): {coef:.3f}")
# Crear un DataFrame con los coeficientes y las variables
## Coeficiente de las variable Edad (b1): 0.091
## Coeficiente de las variable HorasEstudioDia (b2): 14.188
## Coeficiente de las variable HorasRedes (b3): -3.024
## Coeficiente de las variable HorasNetflix (b4): -2.584
## Coeficiente de las variable PorcentajeAsistencia (b5): 1.436
## Coeficiente de las variable HorasSuenho (b6): 2.329
## Coeficiente de las variable FrequenciaEjercicio (b7): 2.855
## Coeficiente de las variable RatingEstadoMental (b8): 5.620
## Coeficiente de las variable Genero_Male (b9): 0.209
## Coeficiente de las variable Genero_Other (b10): 0.425
## Coeficiente de las variable TrabajoMedioTiempo_Yes (b11): 0.143
## Coeficiente de las variable CalidadDieta_Good (b12): -1.179
## Coeficiente de las variable CalidadDieta_Poor (b13): -0.289
## Coeficiente de las variable NivelEducativoPadres_High School (b14): -0.123
## Coeficiente de las variable NivelEducativoPadres_Master (b15): 0.123
## Coeficiente de las variable NivelEducativoPadres_None (b16): -0.699
## Coeficiente de las variable CalidadInternet_Good (b17): -0.261
## Coeficiente de las variable CalidadInternet_Poor (b18): 0.059
## Coeficiente de las variable ActividadExtra_Yes (b19): 0.233
## Coeficiente de las variable RangoEdad_19-21 (b20): 0.261
## Coeficiente de las variable RangoEdad_22-24 (b21): 0.075
## Coeficiente de las variable RangoSuenho_Moderado (b22): 0.145
## Coeficiente de las variable RangoSuenho_Alto (b23): 0.677
coef_df = pd.DataFrame({
'Variable': X_train.columns,
'Peso (coeficiente)': model_sin_pca.coef_
}).sort_values(by='Peso (coeficiente)', key=abs, ascending=False)
# Gráfico de los coeficientes
# Mostrar las variables más importantes
print(coef_df)
## Variable Peso (coeficiente)
## 1 HorasEstudioDia 14.188363
## 7 RatingEstadoMental 5.619587
## 2 HorasRedes -3.023690
## 6 FrequenciaEjercicio 2.854931
## 3 HorasNetflix -2.584230
## 5 HorasSuenho 2.328735
## 4 PorcentajeAsistencia 1.435855
## 11 CalidadDieta_Good -1.178996
## 15 NivelEducativoPadres_None -0.699314
## 22 RangoSuenho_Alto 0.677236
## 9 Genero_Other 0.425014
## 12 CalidadDieta_Poor -0.289265
## 16 CalidadInternet_Good -0.261004
## 19 RangoEdad_19-21 0.260813
## 18 ActividadExtra_Yes 0.233012
## 8 Genero_Male 0.209348
## 21 RangoSuenho_Moderado 0.145118
## 10 TrabajoMedioTiempo_Yes 0.143227
## 13 NivelEducativoPadres_High School -0.123233
## 14 NivelEducativoPadres_Master 0.122895
## 0 Edad 0.091251
## 20 RangoEdad_22-24 0.074681
## 17 CalidadInternet_Poor 0.059092
plt.figure(figsize=(10,6))
sns.barplot(x=coef_df['Peso (coeficiente)'], y=coef_df['Variable'], orient='h')
plt.title('Coeficientes del Modelo de Regresión Lineal')
plt.xlabel('Peso (coeficiente)')
plt.ylabel('Variable')
plt.grid(True)
plt.tight_layout()
plt.show()
score = model_sin_pca.score(X_test, y_test)
print(f"R^2 en test: {score:.3f}")
## R^2 en test: 0.894
Verificando los resultados, vemos que el uso de pca, en este caso no alteró el resultado final obtenido usando las variables originales.
# predicciones sobre el conjunto de prueba
y_pred = model_sin_pca.predict(X_test)
#DataFrame para comparar
df_comparacion = pd.DataFrame({
'Real': y_test.values,
'Predicho': y_pred
})
# 3. Gráfico de dispersión
plt.figure(figsize=(8,6))
sns.scatterplot(data=df_comparacion, x='Real', y='Predicho', alpha=0.6)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--',
label='Perfecta Predicción')
plt.title('Comparación: Notas reales vs Predichas')
plt.xlabel('Notas reales')
plt.ylabel('Notas predichas')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()