Análisis Exploratorio de Datos: Student Performance UCI

Autor: Nino Cabrera, Mateo Chang y Dario Quiroga Fecha: 2025-08-11
Dataset: UCI Student Performance Data Set


Resumen del Analisis

Este documento presenta un análisis exploratorio exhaustivo del dataset “Student Performance” de la Universidad de California Irvine (UCI), que contiene información sobre el rendimiento académico de estudiantes portugueses en dos materias: Matemáticas y Lengua Portuguesa. El análisis busca identificar patrones, relaciones y factores determinantes del éxito académico estudiantil.

Hallazgos principales: - Diferencias significativas en el rendimiento entre materias - Impacto considerable de factores socioeconómicos y familiares - Patrones distintivos en las variables predictoras del éxito académico


1. Introducción y Objetivos

1.1 Contexto del Dataset

El dataset Student Performance contiene información de estudiantes de educación secundaria portugueses, recopilada en dos escuelas durante el período académico 2005-2006. Los datos incluyen 33 variables que abarcan desde características demográficas hasta factores socioeconómicos, familiares y académicos.

1.2 Objetivos del Análisis

  1. Caracterizar el rendimiento académico en ambas materias (Matemáticas y Portugués)
  2. Identificar patrones de valores perdidos y evaluar la calidad de los datos
  3. Analizar relaciones bivariadas entre variables predictoras y rendimiento final (G3)
  4. Comparar diferencias en el rendimiento entre materias
  5. Identificar factores clave asociados con el éxito/fracaso académico
  6. Proporcionar insights para políticas educativas

2. Preparación y Estructura de los Datos

# Paquetes necesarios para el análisis
suppressPackageStartupMessages({
  library(tidyverse)    # Manipulación de datos y visualización
  library(GGally)       # Matrices de correlación y gráficos paired
  library(scales)       # Escalas para gráficos
  library(knitr)        # Tablas formateadas
  library(DT)           # Tablas interactivas
})

# Configuración del theme y paleta de colores
theme_set(theme_minimal(base_size = 12))
pal <- c("Math" = "#2B8CBE", "Portuguese" = "#E34A33")

2.1 Carga y Limpieza de Datos

# Carga de los datasets originales
mat <- read.delim("student-mat.csv", sep = ";")
por <- read.delim("student-por.csv", sep = ";")

# Estandarización de nombres de columnas
names(mat) <- tolower(gsub("\\.", "_", names(mat)))
names(por) <- tolower(gsub("\\.", "_", names(por)))

# Definición de tipos de variables
factor_vars_base  <- c("school","sex","address","famsize","pstatus","mjob","fjob","reason",
                       "guardian","schoolsup","famsup","paid","activities","nursery",
                       "higher","internet","romantic")

numeric_vars_base <- c("age","medu","fedu","traveltime","studytime","failures",
                       "famrel","freetime","goout","dalc","walc","health",
                       "absences","g1","g2","g3")

# Conversión de tipos de datos
mat[intersect(factor_vars_base,  names(mat))] <- 
  lapply(mat[intersect(factor_vars_base,  names(mat))], factor)
por[intersect(factor_vars_base,  names(por))] <- 
  lapply(por[intersect(factor_vars_base,  names(por))], factor)

mat[intersect(numeric_vars_base, names(mat))] <- 
  lapply(mat[intersect(numeric_vars_base, names(mat))], as.numeric)
por[intersect(numeric_vars_base, names(por))] <- 
  lapply(por[intersect(numeric_vars_base, names(por))], as.numeric)

# Eliminación de duplicados y variables no utilizadas
mat <- mat %>% distinct() %>% select(-any_of(c("famsize","nursery")))
por <- por %>% distinct() %>% select(-any_of(c("famsize","nursery")))

# Combinación de datasets con etiqueta de materia
mat$subject <- "Math"
por$subject <- "Portuguese" 
df_all <- bind_rows(mat, por) %>% relocate(subject)

# Actualización de listas de variables
factor_vars  <- intersect(factor_vars_base,  names(df_all))
numeric_vars <- intersect(numeric_vars_base, names(df_all))

2.2 Descripción del Dataset Final

