Titulo: “PROYECTO INTEGRADOR DE ESTADISTICA”

TEMA: Analizar el rendimiento académico de los estudiantes de la Costa y Sierra, en sus diferentes niveles asociados y comprobar brechas estructurales mediante el uso de técnicas de estadística descriptiva e inferencial

if (!require(tidyverse)) install.packages("tidyverse")
## Cargando paquete requerido: tidyverse
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.6
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.1     ✔ tibble    3.3.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.2
## ✔ purrr     1.2.1     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
if (!require(survey)) install.packages("survey")
## Cargando paquete requerido: survey
## Cargando paquete requerido: grid
## Cargando paquete requerido: Matrix
## 
## Adjuntando el paquete: 'Matrix'
## 
## The following objects are masked from 'package:tidyr':
## 
##     expand, pack, unpack
## 
## Cargando paquete requerido: survival
## 
## Adjuntando el paquete: 'survey'
## 
## The following object is masked from 'package:graphics':
## 
##     dotchart
if (!require(kableExtra)) install.packages("kableExtra")
## Cargando paquete requerido: kableExtra
## 
## Adjuntando el paquete: 'kableExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     group_rows
print("Paquetes instalados")
## [1] "Paquetes instalados"
# Librerías a utilizar

library(tidyverse)
library(rstatix)
## 
## Adjuntando el paquete: 'rstatix'
## The following object is masked from 'package:stats':
## 
##     filter
library(readxl)
library(dplyr)
library(survey)
library(knitr)
library(kableExtra)
# Cargar los datos
ser_estudiante <- read_excel("ser_estudiante.xlsx")
# Verificación básica
dim(ser_estudiante)
## [1] 50578    76
print("Dataset cargado correctamente")
## [1] "Dataset cargado correctamente"

Preparación y depuración de datos

En esta sección se realiza la selección de variables relevantes, la depuración de registros incompletos y la transformación de códigos administrativos en variables categóricas interpretables.

#Limpieza y cambio de nombre de variables

data_analisis <- ser_estudiante |>
  # Selección de columnas claves y cambio de nombres segun diccionario
  select(
    Nota_Global = inev,          # La nota (Variable respuesta)
    Peso = fex_inev,             # Factor de expansión (OBLIGATORIO)
    Region = nm_regi,            # Región Natural
    Grado_Cod = grado,           # Código del nivel (4, 7, 10, 3)
    Sostenimiento_Cod = sostenimiento, # Código del tipo de colegio (1, 2, 3, 4)
    Area = tp_area

  ) |>
  
  # 2. FILTRAR datos vacíos
  filter(!is.na(Nota_Global), !is.na(Peso)) |>
  
  # 3. TRADUCIR CÓDIGOS según diccionario del ineval
  mutate(
    # CAmbio de grados segun diccionario
    Nivel = case_when(
      Grado_Cod == 4  ~ "Elemental (4to)",
      Grado_Cod == 7  ~ "Media (7mo)",
      Grado_Cod == 10 ~ "Superior (10mo)",
      Grado_Cod == 3  ~ "Bachillerato (3ro)",
      TRUE ~ as.character(Grado_Cod)
    ),
    
    #convertimos región
    Region = case_when(
      Region == 1 ~ "Costa",     # Usualmente 1 es Costa en INEVAL
      Region == 2 ~ "Sierra",    # Usualmente 2 es Sierra
      Region == 3 ~ "Amazonía",
      Region == 4 ~ "Insular",
      Region == 90 ~ "Zona No Delimitada",
      TRUE ~ as.character(Region)
    ),
    
    
    # COnversion tipo de sostenimineto del plantel
    Tipo_Colegio = case_when(
      Sostenimiento_Cod == 1 ~ "Particular",
      Sostenimiento_Cod == 2 ~ "Municipal",
      Sostenimiento_Cod == 3 ~ "Fiscomisional",
      Sostenimiento_Cod == 4 ~ "Fiscal",
      TRUE ~ as.character(Sostenimiento_Cod)
    ),
    
    # Tipo de financiamiento (agrupación analítica)
    Financiamiento = case_when(
      Tipo_Colegio %in% c("Fiscal", "Municipal", "Fiscomisional") ~ "Público",
      Tipo_Colegio == "Particular" ~ "Privado",
      TRUE ~ NA_character_),
    
    # COnversion de zona
    Area = case_when(
      Area == 1 ~ "Urbana",
      Area == 2 ~ "Rural",
      TRUE ~"Desconocida"
      )
    ) |>
  
   # 4. Filtrar solo regiones comparables (Costa y Sierra)
  filter(Region %in% c("Costa", "Sierra")) |>
  
  # 5. CONVERTIR A FACTORES (Para que R entienda que son grupos)
  mutate(
    Nivel = factor(Nivel, levels = c("Elemental (4to)", "Media (7mo)", "Superior (10mo)", "Bachillerato (3ro)")),
    Tipo_Colegio = factor(Tipo_Colegio),
    Region = factor(Region),
    Area = factor(Area)
  )
