El presente análisis utiliza el dataset “Students Performance in Exams”, una base de datos educativa que recopila información sobre el rendimiento académico de estudiantes de secundaria en tres dimensiones: matemáticas, lectura y escritura.
Además de los puntajes obtenidos, el conjunto de datos incluye variables demográficas como el género, el grupo étnico, el nivel de educación de los padres, el tipo de almuerzo recibido (regular o subsidiado), y si el estudiante completó un curso de preparación para el examen.
Este conjunto de datos es ideal para introducirse en técnicas de análisis de datos con R, ya que permite:
*Explorar la estructura de un dataset real,
*Visualizar relaciones entre variables
*Manipular los datos usando dplyr
*Crear gráficos con ggplot2
*Construir un modelo de regresión lineal simple
A lo largo de este documento se presentarán diferentes ejemplos de código para ilustrar cómo abordar tareas comunes en análisis de datos, lo cual puede servir como guía para el lector. También se explicarán paso a paso los comandos utilizados, con el objetivo de que cualquiera pueda comprender su funcionamiento y adaptarlos a sus propios proyectos.
Pueden acceder a la base copiando y pegando el siguiente link: https://www.kaggle.com/datasets/spscientist/students-performance-in-exams
Este es el momento en el que vamos a planificar nuestro trabajo. Usamos nuestro conocimiento teórico para anticiparnos a las operaciones que tendremos que realizar con los datos.
Lo primero que vamos a hacer es instalar y/o cargar todos los recursos que vayamos a necesitar para nuestro análisis. Solo cargamos lo que vamos a usar.
Durante el proceso de investigación, puede ocurrir que tengamos que recurrir a paquetes que originalmente no habíamos contemplado. Es una buena práctica remitirse a esta sección para declarar de manera ordenada todo lo que hemos utilizado. Eso garantiza que nuestro trabajo y resultado sea reproducible
# Instalamos
# readr nos sirve para leer el archivo csv donde tenemos los datos
if (!require("readr")) install.packages("readr")
## Loading required package: readr
# dplyr nos ayuda a manipular los datos
if (!require("dplyr")) install.packages("dplyr")
## Loading required package: dplyr
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
# ggplot2 nos ayuda a generar gráficos
if (!require("ggplot2")) install.packages("ggplot2")
## Loading required package: ggplot2
# lmtest nos ayuda a comprobar que se cumplen los supuestos de la regresión
if (!require("lmtest")) install.packages("lmtest")
## Loading required package: lmtest
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
# Lo que sigue es cargar las librerías
library(readr)
library(dplyr)
library(ggplot2)
library(lmtest)
En esta sección aprenderemos a cargar un conjunto de datos en R desde un archivo .csv. Utilizaremos varias funciones y opciones para mostrar diferentes formas de importar datos, controlando aspectos como el separador, el tipo de datos, y el manejo de encabezados.
El archivo utilizado se llama StudentsPerformance.csv.
Siempre debemos asegurarnos de que el archivo esté en el directorio de
trabajo. Esto podemos configurarlo en RStudio con
Session > Set Working Directory > mi_proyecto.
Carga rápida usando readr.
Cuando ejecutamos el código, la consola nos proporciona cierta información sobre la base:
1000 filas,
8 columnas,
separador “,”
5 variables nominales
3 variables numéricas (spoiler-alert: estas son las que vamos a usar en la regresión!).
# Cargar el dataset
datos <- read_csv("StudentsPerformance.csv")
## Rows: 1000 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): gender, race/ethnicity, parental level of education, lunch, test pr...
## dbl (3): math score, reading score, writing score
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Esta es la forma tradicional de cargar datos. Conviene especificar
stringsAsFactors = FALSE para evitar que las columnas de
texto se conviertan automáticamente en factores (categorías).
# Opción clásica de R base
datos2 <- read.csv("StudentsPerformance.csv",
header = TRUE, # El archivo tiene encabezados
sep = ",", # Separador de columnas
stringsAsFactors = FALSE) # No convertir texto en factores
En esta opción col_types = cols()nos permite tener
control sobre el tipo de cada columna.
# Cargar datos con tipos de columnas definidos manualmente
datos3 <- read_csv("StudentsPerformance.csv",
col_types = cols(
gender = col_character(),
`race/ethnicity` = col_factor(),
`parental level of education` = col_character(),
lunch = col_factor(),
`test preparation course` = col_factor(),
`math score` = col_double(),
`reading score` = col_double(),
`writing score` = col_double()
))
Por lo general, nos conviene inspeccionar los datos que hemos cargado o fragmentos de la base (si tenemos una cantidad de datos demasiado grande).
El análisis exploratorio es fundamental para realizar un modelo de regresión lineal. Estos datos vamos a tener que reportarlos en un estudio real.
Pero, incluso si ya tenemos cierto conocimiento de nuestra base y simplemente estamos repitiendo un análisis, es buena idea verificar que los datos se hayan cargado correctamente y que no arrastremos problemas a las secciones subsiguientes.
Visualizar las primeras y ultimas filas de la tabla
# Ver las primeras 6 filas del dataset
head(datos) # head() muestra por defecto las primeras 6 filas
## # A tibble: 6 × 8
## gender `race/ethnicity` parental level of educa…¹ lunch test preparation cou…²
## <chr> <chr> <chr> <chr> <chr>
## 1 female group B bachelor's degree stan… none
## 2 female group C some college stan… completed
## 3 female group B master's degree stan… none
## 4 male group A associate's degree free… none
## 5 male group C some college stan… none
## 6 female group B associate's degree stan… none
## # ℹ abbreviated names: ¹`parental level of education`,
## # ²`test preparation course`
## # ℹ 3 more variables: `math score` <dbl>, `reading score` <dbl>,
## # `writing score` <dbl>
# Mostrar las primeras 10 filas utilizando el operador pipe (%>%) de dplyr
datos %>% head(10) # Es equivalente a head(datos, 10)
## # A tibble: 10 × 8
## gender `race/ethnicity` parental level of educ…¹ lunch test preparation cou…²
## <chr> <chr> <chr> <chr> <chr>
## 1 female group B bachelor's degree stan… none
## 2 female group C some college stan… completed
## 3 female group B master's degree stan… none
## 4 male group A associate's degree free… none
## 5 male group C some college stan… none
## 6 female group B associate's degree stan… none
## 7 female group B some college stan… completed
## 8 male group B some college free… none
## 9 male group D high school free… completed
## 10 female group B high school free… none
## # ℹ abbreviated names: ¹`parental level of education`,
## # ²`test preparation course`
## # ℹ 3 more variables: `math score` <dbl>, `reading score` <dbl>,
## # `writing score` <dbl>
# Mostrar las últimas 10 filas del dataset
datos %>% tail(10) # tail() muestra las últimas filas; por defecto 6, aquí indicamos 10
## # A tibble: 10 × 8
## gender `race/ethnicity` parental level of educ…¹ lunch test preparation cou…²
## <chr> <chr> <chr> <chr> <chr>
## 1 male group E high school free… completed
## 2 female group B some high school stan… completed
## 3 female group D associate's degree free… none
## 4 female group D bachelor's degree free… none
## 5 male group A high school stan… none
## 6 female group E master's degree stan… completed
## 7 male group C high school free… none
## 8 female group C high school free… completed
## 9 female group D some college stan… completed
## 10 female group D some college free… none
## # ℹ abbreviated names: ¹`parental level of education`,
## # ²`test preparation course`
## # ℹ 3 more variables: `math score` <dbl>, `reading score` <dbl>,
## # `writing score` <dbl>
Revisar el tipo de datos que tenemos en cada columna
str() muestra las variables, su tipo (numérico,
caracter, etc.), y los primeros valores
# Ver la estructura del dataframe
str(datos)
## spc_tbl_ [1,000 × 8] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ gender : chr [1:1000] "female" "female" "female" "male" ...
## $ race/ethnicity : chr [1:1000] "group B" "group C" "group B" "group A" ...
## $ parental level of education: chr [1:1000] "bachelor's degree" "some college" "master's degree" "associate's degree" ...
## $ lunch : chr [1:1000] "standard" "standard" "standard" "free/reduced" ...
## $ test preparation course : chr [1:1000] "none" "completed" "none" "none" ...
## $ math score : num [1:1000] 72 69 90 47 76 71 88 40 64 38 ...
## $ reading score : num [1:1000] 72 90 95 57 78 83 95 43 64 60 ...
## $ writing score : num [1:1000] 74 88 93 44 75 78 92 39 67 50 ...
## - attr(*, "spec")=
## .. cols(
## .. gender = col_character(),
## .. `race/ethnicity` = col_character(),
## .. `parental level of education` = col_character(),
## .. lunch = col_character(),
## .. `test preparation course` = col_character(),
## .. `math score` = col_double(),
## .. `reading score` = col_double(),
## .. `writing score` = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
summary()devuelve mínimo, 1er cuartil, mediana, media,
3er cuartil y máximo para cada variable numérica
# Mostrar estadísticas descriptivas de las variables numéricas
summary(datos)
## gender race/ethnicity parental level of education
## Length:1000 Length:1000 Length:1000
## Class :character Class :character Class :character
## Mode :character Mode :character Mode :character
##
##
##
## lunch test preparation course math score reading score
## Length:1000 Length:1000 Min. : 0.00 Min. : 17.00
## Class :character Class :character 1st Qu.: 57.00 1st Qu.: 59.00
## Mode :character Mode :character Median : 66.00 Median : 70.00
## Mean : 66.09 Mean : 69.17
## 3rd Qu.: 77.00 3rd Qu.: 79.00
## Max. :100.00 Max. :100.00
## writing score
## Min. : 10.00
## 1st Qu.: 57.75
## Median : 69.00
## Mean : 68.05
## 3rd Qu.: 79.00
## Max. :100.00
Ahora que tenemos una idea general de los datos, podemos realizar resúmenes agrupados por variables como género o grupo étnico.
# Contar cuántos estudiantes hay en cada grupo étnico
datos %>%
count(`race/ethnicity`) %>% # Agrupa por 'race/ethnicity' y cuenta las observaciones
arrange(desc(n)) # Ordena los resultados de mayor a menor
## # A tibble: 5 × 2
## `race/ethnicity` n
## <chr> <int>
## 1 group C 319
## 2 group D 262
## 3 group B 190
## 4 group E 140
## 5 group A 89
# Calcular promedios de puntajes por género
datos %>%
group_by(gender) %>% # Agrupa las observaciones por género
summarise(
promedio_matematicas = mean(`math score`, na.rm = TRUE), # Calcula la media de cada variable
promedio_lectura = mean(`reading score`, na.rm = TRUE),
promedio_escritura = mean(`writing score`, na.rm = TRUE)
)
## # A tibble: 2 × 4
## gender promedio_matematicas promedio_lectura promedio_escritura
## <chr> <dbl> <dbl> <dbl>
## 1 female 63.6 72.6 72.5
## 2 male 68.7 65.5 63.3
También graficaremos los resultados para facilitar su interpretación.
# Crear un gráfico de barras con los promedios de matemáticas por grupo étnico
datos %>%
group_by(`race/ethnicity`) %>%
summarise(promedio_matematicas = mean(`math score`)) %>%
ggplot(aes(x = `race/ethnicity`, y = promedio_matematicas, fill = `race/ethnicity`)) +
geom_bar(stat = "identity") + # Crea las barras con altura igual al valor (no al conteo)
labs(title = "Promedio de Matemáticas por Grupo Étnico",
x = "Grupo Étnico",
y = "Promedio")
# Crear un boxplot para observar la distribución del puntaje de lectura según el nivel educativo de los padres
ggplot(datos, aes(x = `parental level of education`, y = `reading score`)) +
geom_boxplot(fill = "skyblue") + # Boxplot con color de relleno
coord_flip() + # Gira los ejes para facilitar lectura horizontal
labs(title = "Distribución de Reading Score por Nivel Educativo de los Padres",
x = "Nivel educativo",
y = "Reading Score")
Un recurso útil puede ser la creación de un gráfico de dispersión con una línea de regresión lineal ajustada. Esto nos sirve para visualizar si hay una relación lineal entre dos variables numéricas. En nuestro caso, nos interesa estudiar la relación entre el puntaje de escritura y el de lectura.
Desarmamos el código línea a línea:
gplot(datos, aes(x = ..., y = ...)): Inicializa el
gráfico con el conjunto de datos datos, mapeando el eje X
al writing score y el eje Y al reading score.
geom_point(...): Agrega los puntos del gráfico de
dispersión.
alpha = 0.6: Ajusta la transparencia de los puntos (0 totalmente transparente, 1 totalmente opaco).
color = "steelblue": Define el color de los puntos.
geom_smooth(method = "lm", col = "red", se = TRUE):
Agrega una línea de tendencia ajustada por regresión
lineal.
method = "lm": Le dice aggplotque use el modelo lineal (
col = "red": Color rojo para la línea.
se = TRUE: Muestra el intervalo de confianza alrededor de la línea (por defecto es del 95%)
labs(...): Definir el título del gráficoggplot(datos, aes(x = `writing score`, y = `reading score`)) +
geom_point(alpha = 0.6, color = "steelblue") +
geom_smooth(method = "lm", col = "red", se = TRUE) +
labs(title = "Relación entre Reading Score y Writing Score")
## `geom_smooth()` using formula = 'y ~ x'
Si el modelo de regresión que vimos en el paso anterior nos parece interesante, podemos guardarlo en una variable y continuar trabajando sobre el mismo
reading score es la variable dependiente
(Y).
writing score es la variable independiente
(X).
data = datos: El modelo se ajusta usando los datos
del dataframe datos
lm() es una función para ajustar modelos
lineales.
La función lm() utiliza el método de mínimos cuadrados
ordinarios (OLS - Ordinary Least Squares). Eso es justamente lo
que nosotros estamos ensayando, asi que nos viene de diez.
modelo <- lm(`reading score` ~ `writing score`, data = datos)
Llegó la hora de la verdad. El paso que sigue consiste en analizar los resultados de nuestro modelo.
El resumen del modelo nos dice cuánto mejora nuestra predicción del puntaje de lectura gracias a conocer el puntaje de escritura, y cuán confiables son esas estimaciones. Además, nos permite evaluar si el modelo es estadísticamente útil y si los errores (residuals) están distribuidos de manera adecuada.
summary(modelo)
##
## Call:
## lm(formula = `reading score` ~ `writing score`, data = datos)
##
## Residuals:
## Min 1Q Median 3Q Max
## -14.6226 -2.9554 0.1352 3.0399 11.4602
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.75051 0.63175 10.69 <2e-16 ***
## `writing score` 0.91719 0.00906 101.23 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 4.352 on 998 degrees of freedom
## Multiple R-squared: 0.9113, Adjusted R-squared: 0.9112
## F-statistic: 1.025e+04 on 1 and 998 DF, p-value: < 2.2e-16
Vamos a tratar de desarmar esto, parte por parte, para comprenderlo mejor
Call:
lm(formula = `reading score` ~ `writing score`, data = datos)
Esta es simplemente la llamada a la función
lm() que ejecutamos anteriormente Es un recordatorio para
saber qué fórmula usamos, qué variables incluimos en el modelo y de qué
base de datos (datos) provienen.
Residuals:
Min 1Q Median 3Q Max
-14.6226 -2.9554 0.1352 3.0399 11.4602
Los residuos son las diferencias entre los valores reales observados de la variable dependiente (reading score) y los valores que predice el modelo. Es decir:
residuo = valor real − valor predicho
Tenemos un resumen de los residuos
Min y Max: residuos situados en los extremos (peor y mejor predicción).
1Q (Primer Cuartil) y 3Q (Tercer Cuartil): nos dicen cómo están distribuidos los errores.
Median: el residuo del caso “típico” (debería estar cerca de 0 si el modelo es bueno).
Esta es una parte crucial del resumen, ya que nos muestra los parámetros estimados del modelo, junto con su precisión e importancia estadística.
| Columna | Significado |
|---|---|
| Estimate | Es el valor estimado del coeficiente del modelo. |
| Std. Error | El error estándar: mide la precisión de la estimación del coeficiente. Valores bajos indican mayor precisión. |
| t value | Valor t de Student: se usa para testear si el coeficiente es
significativamente distinto de 0. Se calcula como
Estimate / Std.Error. |
| Pr(>|t|) | Valor p (p-value): indica la probabilidad de obtener un coeficiente tan extremo por azar si el coeficiente real fuera 0 (hipótesis nula). Un valor p bajo sugiere que el coeficiente es estadísticamente significativo. |
Intercept (β₀ = 6.75): Cuando writing score = 0, el modelo predice un reading score de 6.75 (aunque en la práctica un puntaje 0 en escritura probablemente no exista, el intercepto se incluye en el modelo como parte de la fórmula matemática).
Coeficiente de writing score (β₁ = 0.917): por cada
punto adicional en escritura, el modelo predice un aumento de 0.917
puntos en lectura. Es decir, hay una fuerte relación positiva entre
ambas habilidades.
El t value (valor t) es el resultado de un test
estadístico que sirve para evaluar si cada coeficiente (es decir, cada
parámetro del modelo) es estadísticamente diferente de cero.
Si un coeficiente es igual a cero, significa que esa variable no aporta nada al modelo (no está relacionada con la variable que queremos predecir). En cambio, si es significativamente distinto de cero, entonces sí influye en la variable dependiente y vale la pena mantenerlo en el modelo.
$$t = \frac{Estimate}{Std. Error} = \frac{0.91719}{0.00906} = 101.23$$
Esto significa que el coeficiente estimado (0.917) está a más de 100 desviaciones estándar de 0.
¿Qué indica un valor t alto? Un valor t grande (positivo o negativo) indica que es muy poco probable que ese coeficiente sea cero por casualidad.
Eso se refleja en el valor p (Pr(>|t|)) que aparece
al lado:
Si el valor t es alto, el valor p debería ser muy bajo → el coeficiente es significativo
Ambos valores p (< 2e-16) son extremadamente bajos,
por lo que podemos afirmar con seguridad que ambos coeficientes son
estadísticamente significativos. En la misma línea, los `***` nos dicen
que el nivel de significacncia es menor a 0.001.
Es una medida de la desviación típica de los residuales, es decir, cuánto se equivocan nuestras predicciones en promedio. Un valor más bajo indica mejor ajuste.
Se nos indica que tenemos 988 grados de libertad porque hay 1000 observaciones y tenemos dos parámetros en el modelo (β₀ y β₁)
Residual standard error: 4.352 on 998 degrees of freedom
R² es el Coeficiente de determinación: nos indica el porcentaje de variabilidad de la variable dependiente (reading score) que es explicada por el modelo. En este caso, el modelo explica el 91.13% de la variabilidad, lo cual es excelente para un modelo lineal.
Multiple R-squared: 0.9113, Adjusted R-squared: 0.9112
El F-statistic evalúa si el modelo explica una parte significativa de la variabilidad de la variable dependiente (reading score), comparandolo con un modelo que no incluye ninguna variable explicativa (sólo una constante, la media).
En este caso, el valor F es muy alto (10,250), y el valor p es extremadamente bajo (< 2.2e-16), lo cual indica que el predictor writing score aporta información relevante al modelo y no deberíamos descartarlo.
F-statistic: 1.025e+04 on 1 and 998 DF, p-value: < 2.2e-16
Los elementos que hemos mencionado anteriormente pueden complementarse mediante el recurso a los intervalos de confianza.
confint(modelo)
## 2.5 % 97.5 %
## (Intercept) 5.5107855 7.990224
## `writing score` 0.8994114 0.934970
Veamos cómo leer esto
El intervalo de confianza del 95% para la ordenada al origen
(intercepto) va de 5.51 a 7.99.Esto significa que, si el
writing score fuera 0 (lo cual puede no tener sentido
práctico), el modelo predice que el valor promedio de la variable
dependiente estaría entre 5.51 y 7.99, con un 95% de confianza.
El intervalo de confianza del 95% para el coeficiente de writing score va de 0.899 a 0.935. Esto indica que, con un 95% de confianza, el verdadero efecto poblacional de aumentar una unidad en writing score sobre la variable dependiente está entre +0.899 y +0.935.
Si el intervalo no incluye 0, el predictor se considera estadísticamente significativo.
Siempre que realizamos un análisis de regresión debemos comprobar que se cumplan los supuestos.
Podemos usar la siguiente tabla como guía para evaluar cada supuesto y graficar nuestros datos. Es posible que un gráfico se use para más de un supuesto.
| Supuesto | Descripción | Test estadístico | Gráfico recomendado |
|---|---|---|---|
| 1. Linealidad | El modelo asume que existe una relación lineal entre la variable dependiente (respuesta) y las variables independiente (predictora). | Coeficiente de correlación de Pearson | - Gráfico de dispersión (X vs. Y) - Residuos vs. valores ajustados |
| 2. Homocedasticidad | Se espera que los residuos tengan una varianza constante a lo largo de todos los niveles de las variables predictoras. | - Breusch-Pagan | Residuos vs. valores ajustados - Residuos en orden de observación (solo si hay orden temporal). |
| 3. Normalidad de los errores | Los errores del modelo (no las variables originales) deben seguir una distribución normal con media cero. | - Shapiro-Wilk | - Histograma de residuos - Q–Q plot de residuos |
| 4. Independencia de los errores | Se asume que los errores son independientes entre sí, es decir, el error asociado a una observación no debe estar relacionado con el error de otra. | Durbin-Watson (solo si hay orden temporal). | Ninguno específico (se evalúa que no haya patrones en residuos) |
Este gráfico nos muestra en el eje x los valores ajustados (predichos) por el modelo lineal, es decir, las estimaciones obtenidas a partir de la combinación de los coeficientes del modelo y los valores de las variables independientes. En el eje y se representan los residuos, es decir, las diferencias entre los valores reales observados de la variable dependiente y los valores predichos por el modelo.
Este gráfico es esencial para evaluar la validez del supuesto de homocedasticidad, que indica que los residuos deberían tener una varianza constante a lo largo del rango de valores ajustados. Si el modelo es adecuado, deberíamos observar una nube de puntos dispersos alrededor de la línea horizontal (residuo = 0), sin ningún patrón sistemático.
Patrones en forma de abanico (es decir, residuos que se expanden o se contraen a medida que aumentan los valores ajustados) indican heterocedasticidad, lo cual viola uno de los supuestos fundamentales de la regresión lineal y puede afectar la validez de los intervalos de confianza y pruebas de hipótesis. Asimismo, si los residuos se organizan en forma curva, esto puede indicar no linealidad, es decir, que la relación entre las variables no es lineal, lo que sugiere que el modelo lineal no es adecuado y podría ser necesario incorporar transformaciones o un modelo más complejo.
# calculamos residuos y valores ajustados
residuos <- resid(modelo)
valores_ajustados <- fitted(modelo)
# graficamos
ggplot(data = NULL, aes(x = valores_ajustados, y = residuos)) +
geom_point(alpha = 0.6, color = "darkorange") +
geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
labs(
title = "Residuos vs. Valores Ajustados",
x = "Valores Ajustados",
y = "Residuos"
) +
theme_minimal()
El test de Breusch-Pagan evalúa si los residuos del modelo de regresión tienen varianza constante (homocedasticidad) o si, por el contrario, la varianza cambia sistemáticamente en función de los valores ajustados u otras variables (heterocedasticidad).
Hipótesis nula (H₀): los residuos tienen varianza constante (homocedasticidad).
Hipótesis alternativa (H₁): la varianza de los residuos no es constante (heterocedasticidad).
Un p-valor bajo (< 0.05) sugiere que hay evidencia de heterocedasticidad.
#Usamos la función de lmtest
## para el test Breusch-Pagan
bptest(modelo)
##
## studentized Breusch-Pagan test
##
## data: modelo
## BP = 0.11724, df = 1, p-value = 0.7321
Dado que el p-value = 0.7321 es mucho mayor que 0.05, no tenemos evidencia suficiente para rechazar la hipótesis nula. Asumimos que hay homocedasticidad en el modelo.
El histograma de residuos tiene como objetivo evaluar la distribución de los errores del modelo, o más específicamente, si los residuos se distribuyen de manera aproximadamente normal. Este supuesto de normalidad de los errores es clave para que las pruebas de significancia (como los t-tests para los coeficientes del modelo) y los intervalos de confianza funcionen correctamente.
En este gráfico, se espera que la distribución de los residuos tenga una forma de campana simétrica alrededor de cero. Si el histograma muestra asimetría (skewness) hacia la derecha o izquierda, o si hay muchas observaciones en los extremos (colas gruesas), esto sugiere desviaciones de la normalidad, lo cual puede hacer que las inferencias estadísticas asociadas al modelo sean menos confiables.
ggplot(data = NULL, aes(x = residuos)) +
geom_histogram(bins = 30, fill = "skyblue", color = "black") +
labs(
title = "Histograma de Residuos",
x = "Residuos",
y = "Frecuencia"
) +
theme_minimal()
El Q-Q plot es una herramienta gráfica para evaluar si los residuos del modelo se distribuyen de forma normal. En este gráfico se comparan los cuantiles teóricos de una distribución normal (en el eje x) con los cuantiles observados de los residuos (en el eje y).
Si los residuos siguen una distribución normal, los puntos deberían alinearse aproximadamente sobre una línea recta de 45°, que representa la concordancia entre los valores observados y los esperados bajo normalidad. Las desviaciones sistemáticas de esta línea indican problemas con la distribución de los errores. Por ejemplo:
Si los puntos se curvan hacia arriba o hacia abajo en los extremos, esto indica colas pesadas o ligeras (leptocurtosis o platicurtosis).
Si los puntos se desvían de forma asimétrica hacia uno de los lados, se trata de una distribución sesgada.
El Q-Q plot es especialmente útil para detectar valores atípicos, ya que estos suelen aparecer como puntos que se alejan notoriamente de la línea de referencia. En combinación con el histograma, nos brinda una perspectiva más rica sobre el supuesto de normalidad en el modelo.
ggplot(data = NULL, aes(sample = residuos)) +
stat_qq(color = "steelblue") +
stat_qq_line(color = "red") +
labs(
title = "Q-Q Plot de los Residuos",
x = "Cuantiles teoricos",
y = "Cuantiles de los residuos"
) +
theme_minimal()
Hay algunos test que podemos usar para evaluar la normalidad de los residuos
# Calcular los residuos del modelo
residuos <- resid(modelo)
# Test de Shapiro-Wilk
shapiro.test(residuos)
##
## Shapiro-Wilk normality test
##
## data: residuos
## W = 0.99782, p-value = 0.2163
El test de Shapiro-Wilk evalúa la hipótesis nula de que los datos provienen de una distribución normal. Dado que el p-valor es mayor a 0.05, no se rechaza la hipótesis nula, lo cual indica que no hay evidencia suficiente para afirmar que los residuos se desvían de la normalidad.
Para practicar lo que hemos visto hasta aquí, pueden realizar los siguientes ejercicios que compartiremos en el encuentro:
Use las categorías de la columna “gender” para separar la base en grupos. Vea cómo cambian los resultados que obtuvimos al realizar nuestros análisis con la base completa. Trate de explicar los resultados obtenidos: la recta estimada, pendiente, ordenada al origen, errores, etc.
Puede evaluar la relación entre la puntuación obtenida en escritura y la puntuación obtenida en matemáticas.