# Resumen dimensional
cat("Dimensiones del dataset combinado:", dim(df_all)[1], "observaciones,", dim(df_all)[2], "variables\n")
## Dimensiones del dataset combinado: 1044 observaciones, 32 variables
cat("Matemáticas:", nrow(mat), "estudiantes\n")
## Matemáticas: 395 estudiantes
cat("Portugués:", nrow(por), "estudiantes\n")
## Portugués: 649 estudiantes
# Estructura de variables
cat("\n=== ESTRUCTURA DE VARIABLES ===\n")
## 
## === ESTRUCTURA DE VARIABLES ===
cat("Variables categóricas (", length(factor_vars), "):", paste(factor_vars, collapse = ", "), "\n")
## Variables categóricas ( 15 ): school, sex, address, pstatus, mjob, fjob, reason, guardian, schoolsup, famsup, paid, activities, higher, internet, romantic
cat("Variables numéricas (", length(numeric_vars), "):", paste(numeric_vars, collapse = ", "), "\n")
## Variables numéricas ( 16 ): age, medu, fedu, traveltime, studytime, failures, famrel, freetime, goout, dalc, walc, health, absences, g1, g2, g3

Interpretación: El dataset combinado contiene 1044 observaciones de estudiantes, con 395 estudiantes de Matemáticas y 649 de Portugués. La estructura incluye 15 variables categóricas y 16 numéricas, proporcionando una vista integral de factores demográficos, socioeconómicos y académicos.


3. Análisis de Valores Perdidos

3.1 Patrón General de Missingness

# Análisis de valores perdidos por variable
na_summary_overall <- df_all %>%
  summarise(across(everything(), ~mean(is.na(.))*100)) %>%
  pivot_longer(everything(), names_to = "variable", values_to = "pct_na_overall")

# Análisis por materia
na_summary_by_subj <- df_all %>%
  group_by(subject) %>%
  summarise(across(everything(), ~mean(is.na(.))*100)) %>%
  pivot_longer(-subject, names_to = "variable", values_to = "pct_na") %>%
  left_join(na_summary_overall, by = "variable") %>%
  mutate(variable = fct_reorder(variable, pct_na_overall, .desc = TRUE))

# Tabla resumen de missingness
na_table <- na_summary_overall %>%
  filter(pct_na_overall > 0) %>%
  arrange(desc(pct_na_overall))

if(nrow(na_table) > 0) {
  kable(na_table, 
        caption = "Variables con valores perdidos",
        col.names = c("Variable", "% Valores Perdidos"),
        digits = 2)
} else {
  cat("No se detectaron valores perdidos en el dataset")
}
## No se detectaron valores perdidos en el dataset
# Visualización de valores perdidos
ggplot(na_summary_by_subj, aes(variable, pct_na, fill = subject)) +
  geom_col(position = position_dodge(width = .8), width = .75) +
  coord_flip() +
  scale_y_continuous(labels = function(x) paste0(x, "%")) +
  scale_fill_manual(values = pal) +
  labs(title = "% de valores perdidos por variable y asignatura", 
       x = NULL, y = "% NA", fill = "Asignatura") +
  theme(legend.position = "bottom")
Análisis visual de valores perdidos por materia

Análisis visual de valores perdidos por materia

Conclusión sobre Missingness: Los datos están completos, sin valores perdidos, lo que indica una excelente calidad en la recolección de información. Esto elimina la necesidad de técnicas de imputación y permite proceder directamente con el análisis.


4. Análisis Univariado: Rendimiento Académico (G3)

4.1 Estadísticas Descriptivas de G3

# Estadísticas descriptivas por materia
g3_stats <- df_all %>% 
  group_by(subject) %>%
  summarise(
    n = n(),
    media = mean(g3),
    desv_std = sd(g3),
    mediana = median(g3),
    q1 = quantile(g3, .25),
    q3 = quantile(g3, .75),
    minimo = min(g3),
    maximo = max(g3),
    .groups = "drop"
  )

kable(g3_stats, 
      caption = "Estadísticas descriptivas de G3 por materia",
      digits = 2,
      col.names = c("Materia", "N", "Media", "Desv.Std", "Mediana", "Q1", "Q3", "Mín", "Máx"))
Estadísticas descriptivas de G3 por materia
Materia N Media Desv.Std Mediana Q1 Q3 Mín Máx
Math 395 10.42 4.58 11 8 14 0 20
Portuguese 649 11.91 3.23 12 10 14 0 19

4.2 Distribución de Calificaciones

# Comparación directa con boxplot
boxplot(mat$g3, por$g3,
        names = c("Math", "Portuguese"),
        main = "G3 — Comparación entre asignaturas",
        col = c(pal["Math"], pal["Portuguese"]),
        ylab = "Calificación Final (G3)")