# 6. Mantener solo variables analíticas finales
data_analisis <- data_analisis |>
  select(
    Nota_Global,
    Peso,
    Region,
    Nivel,
    Area,
    Tipo_Colegio,
    Financiamiento
  )


knitr::kable(
  head(data_analisis),
  caption = "Primeras observaciones del conjunto de datos analizado",
  align = "c"
) %>%
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "bordered", "hover")
  )
Primeras observaciones del conjunto de datos analizado
Nota_Global Peso Region Nivel Area Tipo_Colegio Financiamiento
690 83.73792 Sierra Superior (10mo) Rural Fiscal Público
740 83.73792 Sierra Superior (10mo) Rural Fiscal Público
723 83.73792 Sierra Superior (10mo) Rural Fiscal Público
691 83.73792 Sierra Superior (10mo) Rural Fiscal Público
710 83.73792 Sierra Superior (10mo) Rural Fiscal Público
730 83.73792 Sierra Superior (10mo) Rural Fiscal Público

Diseño muestral

# 1. CREAR EL DISEÑO MUESTRAL (Obligatorio para que svyby funcione)
diseno_ineval <- svydesign(
  id = ~1,
  weights = ~Peso,
  data = data_analisis
)
options(survey.lonely.psu = "adjust")

# 2. CALCULAR ESTADÍSTICOS DE REGIÓN

# Esto crea 'media_region' y 'tabla_region'
media_region <- svyby(~Nota_Global, by = ~Region, design = diseno_ineval, FUN = svymean, na.rm = TRUE)
tabla_region <- svytable(~Region, diseno_ineval)

# 3. CALCULAR ESTADÍSTICOS DE COLEGIO

media_colegio <- svyby(  ~Nota_Global,  by = ~Tipo_Colegio,  design = diseno_ineval,   FUN = svymean,   na.rm = TRUE)


tabla_colegio <- svytable(~Tipo_Colegio, diseno_ineval)

ESTADÍSTICOS DESCRIPTIVOS

A. Análisis por región natural

A continuación, se presentan los resultados correspondientes al análisis por región natural (Costa y Sierra), incluyendo el tamaño poblacional estimado, el promedio de la Nota Global con su error estándar y la dispersión medida mediante la desviación estándar.

# Tabla de frecuencia ponderada por región
tabla_region <- svytable(~Region, diseno_ineval)

knitr::kable(
  tabla_region,
  caption = "Estudiantes estimados por región",
  align = "c"
) %>%
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "bordered", "hover")
  )
Estudiantes estimados por región
Region Freq
Costa 576112.9
Sierra 453711.5
# Media de la Nota Global y Error Estándar por región
media_region <- svyby(
  ~Nota_Global,
  by = ~Region,
  design = diseno_ineval,
  FUN = svymean,
  na.rm = TRUE
)

knitr::kable(
  media_region,
  caption = "Promedio de la Nota Global y error estándar por región",
  align = "c"
) %>%
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "bordered", "hover")
  )
Promedio de la Nota Global y error estándar por región
Region Nota_Global se
Costa Costa 677.8819 0.4003375
Sierra Sierra 694.1146 0.4111974
# Desviación estándar de la Nota Global por región
desviacion_region <- svyby(
  ~Nota_Global,
  by = ~Region,
  design = diseno_ineval,
  FUN = svyvar,
  na.rm = TRUE
)

desviacion_region$Nota_Global <- sqrt(desviacion_region$Nota_Global)

knitr::kable(
  desviacion_region,
  caption = "Desviación estándar de la Nota Global por región",
  align = "c"
) %>%
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "bordered", "hover")
  )