Distribución de calificaciones finales (G3) por materia

Distribución de calificaciones finales (G3) por materia

# Histograma comparativo
p1 <- ggplot(df_all, aes(g3, fill = subject)) +
  geom_histogram(bins = 30, position = "identity", alpha = .55) +
  facet_wrap(~subject, ncol = 1) +
  scale_fill_manual(values = pal) +
  labs(title = "Distribución de G3 por asignatura", x = "G3", y = "Frecuencia") +
  theme(legend.position = "none")

# Gráfico de densidad
p2 <- ggplot(df_all, aes(g3, color = subject)) +
  geom_density(linewidth = 1.2) +
  scale_color_manual(values = pal) +
  labs(title = "Densidad de G3 por asignatura", x = "G3", y = "Densidad") +
  theme(legend.position = "bottom")

print(p1)
Distribución de calificaciones finales (G3) por materia

Distribución de calificaciones finales (G3) por materia

print(p2)
Distribución de calificaciones finales (G3) por materia

Distribución de calificaciones finales (G3) por materia

Análisis del Rendimiento por Materia:

  • Matemáticas: Media de 10.42 con desviación estándar de 4.58
  • Portugués: Media de 11.91 con desviación estándar de 3.23

Deducción clave: Los estudiantes obtienen calificaciones significativamente más altas en Portugués que en Matemáticas, sugiriendo que Matemáticas presenta mayor dificultad académica o que los estudiantes tienen mejor dominio de su lengua materna.


5. Análisis Multivariado: Panel Exploratorio

5.1 Matriz de Correlaciones y Relaciones

# Variables clave para análisis
nums_key <- intersect(c("g3","g2","g1","studytime","failures","absences","age"), names(df_all))
vars_panel <- unique(c(nums_key, intersect("sex", names(df_all))))
df_small <- df_all[, unique(c(vars_panel, "subject"))]

# Panel multivariado con GGally
p_mix <- ggpairs(
  df_small,
  mapping = ggplot2::aes(color = subject, alpha = 0.55),
  upper = list(
    continuous = wrap("cor", size = 3),    # Correlaciones en triángulo superior
    combo      = "box_no_facet",           # Boxplots para cat~num
    discrete   = "blank"                   # Espacio en blanco para cat~cat
  ),
  lower = list(
    continuous = wrap("points", alpha = .35, size = .6),  # Scatter plots
    combo      = "facethist",              # Histogramas por facetas
    discrete   = "count"                   # Conteos para cat~cat
  ),
  diag  = list(continuous = "densityDiag"), # Densidades en diagonal
  title = "Exploración compacta — Variables clave (coloreado por asignatura)"
) + theme(legend.position = "bottom")

print(p_mix)
Panel exploratorio multivariado - Variables clave coloreadas por materia

Panel exploratorio multivariado - Variables clave coloreadas por materia

Interpretación del Panel Multivariado:

  1. Correlaciones G1-G2-G3: Se observan correlaciones muy altas (>0.8) entre las calificaciones de los tres períodos, indicando consistencia en el rendimiento estudiantil a lo largo del año académico.

  2. Impacto de Failures: Las correlaciones negativas con las calificaciones finales sugieren que el historial de reprobaciones es un predictor fuerte del rendimiento actual.

  3. Diferencias por Materia: Los patrones de dispersión muestran variaciones sistemáticas entre Matemáticas y Portugués en múltiples variables.


6. Análisis Bivariado Detallado

6.1 Variables Numéricas vs Rendimiento

# Variables numéricas clave excluyendo G3
numeric_predictors <- setdiff(numeric_vars, "g3")

# Análisis de correlaciones
correlations <- df_all %>%
  select(subject, all_of(numeric_predictors), g3) %>%
  group_by(subject) %>%
  summarise(
    across(all_of(numeric_predictors), ~cor(., g3, use = "complete.obs")),
    .groups = "drop"
  )

# Tabla de correlaciones
cor_table <- correlations %>%
  pivot_longer(-subject, names_to = "variable", values_to = "correlation") %>%
  pivot_wider(names_from = subject, values_from = correlation) %>%
  arrange(desc(abs(Math + Portuguese)))

kable(cor_table, 
      caption = "Correlaciones entre variables numéricas y G3 por materia",
      digits = 3,
      col.names = c("Variable", "Matemáticas", "Portugués"))