Desviación estándar de la Nota Global por región
Region Nota_Global se
Costa Costa 39.40888 31.84468
Sierra Sierra 39.31648 35.48828

GRÁFICOS Y VISUALIZACIONES

Análisis gráfico del rendimiento académico

1. Rendimiento académico promedio por región

region_bar <- as.data.frame(media_region)
ggplot(
  region_bar,
  aes(x = Region, y = Nota_Global, fill = Region)
) +
  geom_col(
    width = 0.6,
    color = "#333333",   # borde oscuro para que destaque en proyector
    linewidth = 0.5
  ) +
  geom_text(
    aes(label = round(Nota_Global, 1)),
    vjust = -0.4,
    size = 5,
    fontface = "bold",
    color = "#111111"
  ) +
  scale_fill_manual(
    values = c(
      "Costa"  = "#1F77B4",  # Azul presentación
      "Sierra" = "#FF7F0E"   # Naranja contraste
    )
  ) +
  labs(
    title = "Nota Promedio Global por Región",
    x = "Región",
    y = "Puntaje Promedio"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5),
    axis.title = element_text(face = "bold"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank()
  ) +
  coord_cartesian(ylim = c(600, 750))

2.- Rendimiento Académico por Región y Tipo de Financiamiento

media_region_fin <- svyby(
  ~Nota_Global,
  by = ~Region + Financiamiento,
  design = diseno_ineval,
  FUN = svymean,
  na.rm = TRUE
)

media_region_fin_df <- as.data.frame(media_region_fin)

p <- ggplot(
  media_region_fin_df,
  aes(x = Financiamiento, y = Nota_Global, fill = Financiamiento)
) +
  geom_col(
    width = 0.6,
    color = "#333333",
    linewidth = 0.5
  ) +
  geom_text(
    aes(label = round(Nota_Global, 1)),
    vjust = -0.4,
    size = 4.5,
    fontface = "bold",
    color = "#111111"
  ) +
  facet_wrap(~Region) +
  scale_fill_manual(
    values = c(
      "Público" = "#1F77B4",
      "Privado" = "#FF7F0E"
    )
  ) +
  labs(
    title = "Rendimiento Académico por Región y
    Tipo de Financiamiento",
    x = "Tipo de Financiamiento",
    y = "Puntaje Promedio"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5),
    axis.title = element_text(face = "bold"),
    strip.text = element_text(face = "bold"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank()
  ) +
  coord_cartesian(ylim = c(600, 750))

p

3.- Rendimiento académico por nivel educativo y región

media_region_nivel <- svyby(
  ~Nota_Global,
  by = ~Region + Nivel,
  design = diseno_ineval,
  FUN = svymean,
  na.rm = TRUE
)

media_region_nivel_df <- as.data.frame(media_region_nivel)

ggplot(
  media_region_nivel_df,
  aes(x = Nivel, y = Nota_Global, fill = Region)
) +
  geom_col(
    position = position_dodge(width = 0.7),
    width = 0.6,
    color = "#333333",
    linewidth = 0.4
  ) +
  geom_text(
    aes(label = round(Nota_Global, 1)),
    position = position_dodge(width = 0.7),
    vjust = -0.4,
    size = 3.6,
    fontface = "bold"
  ) +
  scale_fill_manual(
    values = c(
      "Costa"  = "#1F77B4",
      "Sierra" = "#FF7F0E"
    )
  ) +
  labs(
    title = "Rendimiento académico por nivel educativo y región",
    x = "Nivel educativo",
    y = "Puntaje promedio"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    axis.title = element_text(face = "bold"),
    legend.position = "top",
    panel.grid.major.x = element_blank()
  ) +
  coord_cartesian(ylim = c(600, 750))

4.- Distribución de la Nota Global por Región

p <- ggplot(
  data_analisis,
  aes(
    x = Region,
    y = Nota_Global,
    weight = Peso,
    fill = Region
  )
) +
  geom_boxplot(
    alpha = 0.8,
    color = "#333333",     # borde oscuro (mejor definición)
    linewidth = 0.6,
    outlier.shape = 21,    # puntos de outliers más visibles
    outlier.size = 1.8
  ) +
  scale_fill_manual(
    values = c(
      "Costa"  = "#1F77B4",  # Azul presentación
      "Sierra" = "#FF7F0E"   # Naranja contraste
    )
  ) +
  labs(
    title = "Distribución de la Nota Global por Región",
    x = "Región",
    y = "Nota Global"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5),
    axis.title = element_text(face = "bold"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank()
  ) +
  coord_cartesian(ylim = c(500, 800))