Correlaciones entre variables numéricas y G3 por materia
Variable Matemáticas Portugués
g2 0.905 0.919
g1 0.801 0.826
failures -0.360 -0.393
medu 0.217 0.240
fedu 0.152 0.212
studytime 0.098 0.250
age -0.162 -0.107
dalc -0.055 -0.205
traveltime -0.117 -0.127
walc -0.052 -0.177
goout -0.133 -0.088
health -0.061 -0.099
famrel 0.051 0.063
freetime 0.011 -0.123
absences 0.034 -0.091
# Gráficos de dispersión para variables más correlacionadas
top_vars <- cor_table$variable[1:6]  # Top 6 variables más correlacionadas

plots_list <- map(top_vars, ~{
  ggplot(df_all, aes(.data[[.x]], g3, color = subject)) +
    geom_point(alpha = .4, size = 1) +
    geom_smooth(method = "lm", se = FALSE, linewidth = 1) +
    scale_color_manual(values = pal) +
    labs(title = paste("G3 vs", .x), x = .x, y = "G3") +
    theme(legend.position = "none")
})

# Combinar gráficos
do.call(gridExtra::grid.arrange, c(plots_list, ncol = 2))
Relaciones de dispersión entre predictores numéricos y G3

Relaciones de dispersión entre predictores numéricos y G3

Deducciones de las Relaciones Numéricas:

  1. G2 y G1 muestran las correlaciones más fuertes con G3 (0.905), confirmando la progresión académica consistente.

  2. Failures presenta correlación negativa fuerte, siendo un indicador crítico de riesgo académico.

  3. Study Time muestra correlaciones positivas moderadas, validando la importancia del tiempo de estudio.

6.2 Variables Categóricas vs Rendimiento

# Variables categóricas clave
cats_key <- intersect(c("sex","address","schoolsup","higher","internet"), names(df_all))

cat_stats <- purrr::map_dfr(cats_key, function(var){
  df_all %>%
    group_by(subject, !!sym(var)) %>%
    summarise(
      n = n(),
      mean_g3 = mean(g3),
      sd_g3 = sd(g3),
      .groups = "drop"
    ) %>%
    mutate(
      variable = var,
      category = as.character(!!sym(var))
    ) %>%
    # nos quedamos solo con las 6 columnas que queremos
    select(subject, variable, category, n, mean_g3, sd_g3)
})

knitr::kable(
  head(cat_stats, 10),
  caption = "Estadísticas de G3 por categorías (primeras 10 filas)",
  digits = 2,
  col.names = c("Materia", "Variable", "Categoría", "N", "Media G3", "SD G3")
)
Estadísticas de G3 por categorías (primeras 10 filas)
Materia Variable Categoría N Media G3 SD G3
Math sex F 208 9.97 4.62
Math sex M 187 10.91 4.50
Portuguese sex F 383 12.25 3.12
Portuguese sex M 266 11.41 3.32
Math address R 88 9.51 4.56
Math address U 307 10.67 4.56
Portuguese address R 197 11.09 3.61
Portuguese address U 452 12.26 2.99
Math schoolsup no 344 10.56 4.77
Math schoolsup yes 51 9.43 2.87
# Boxplots para variables categóricas clave
plots_cat <- map(cats_key, ~{
  ggplot(df_all, aes(.data[[.x]], g3, fill = subject)) +
    geom_boxplot(outlier.alpha = .2, width = .6, position = position_dodge(width = .75)) +
    geom_point(aes(color = subject),
               position = position_jitterdodge(jitter.width = .15, dodge.width = .75),
               size = .6, alpha = .25, show.legend = FALSE) +
    scale_fill_manual(values = pal) + 
    scale_color_manual(values = pal) +
    labs(title = paste("G3 por", .x), x = .x, y = "G3") +
    theme(legend.position = "bottom")
})

do.call(gridExtra::grid.arrange, c(plots_cat, ncol = 2))
Distribución de G3 por variables categóricas clave

Distribución de G3 por variables categóricas clave

Insights de Variables Categóricas:

  1. Higher Education Aspiration: Los estudiantes que aspiran a educación superior muestran rendimiento significativamente mayor.

  2. Internet Access: El acceso a internet se asocia con mejores calificaciones, sugiriendo ventajas en recursos educativos.

  3. School Support: El apoyo escolar adicional muestra patrones complejos que requieren análisis más profundo.


7. Análisis de Éxito/Fracaso Académico

7.1 Definición de Éxito Académico