p

region_freq <- as.data.frame(tabla_region)

ggplot(region_freq, aes(x = Region, y = Freq, fill = Region)) +
  geom_col(
    width = 0.6,
    color = "#333333",
    linewidth = 0.4
  ) +
  geom_text(
    aes(label = format(round(Freq, 0), big.mark = ",")),
    vjust = -0.4,
    size = 4,
    fontface = "bold",
    color = "#111111"
  ) +
  scale_fill_manual(
    values = c(
      "Costa"  = "#1F77B4",
      "Sierra" = "#FF7F0E"
    )
  ) +
  labs(
    title = "Distribución de estudiantes por región",
    x = "Región",
    y = "Estudiantes estimados"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5),
    axis.title = element_text(face = "bold"),
    panel.grid.major.x = element_blank()
  )

generar_tabla_resumen <- function(variable_formula) {
  # Frecuencia ponderada
  tabla_freq <- svytable(variable_formula, diseno_ineval)
  df_freq <- as.data.frame(tabla_freq)
  colnames(df_freq) <- c("Categoria", "Estudiantes_Estimados")

  # CERRAMOS las cantidades (personas)
  df_freq$Estudiantes_Estimados <- round(df_freq$Estudiantes_Estimados, 0)

  # Media y error estándar
  tabla_media <- svyby(
    ~Nota_Global,
    variable_formula,
    diseno_ineval,
    svymean,
    na.rm = TRUE
  )
  colnames(tabla_media) <- c("Categoria", "Nota_Promedio", "Error_Estandar")

  # Unimos
  tabla_final <- merge(df_freq, tabla_media, by = "Categoria")

  return(tabla_final)
}
datos_region <- generar_tabla_resumen(~Region)