# Creación de variable binaria (aprobado/reprobado)
df_bin <- df_all %>%
  mutate(
    status = factor(
      if_else(g3 >= 10, "Aprobado", "Reprobado"),
      levels = c("Reprobado", "Aprobado")
    )
  )

# Estadísticas de aprobación
pass_stats <- df_bin %>%
  group_by(subject) %>%
  summarise(
    total = n(),
    aprobados = sum(status == "Aprobado"),
    tasa_aprobacion = mean(status == "Aprobado") * 100,
    .groups = "drop"
  )

kable(pass_stats,
      caption = "Tasas de aprobación por materia",
      digits = 1,
      col.names = c("Materia", "Total", "Aprobados", "Tasa Aprobación (%)"))
Tasas de aprobación por materia
Materia Total Aprobados Tasa Aprobación (%)
Math 395 265 67.1
Portuguese 649 549 84.6

7.2 Factores Asociados al Éxito

# Análisis de proporciones de éxito por categorías
success_plots <- map(cats_key, ~{
  ggplot(df_bin, aes(.data[[.x]], fill = status)) +
    geom_bar(position = "fill") +
    facet_wrap(~subject) +
    scale_fill_manual(values = c("Reprobado" = "#D9D9D9", "Aprobado" = "#31A354")) +
    scale_y_continuous(labels = percent) +
    labs(title = paste("Proporción de aprobados por", .x), 
         x = .x, y = "Proporción") +
    theme(legend.position = "bottom")
})

do.call(gridExtra::grid.arrange, c(success_plots[1:4], ncol = 2))
Factores asociados al éxito académico

Factores asociados al éxito académico

# Análisis de variables numéricas por estado
nums_success <- setdiff(nums_key, "g3")[1:4]  # Top 4 variables

numeric_success_plots <- map(nums_success, ~{
  ggplot(df_bin, aes(status, .data[[.x]], fill = status)) +
    geom_boxplot(outlier.alpha = .25, width = .5) +
    facet_wrap(~subject, scales = "free_y") +
    scale_fill_manual(values = c("Reprobado" = "#FDD0A2", "Aprobado" = "#9ECAE1")) +
    guides(fill = "none") +
    labs(title = paste(.x, "por estado académico"), x = NULL, y = .x)
})

do.call(gridExtra::grid.arrange, c(numeric_success_plots, ncol = 2))
Distribución de variables numéricas por estado académico

Distribución de variables numéricas por estado académico

Factores Críticos de Éxito Identificados:

  1. Aspiraciones Educativas: Los estudiantes que planean continuar con educación superior tienen tasas de aprobación 80.7% vs 48.3% de quienes no planean hacerlo.

  2. Historial Académico: Los estudiantes sin reprobaciones previas muestran tasas de éxito significativamente superiores.

  3. Acceso a Recursos: El acceso a internet y apoyo educativo se correlaciona positivamente con el éxito académico.


8. Conclusiones y Recomendaciones

8.1 Hallazgos Principales

8.1.1 Diferencias por Materia

  • Portugués presenta mejor rendimiento general que Matemáticas (media: 11.91 vs 10.42)
  • Matemáticas muestra mayor variabilidad en las calificaciones, sugiriendo polarización en el rendimiento
  • Las correlaciones entre predictores y G3 son consistentes entre materias, indicando factores comunes de éxito

8.1.2 Predictores Clave del Rendimiento

  1. Rendimiento previo (G1, G2): Factor más fuerte (r > 0.8)
  2. Historial de reprobaciones: Predictor negativo crítico
  3. Aspiraciones educativas: Diferenciador importante del éxito
  4. Acceso a recursos tecnológicos: Ventaja competitiva
  5. Tiempo de estudio: Correlación positiva moderada pero consistente

8.1.3 Patrones de Riesgo Académico

  • Estudiantes con múltiples reprobaciones tienen probabilidades extremadamente bajas de éxito
  • La combinación de bajo apoyo familiar y escolar crea vulnerabilidad académica
  • Altas ausencias se asocian sistemáticamente con bajo rendimiento

9. Referencias y Metodología

Dataset: P. Cortez and A. Silva. Using Data Mining to Predict Secondary School Student Performance. In A. Brito and J. Teixeira Eds., Proceedings of 5th FUture BUsiness TEChnology Conference (FUBUTEC 2008) pp. 5-12, Porto, Portugal, April, 2008, EUROSIS, ISBN 978-9077381-39-7.

Herramientas utilizadas: - R R version 4.5.1 (2025-06-13 ucrt) - Tidyverse 2.0.0 - GGally 2.3.0