datos_region %>%
  kable(
    caption = "Resumen descriptivo por región natural",
    digits = c(0, 0, 2, 2),  # 👈 control fino por columna
    col.names = c(
      "Región",
      "Estudiantes estimados",
      "Nota promedio",
      "Error estándar"
    ),
    format.args = list(big.mark = ","),
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Resumen descriptivo por región natural
Región Estudiantes estimados Nota promedio Error estándar
Costa 576,113 677.88 0.40
Sierra 453,711 694.11 0.41

PRUEBA DE HIPÓTESIS

Para que ‘anova’ funcione en encuestas, debemos comparar el modelo de interés contra un “modelo nulo” (sin variables).

# =========================================================
# PRUEBAS DE HIPÓTESIS (CÁLCULO – SIN PRESENTACIÓN)
# =========================================================

# Modelo nulo (referencia)
modelo_nulo <- svyglm(Nota_Global ~ 1, design = diseno_ineval)

# ---------------------------------------------------------
# H1: REGIÓN (Costa vs Sierra)
# ---------------------------------------------------------
modelo_region <- svyglm(Nota_Global ~ Region, design = diseno_ineval)
anova_region <- anova(modelo_nulo, modelo_region)

# ---------------------------------------------------------
# H2: TIPO DE FINANCIAMIENTO (Público vs Privado)
# ---------------------------------------------------------
modelo_financiamiento <- svyglm(Nota_Global ~ Financiamiento, design = diseno_ineval)
anova_financiamiento <- anova(modelo_nulo, modelo_financiamiento)

# ---------------------------------------------------------
# H3: ÁREA GEOGRÁFICA (Urbana vs Rural)
# ---------------------------------------------------------
modelo_area <- svyglm(Nota_Global ~ Area, design = diseno_ineval)
anova_area <- anova(modelo_nulo, modelo_area)

# ---------------------------------------------------------
# H4: NIVEL EDUCATIVO
# ---------------------------------------------------------
modelo_nivel <- svyglm(Nota_Global ~ Nivel, design = diseno_ineval)
anova_nivel <- anova(modelo_nulo, modelo_nivel)
anova_region
## Working (Rao-Scott+F) LRT for Region
##  in svyglm(formula = Nota_Global ~ Region, design = diseno_ineval)
## Working 2logLR =  800.0554 p= < 2.22e-16 
## df=1;  denominator df= 44052
# TABLA RESUMEN DE HIPÓTESIS (PRESENTACIÓN)


library(stringr)

extraer_p <- function(test_obj) {
  # Captura la salida como texto
  salida <- capture.output(test_obj)
  # Busca la línea que contiene "p="
  linea_p <- salida[grepl("p=", salida)]
  # Extrae el número después de "p="
  valor <- str_extract(linea_p, "(?<=p= ).+")
  # Si aparece con "<", lo tratamos como muy pequeño
  if (grepl("<", valor)) {
    return(1e-16)  # o cualquier umbral pequeño que se quiera mostrar
  } else {
    return(as.numeric(valor))
  }
}



tabla_hipotesis <- data.frame(
  Hipotesis = c(
    "Región (Costa vs Sierra)",
    "Tipo de financiamiento (Público vs Privado)",
    "Área geográfica (Urbana vs Rural)",
    "Nivel educativo"
  ),
  Valor_P = c(
    extraer_p(anova_region),
    extraer_p(anova_financiamiento),
    extraer_p(anova_area),
    extraer_p(anova_nivel)
  )
)

tabla_hipotesis <- tabla_hipotesis |>
  dplyr::mutate(
    Significancia = dplyr::case_when(
      Valor_P < 0.001 ~ "***",
      Valor_P < 0.01  ~ "**",
      Valor_P < 0.05  ~ "*",
      TRUE ~ "ns"
    ),
    Decision = dplyr::case_when(
      Valor_P < 0.05 ~ "Se rechaza H₀ (existen diferencias)",
      TRUE ~ "No se rechaza H₀"
    ),
    `Valor p` = ifelse(Valor_P < 0.001, "< 0.001", round(Valor_P, 4))
  ) |>
  dplyr::select(Hipotesis, `Valor p`, Significancia, Decision)

knitr::kable(
  tabla_hipotesis,
  caption = "Resumen de pruebas de hipótesis (Rao–Scott LRT, diseño muestral)",
  col.names = c(
    "Hipótesis evaluada",
    "Valor p",
    "Sig.",
    "Conclusión estadística"
  ),
  align = "lccc"
) |>
  kableExtra::kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Resumen de pruebas de hipótesis (Rao–Scott LRT, diseño muestral)
Hipótesis evaluada Valor p Sig. Conclusión estadística
Región (Costa vs Sierra) < 0.001 *** Se rechaza H₀ (existen diferencias)
Tipo de financiamiento (Público vs Privado) < 0.001 *** Se rechaza H₀ (existen diferencias)
Área geográfica (Urbana vs Rural) < 0.001 *** Se rechaza H₀ (existen diferencias)
Nivel educativo < 0.001 *** Se rechaza H₀ (existen diferencias)

Anexo

Rendimiento académico por región, área geográfica y tipo de financiamiento (análisis complementario)

media_region_area <- svyby(
  ~Nota_Global,
  by = ~Region + Area + Financiamiento,
  design = diseno_ineval,
  FUN = svymean,
  na.rm = TRUE
)

media_region_area_df <- as.data.frame(media_region_area)

p <- ggplot(
  media_region_area_df,
  aes(x = Financiamiento, y = Nota_Global, fill = Financiamiento)
) +
  geom_col(
    position = position_dodge(width = 0.7),
    width = 0.6,
    color = "#333333",
    linewidth = 0.4
  ) +
  geom_text(
    aes(label = round(Nota_Global, 1)),
    position = position_dodge(width = 0.7),
    vjust = -0.4,
    size = 3.5,
    fontface = "bold",
    color = "#111111"
  ) +
  facet_grid(Region ~ Area) +
  scale_fill_manual(
    values = c(
      "Público" = "#1F77B4",
      "Privado" = "#FF7F0E"
    )
  ) +
  labs(
    title = "Rendimiento Académico por Región, 
    Área Geográfica y Tipo de Financiamiento",
    x = "Tipo de Financiamiento",
    y = "Puntaje Promedio"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5),
    axis.title = element_text(face = "bold"),
    strip.text = element_text(face = "bold"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank()
  ) +
  coord_cartesian(ylim = c(600, 750))

p