¡Bienvenido a este tutorial sobre estadística descriptiva en R! La estadística descriptiva es la rama de la estadística que recolecta, analiza y caracteriza un conjunto de datos (peso de la población, beneficios diarios de una empresa, temperatura mensual, etc.) con el objetivo de describir las características y comportamientos de este conjunto mediante medidas de resumen, tablas o gráficos.

En este tutorial, aprenderás a:

  1. Crear y analizar distribuciones de frecuencias.
  2. Construir e interpretar tablas de contingencia.
  3. Generar visualizaciones gráficas para resumir y entender tus datos.

1. Preparación del Entorno

Primero, vamos a cargar las librerías que necesitaremos y a crear un conjunto de datos de ejemplo. Los paquetes que usaremos son dplyr para la manipulación de datos y ggplot2 para las gráficas. knitr se usa para crear tablas bien formateadas.

# Cargar librerías necesarias
library(dplyr)
library(ggplot2)
library(knitr)

NOTA: Al guardar la base CAP_modif en formato Excel los factores se convirtieron en character. Abrir la base de datos “CAP_modif” y hacer una copia que llame “datos”

# Cargar la librería
library(readxl)

# Usamos (read_excel) para leer el archivo
# R buscará el archivo en el directorio de tu proyecto actual
CAP_modif <- read_excel("CAP_modif.xlsx")
View(CAP_modif)

# Hacemos una copia idéntica con otro nombre por ejemplo "datos"
datos <- CAP_modif

Ahora debemos volver a convertir las variables categóricas en factores, no es necesario etiquetar porque ya tienen etiquetas.

# verifiquemos el tipo de variables
str(datos)
## tibble [515 × 92] (S3: tbl_df/tbl/data.frame)
##  $ id                     : num [1:515] 1 2 3 4 5 6 7 8 9 10 ...
##  $ part_prev              : chr [1:515] "No" "Sí" "No" "No" ...
##  $ municipio              : chr [1:515] "Santa Tecla" "Santa Tecla" "Santa Tecla" "Santa Tecla" ...
##  $ edad                   : num [1:515] 52 50 27 50 47 47 33 21 37 29 ...
##  $ genero                 : chr [1:515] "Femenino" "Masculino" "Femenino" "Masculino" ...
##  $ nivel_edu              : chr [1:515] "Nunca asistió" "Primer ciclo" "Bachillerato" "Tercer ciclo" ...
##  $ enf_cro                : chr [1:515] "No" "Sí" "No" "No" ...
##  $ hipertension           : chr [1:515] "No" "No" "No" "No" ...
##  $ diabetes               : chr [1:515] "No" "Sí" "No" "No" ...
##  $ epoc                   : chr [1:515] "No" "No" "No" "No" ...
##  $ renal                  : chr [1:515] "No" "No" "No" "No" ...
##  $ artritis               : chr [1:515] "No" "No" "No" "No" ...
##  $ cardiopatia            : chr [1:515] "No" "No" "No" "No" ...
##  $ hipotiroidismo         : chr [1:515] "No" "No" "No" "No" ...
##  $ glaucoma               : chr [1:515] "No" "No" "No" "No" ...
##  $ lupus                  : chr [1:515] "No" "No" "No" "No" ...
##  $ ulcera_venosa          : chr [1:515] "No" "No" "No" "No" ...
##  $ ninguna                : logi [1:515] NA NA NA NA NA NA ...
##  $ area                   : chr [1:515] "Urbana" "Rural" "Rural" "Urbana" ...
##  $ hijxs                  : chr [1:515] "Sí" "Sí" "Sí" "No" ...
##  $ nucleo                 : num [1:515] 5 2 5 3 4 5 3 2 3 3 ...
##  $ empleo                 : chr [1:515] "Desempleo" "Empleo" "Desempleo" "Empleo" ...
##  $ ocupacion              : chr [1:515] "Desempleo" "Empleada(o) permanente" "Desempleo" "Trabajo por cuenta propia sin local" ...
##  $ ingreso                : chr [1:515] "Menos de 365" "Prefiero no contestar" "366 a 730" "Menos de 365" ...
##  $ prev_per               : chr [1:515] "No" "No, sabe" "No" "No" ...
##  $ rec_apoyo              : chr [1:515] NA "No" NA NA ...
##  $ satisfaccion           : chr [1:515] NA NA NA NA ...
##  $ prev_fam               : chr [1:515] "Sí, hubo sospecha pero no se hizo la prueba" "No" "No" "No" ...
##  $ mortalidad             : chr [1:515] "No" "No" "No" "No" ...
##  $ c1                     : num [1:515] 3 1 1 1 1 1 1 1 1 1 ...
##  $ c2                     : num [1:515] 2 1 1 1 1 1 1 1 1 1 ...
##  $ c3                     : num [1:515] 3 1 3 1 1 3 1 3 3 1 ...
##  $ c4                     : num [1:515] 1 1 1 1 1 1 1 3 3 1 ...
##  $ c5                     : num [1:515] 1 3 1 1 3 1 3 3 3 2 ...
##  $ c6                     : num [1:515] 1 1 2 1 1 2 1 2 2 1 ...
##  $ c7                     : num [1:515] 3 3 2 2 3 1 1 3 1 1 ...
##  $ c8                     : num [1:515] 1 1 1 1 1 1 1 1 1 1 ...
##  $ c9                     : num [1:515] 1 1 2 1 1 1 1 1 1 1 ...
##  $ c10                    : num [1:515] 2 1 1 2 1 1 1 1 1 1 ...
##  $ a1                     : num [1:515] 5 4 5 5 4 3 4 3 2 5 ...
##  $ a2                     : num [1:515] 5 5 5 5 4 3 4 3 5 5 ...
##  $ a3                     : num [1:515] 1 2 5 5 3 1 4 1 2 4 ...
##  $ a4                     : num [1:515] 5 5 5 5 4 5 4 5 5 4 ...
##  $ a5                     : num [1:515] 1 3 5 1 4 4 4 1 3 4 ...
##  $ p1                     : num [1:515] 5 3 5 5 1 4 3 3 3 2 ...
##  $ p2                     : num [1:515] 2 0 3 3 0 2 0 2 2 0 ...
##  $ p3                     : num [1:515] 3 0 2 3 0 3 0 2 2 0 ...
##  $ p4                     : num [1:515] 5 1 5 3 1 3 1 3 3 1 ...
##  $ p5                     : num [1:515] 5 1 3 2 3 3 3 2 5 3 ...
##  $ dosis                  : num [1:515] 3 3 4 3 2 2 2 1 4 3 ...
##  $ razon_nv               : chr [1:515] "Razón 5" "Razón 6" "Razón 1" "Razón 6" ...
##  $ periodico              : chr [1:515] "Sí" "No" "No" "Sí" ...
##  $ television             : chr [1:515] "Sí" "Sí" "Sí" "Sí" ...
##  $ redes_sociales         : chr [1:515] "No" "Sí" "Sí" "No" ...
##  $ personal_salud         : chr [1:515] "No" "No" "No" "No" ...
##  $ radio                  : chr [1:515] "Sí" "No" "Sí" "No" ...
##  $ paginas_internet       : chr [1:515] "No" "No" "Sí" "Sí" ...
##  $ escuela                : chr [1:515] "Sí" "No" "No" "No" ...
##  $ mensajes               : chr [1:515] "Sí" "Sí" "Sí" "Sí" ...
##  $ patrocinio             : chr [1:515] "Otras empresas privadas" "Asociación Salvadoreña Promotora de la Salud (ASPS)" "No recuerda" "Ministerio de Salud/Gobierno central" ...
##  $ uso_de_mascarilla      : chr [1:515] "Sí" "Sí" "Sí" "Sí" ...
##  $ distanciamiento_social : chr [1:515] "Sí" "No" "Sí" "Sí" ...
##  $ lavado_de_manos        : chr [1:515] "No" "Sí" "Sí" "No" ...
##  $ ponerse_la_vacuna      : chr [1:515] "Si" "No" "No" "Si" ...
##  $ no_recuerdo            : chr [1:515] "No" "No" "No" "No" ...
##  $ todas                  : chr [1:515] "No" "No" "No" "No" ...
##  $ No_he_escuchado        : chr [1:515] "No" "No" "No" "No" ...
##  $ transmite_por_el_agua  : chr [1:515] "Si" "No" "No" "Si" ...
##  $ transmite_en_las_suelas: chr [1:515] "Sí" "No" "No" "Sí" ...
##  $ rociar_desinfectantes  : chr [1:515] "Sí" "No" "Sí" "Sí" ...
##  $ beben_alcohol          : chr [1:515] "No" "No" "No" "Sí" ...
##  $ pueden_dejar_de_aplicar: chr [1:515] "Sí" "No" "No" "No" ...
##  $ no_se_debe_vacunar     : chr [1:515] "No" "No" "Sí" "Sí" ...
##  $ producen_esterilidad   : chr [1:515] "No" "No" "No" "No" ...
##  $ producen_homosexualidad: chr [1:515] "No" "Sí" "No" "No" ...
##  $ inyectan_el_virus      : chr [1:515] "No" "No" "Sí" "Sí" ...
##  $ antibioticos_son_buenos: chr [1:515] "No" "No" "No" "Sí" ...
##  $ vitaminas_y_minerales  : chr [1:515] "No" "No" "No" "No" ...
##  $ entrevistadoras        : num [1:515] 2 1 2 2 1 2 1 2 2 1 ...
##  $ puntaje_A              : num [1:515] 17 19 25 21 19 16 20 13 17 22 ...
##  $ c1recod                : num [1:515] 0 1 1 1 1 1 1 1 1 1 ...
##  $ c2recod                : num [1:515] 0 1 1 1 1 1 1 1 1 1 ...
##  $ c3recod                : num [1:515] 0 1 0 1 1 0 1 0 0 1 ...
##  $ c4recod                : num [1:515] 1 1 1 1 1 1 1 0 0 1 ...
##  $ c5recod                : num [1:515] 1 0 1 1 0 1 0 0 0 0 ...
##  $ c6recod                : num [1:515] 1 1 0 1 1 0 1 0 0 1 ...
##  $ c7recod                : num [1:515] 0 0 0 0 0 1 1 0 1 1 ...
##  $ c8recod                : num [1:515] 1 1 1 1 1 1 1 1 1 1 ...
##  $ c9recod                : num [1:515] 1 1 0 1 1 1 1 1 1 1 ...
##  $ c10recod               : num [1:515] 0 1 1 0 1 1 1 1 1 1 ...
##  $ puntaje_C              : num [1:515] 5 8 6 8 8 8 9 5 6 9 ...
##  $ puntaje_P              : num [1:515] 20 5 18 16 5 15 7 12 15 6 ...
# Usamos 'across()' para aplicar la función 'as.factor' a todas las columnas 'where(is.character)'
datos <- datos %>%
  mutate(across(where(is.character), as.factor))

# Verificamos la estructura para confirmar que ahora son factores
str(datos)
## tibble [515 × 92] (S3: tbl_df/tbl/data.frame)
##  $ id                     : num [1:515] 1 2 3 4 5 6 7 8 9 10 ...
##  $ part_prev              : Factor w/ 2 levels "No","Sí": 1 2 1 1 1 1 2 1 2 1 ...
##  $ municipio              : Factor w/ 7 levels "Antiguo Cuscatlán",..: 6 6 6 6 6 6 6 6 6 6 ...
##  $ edad                   : num [1:515] 52 50 27 50 47 47 33 21 37 29 ...
##  $ genero                 : Factor w/ 2 levels "Femenino","Masculino": 1 2 1 2 1 2 1 1 2 2 ...
##  $ nivel_edu              : Factor w/ 7 levels "Bachillerato",..: 2 3 1 6 6 4 1 6 4 1 ...
##  $ enf_cro                : Factor w/ 3 levels "No","No sabe",..: 1 3 1 1 3 3 1 1 3 1 ...
##  $ hipertension           : Factor w/ 2 levels "No","Sí": 1 1 1 1 2 2 1 1 2 1 ...
##  $ diabetes               : Factor w/ 2 levels "No","Sí": 1 2 1 1 1 2 1 1 1 1 ...
##  $ epoc                   : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 1 1 ...
##  $ renal                  : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 1 1 ...
##  $ artritis               : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 1 1 ...
##  $ cardiopatia            : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 1 1 ...
##  $ hipotiroidismo         : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 1 1 ...
##  $ glaucoma               : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 1 1 ...
##  $ lupus                  : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 1 1 ...
##  $ ulcera_venosa          : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 1 1 ...
##  $ ninguna                : logi [1:515] NA NA NA NA NA NA ...
##  $ area                   : Factor w/ 2 levels "Rural","Urbana": 2 1 1 2 2 1 2 1 2 2 ...
##  $ hijxs                  : Factor w/ 2 levels "No","Sí": 2 2 2 1 2 2 2 2 2 2 ...
##  $ nucleo                 : num [1:515] 5 2 5 3 4 5 3 2 3 3 ...
##  $ empleo                 : Factor w/ 4 levels "Desempleo","Empleo",..: 1 2 1 2 2 1 2 1 2 2 ...
##  $ ocupacion              : Factor w/ 11 levels "Agricultura",..: 2 4 2 11 11 2 10 2 4 4 ...
##  $ ingreso                : Factor w/ 5 levels "366 a 730","731 a 1,000",..: 4 5 1 4 5 4 2 4 1 5 ...
##  $ prev_per               : Factor w/ 4 levels "No","No, sabe",..: 1 2 1 1 1 1 1 1 4 4 ...
##  $ rec_apoyo              : Factor w/ 3 levels "No","Sí, del ISSS",..: NA 1 NA NA NA NA NA NA 2 2 ...
##  $ satisfaccion           : Factor w/ 3 levels "Buena","excelente",..: NA NA NA NA NA NA NA NA 1 2 ...
##  $ prev_fam               : Factor w/ 4 levels "No","No sabe",..: 3 1 1 1 1 2 1 2 4 4 ...
##  $ mortalidad             : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 2 1 ...
##  $ c1                     : num [1:515] 3 1 1 1 1 1 1 1 1 1 ...
##  $ c2                     : num [1:515] 2 1 1 1 1 1 1 1 1 1 ...
##  $ c3                     : num [1:515] 3 1 3 1 1 3 1 3 3 1 ...
##  $ c4                     : num [1:515] 1 1 1 1 1 1 1 3 3 1 ...
##  $ c5                     : num [1:515] 1 3 1 1 3 1 3 3 3 2 ...
##  $ c6                     : num [1:515] 1 1 2 1 1 2 1 2 2 1 ...
##  $ c7                     : num [1:515] 3 3 2 2 3 1 1 3 1 1 ...
##  $ c8                     : num [1:515] 1 1 1 1 1 1 1 1 1 1 ...
##  $ c9                     : num [1:515] 1 1 2 1 1 1 1 1 1 1 ...
##  $ c10                    : num [1:515] 2 1 1 2 1 1 1 1 1 1 ...
##  $ a1                     : num [1:515] 5 4 5 5 4 3 4 3 2 5 ...
##  $ a2                     : num [1:515] 5 5 5 5 4 3 4 3 5 5 ...
##  $ a3                     : num [1:515] 1 2 5 5 3 1 4 1 2 4 ...
##  $ a4                     : num [1:515] 5 5 5 5 4 5 4 5 5 4 ...
##  $ a5                     : num [1:515] 1 3 5 1 4 4 4 1 3 4 ...
##  $ p1                     : num [1:515] 5 3 5 5 1 4 3 3 3 2 ...
##  $ p2                     : num [1:515] 2 0 3 3 0 2 0 2 2 0 ...
##  $ p3                     : num [1:515] 3 0 2 3 0 3 0 2 2 0 ...
##  $ p4                     : num [1:515] 5 1 5 3 1 3 1 3 3 1 ...
##  $ p5                     : num [1:515] 5 1 3 2 3 3 3 2 5 3 ...
##  $ dosis                  : num [1:515] 3 3 4 3 2 2 2 1 4 3 ...
##  $ razon_nv               : Factor w/ 7 levels "Razón 1","Razón 10",..: 4 5 1 5 5 5 5 5 1 5 ...
##  $ periodico              : Factor w/ 2 levels "No","Sí": 2 1 1 2 1 2 1 1 2 1 ...
##  $ television             : Factor w/ 2 levels "No","Sí": 2 2 2 2 1 2 2 2 2 1 ...
##  $ redes_sociales         : Factor w/ 2 levels "No","Sí": 1 2 2 1 2 1 2 2 1 2 ...
##  $ personal_salud         : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 1 1 ...
##  $ radio                  : Factor w/ 2 levels "No","Sí": 2 1 2 1 1 2 1 1 2 1 ...
##  $ paginas_internet       : Factor w/ 2 levels "No","Sí": 1 1 2 2 1 1 1 1 1 2 ...
##  $ escuela                : Factor w/ 2 levels "No","Sí": 2 1 1 1 1 1 1 1 1 1 ...
##  $ mensajes               : Factor w/ 3 levels "No","No recuerda",..: 3 3 3 3 3 3 3 3 3 3 ...
##  $ patrocinio             : Factor w/ 5 levels "Asociación Salvadoreña Promotora de la Salud (ASPS)",..: 5 1 4 2 2 2 1 2 1 2 ...
##  $ uso_de_mascarilla      : Factor w/ 2 levels "No","Sí": 2 2 2 2 1 1 1 1 2 2 ...
##  $ distanciamiento_social : Factor w/ 2 levels "No","Sí": 2 1 2 2 2 1 2 1 2 1 ...
##  $ lavado_de_manos        : Factor w/ 2 levels "No","Sí": 1 2 2 1 2 1 2 1 2 2 ...
##  $ ponerse_la_vacuna      : Factor w/ 2 levels "No","Si": 2 1 1 2 2 1 1 1 1 2 ...
##  $ no_recuerdo            : Factor w/ 2 levels "No","Si": 1 1 1 1 1 1 1 2 1 1 ...
##  $ todas                  : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 2 1 1 1 1 ...
##  $ No_he_escuchado        : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 1 1 1 ...
##  $ transmite_por_el_agua  : Factor w/ 2 levels "No","Si": 2 1 1 2 1 2 1 2 2 1 ...
##  $ transmite_en_las_suelas: Factor w/ 2 levels "No","Sí": 2 1 1 2 1 2 1 2 2 1 ...
##  $ rociar_desinfectantes  : Factor w/ 2 levels "No","Sí": 2 1 2 2 1 2 1 2 2 1 ...
##  $ beben_alcohol          : Factor w/ 2 levels "No","Sí": 1 1 1 2 1 1 1 1 1 1 ...
##  $ pueden_dejar_de_aplicar: Factor w/ 2 levels "No","Sí": 2 1 1 1 1 1 1 2 1 1 ...
##  $ no_se_debe_vacunar     : Factor w/ 2 levels "No","Sí": 1 1 2 2 2 1 2 1 1 2 ...
##  $ producen_esterilidad   : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 2 1 1 1 ...
##  $ producen_homosexualidad: Factor w/ 2 levels "No","Sí": 1 2 1 1 1 1 1 1 1 1 ...
##  $ inyectan_el_virus      : Factor w/ 2 levels "No","Sí": 1 1 2 2 1 2 1 1 1 1 ...
##  $ antibioticos_son_buenos: Factor w/ 2 levels "No","Sí": 1 1 1 2 1 2 1 2 2 1 ...
##  $ vitaminas_y_minerales  : Factor w/ 2 levels "No","Sí": 1 1 1 1 1 1 1 2 2 1 ...
##  $ entrevistadoras        : num [1:515] 2 1 2 2 1 2 1 2 2 1 ...
##  $ puntaje_A              : num [1:515] 17 19 25 21 19 16 20 13 17 22 ...
##  $ c1recod                : num [1:515] 0 1 1 1 1 1 1 1 1 1 ...
##  $ c2recod                : num [1:515] 0 1 1 1 1 1 1 1 1 1 ...
##  $ c3recod                : num [1:515] 0 1 0 1 1 0 1 0 0 1 ...
##  $ c4recod                : num [1:515] 1 1 1 1 1 1 1 0 0 1 ...
##  $ c5recod                : num [1:515] 1 0 1 1 0 1 0 0 0 0 ...
##  $ c6recod                : num [1:515] 1 1 0 1 1 0 1 0 0 1 ...
##  $ c7recod                : num [1:515] 0 0 0 0 0 1 1 0 1 1 ...
##  $ c8recod                : num [1:515] 1 1 1 1 1 1 1 1 1 1 ...
##  $ c9recod                : num [1:515] 1 1 0 1 1 1 1 1 1 1 ...
##  $ c10recod               : num [1:515] 0 1 1 0 1 1 1 1 1 1 ...
##  $ puntaje_C              : num [1:515] 5 8 6 8 8 8 9 5 6 9 ...
##  $ puntaje_P              : num [1:515] 20 5 18 16 5 15 7 12 15 6 ...

2. Distribuciones de Frecuencias

Una distribución de frecuencias nos muestra cuántas veces ocurre cada valor de una variable en nuestro conjunto de datos.

Frecuencia Absoluta

Es el número de veces que aparece un determinado valor. Usamos la función table() para calcularla.

# Frecuencia absoluta para la variable 'nivel_edu'
frec_abs <- table(datos$nivel_edu)
print(frec_abs)
## 
##  Bachillerato Nunca asistió  Primer ciclo Segundo ciclo       Técnico 
##           185            22            48            81            20 
##  Tercer ciclo Universitario 
##           117            42

Para crear una tabla más limpia y con encabezados personalizados podemos utilizar la función kabledel paquete knitr

# 1. Crear la tabla de frecuencias como antes
frec_abs <- table(datos$nivel_edu)

# 2. Convertir el objeto 'table' a un dataframe para que kable() lo maneje mejor
tabla_frec_df <- as.data.frame(frec_abs)

# 3. Usar kable() para presentar la tabla
knitr::kable(
  tabla_frec_df,
  # 4. (Opcional pero recomendado) Personalizar los nombres de las columnas
  col.names = c("Nivel Educativo", "Frecuencia Absoluta"),
  # 5. (Opcional) Añadir un título a la tabla
  caption = "Distribucion de Frecuencias del Nivel Educativo"
)
Distribucion de Frecuencias del Nivel Educativo
Nivel Educativo Frecuencia Absoluta
Bachillerato 185
Nunca asistió 22
Primer ciclo 48
Segundo ciclo 81
Técnico 20
Tercer ciclo 117
Universitario 42

Frecuencia Relativa

Es la proporción de veces que aparece cada valor. Se calcula dividiendo la frecuencia absoluta por el total de datos. Usamos prop.table().

# Frecuencia relativa (proporción)
frec_rel <- prop.table(frec_abs)
print(frec_rel)
## 
##  Bachillerato Nunca asistió  Primer ciclo Segundo ciclo       Técnico 
##    0.35922330    0.04271845    0.09320388    0.15728155    0.03883495 
##  Tercer ciclo Universitario 
##    0.22718447    0.08155340
# Frecuencia relativa (porcentaje)
frec_rel_pct <- frec_rel * 100
print(frec_rel_pct)
## 
##  Bachillerato Nunca asistió  Primer ciclo Segundo ciclo       Técnico 
##     35.922330      4.271845      9.320388     15.728155      3.883495 
##  Tercer ciclo Universitario 
##     22.718447      8.155340

Con el siguiente código calcularemos las frecuencias absolutas, luego las usaremos para obtener las relativas y porcentuales, y finalmente presentaremos todo en una tabla bien formateada.

# Usando dplyr para calcular todas las frecuencias en un solo flujo
tabla_relativa <- datos %>%
    count(nivel_edu, name = "Frecuencia_Absoluta") %>% # Cuenta las frecuencias absolutas para 'nivel_edu'
    mutate(
    Frecuencia_Relativa = Frecuencia_Absoluta / sum(Frecuencia_Absoluta),
    Porcentaje = Frecuencia_Relativa * 100
  )  # Calcula las frecuencias relativas y porcentuales
kable(
  tabla_relativa,# Presentar la tabla final con kable()
  col.names = c("Nivel Educativo", "Frec. Absoluta", "Frec. Relativa", "Porcentaje (%)"),# Personalizar los nombres de las columnas para la presentación
  caption = "Distribucion de Frecuencias Relativas y Porcentuales del Nivel Educativo", # Añadir un título a la tabla
  digits = c(0, 0, 2, 1) # 0 decimales para Frec. Absoluta, 2 para Relativa, 1 para Porcentaje
)
Distribucion de Frecuencias Relativas y Porcentuales del Nivel Educativo
Nivel Educativo Frec. Absoluta Frec. Relativa Porcentaje (%)
Bachillerato 185 0.36 35.9
Nunca asistió 22 0.04 4.3
Primer ciclo 48 0.09 9.3
Segundo ciclo 81 0.16 15.7
Técnico 20 0.04 3.9
Tercer ciclo 117 0.23 22.7
Universitario 42 0.08 8.2

Frecuencia Relativa Acumulada

Es la suma de las frecuencias relativas de los valores menores o iguales al valor actual. Usamos cumsum() sobre la frecuencia relativa.

# Frecuencia relativa acumulada
frec_acum <- cumsum(frec_rel)
print(frec_acum)
##  Bachillerato Nunca asistió  Primer ciclo Segundo ciclo       Técnico 
##     0.3592233     0.4019417     0.4951456     0.6524272     0.6912621 
##  Tercer ciclo Universitario 
##     0.9184466     1.0000000

Tabla de Frecuencias Completa

Podemos unir todo en una sola tabla para una mejor visualización.

# Unimos todo en un data.frame
tabla_frecuencias <- as.data.frame(frec_abs)
names(tabla_frecuencias) <- c("Nivel_Educativo", "Frec_Absoluta")

tabla_frecuencias <- tabla_frecuencias %>%
  mutate(
    Frec_Relativa = prop.table(Frec_Absoluta),
    Porcentaje = Frec_Relativa * 100,
    Frec_Acumulada = cumsum(Frec_Relativa),
    Porecntaje_Acum = Frec_Acumulada * 100
  )

# Mostramos la tabla con kable() para un formato limpio
kable(tabla_frecuencias, 
      caption = "Tabla de Frecuencias para Nivel Educativo",
      digits = 2) # Redondeamos a 2 decimales
Tabla de Frecuencias para Nivel Educativo
Nivel_Educativo Frec_Absoluta Frec_Relativa Porcentaje Frec_Acumulada Porecntaje_Acum
Bachillerato 185 0.36 35.92 0.36 35.92
Nunca asistió 22 0.04 4.27 0.40 40.19
Primer ciclo 48 0.09 9.32 0.50 49.51
Segundo ciclo 81 0.16 15.73 0.65 65.24
Técnico 20 0.04 3.88 0.69 69.13
Tercer ciclo 117 0.23 22.72 0.92 91.84
Universitario 42 0.08 8.16 1.00 100.00

3. Tablas de Contingencia

Las tablas de contingencia o tabulaciones cruzadas nos permiten analizar la relación entre dos o más variables categóricas.

Crear una tabla cruzada de genero vrs enfcro con la función table(), el formato es table(filas, columnas). Para que el género esté en las columnas lo pondremos como segundo argumento.

# Crea la tabla de contingencia. 
# La primera variable (enf_cro) va en las filas, la segunda (genero) en las columnas.
tabla_cruzada <- table(datos$enf_cro, datos$genero)

# Muestra la tabla con kable() para una presentación limpia
knitr::kable(
  tabla_cruzada,
  caption = "Tabla de Contingencia: Padecimiento de Enfermedad Crónica vs. Género"
)
Tabla de Contingencia: Padecimiento de Enfermedad Crónica vs. Género
Femenino Masculino
No 210 136
No sabe 1 2
80 86

Esta tabla te muestra el conteo absoluto de cuántas personas caen en cada combinación de las categorías (ej. cuántos hombres tienen una enfermedad crónica).

A menudo es más útil ver las proporciones o porcentajes. Puedes calcularlos fácilmente con la función prop.table().

Porcentajes por Fila

Esto responde a la pregunta: “Del total de personas que SÍ padecen una enfermedad crónica, ¿qué porcentaje son hombres y qué porcentaje son mujeres?”.

# Crear la tabla de frecuencias absolutas
tabla_cruzada_percent <- table(datos$enf_cro, datos$genero)

# Calcular los porcentajes por fila (margin = 1) y redondear
# Multiplicamos por 100 para obtener el porcentaje.
tabla_porcentajes_fila <- round(prop.table(tabla_cruzada, margin = 1) * 100, 1)

# Mostrar la tabla de porcentajes con kable()
knitr::kable(
  tabla_porcentajes_fila,
  caption = "Distribución Porcentual por Fila: Enfermedad Crónica vs. Género (%)"
)
Distribución Porcentual por Fila: Enfermedad Crónica vs. Género (%)
Femenino Masculino
No 60.7 39.3
No sabe 33.3 66.7
48.2 51.8

Para calcular porcentajes por columna, simplemente cambia el argumento a margin = 2. Esto respondería a la pregunta: “Del total de hombres, ¿qué porcentaje padece una enfermedad crónica?”.

Puntajes (categorizados) vs. Promedio de Dosis

Primero, vamos a categorizar las variables de puntajes en tres niveles: “Bajo” (<6), “Medio” (6 a 8), y “Alto” (>8). Luego calcularemos el promedio de dosis de vacuna para cada categoría.

# Categorizar los puntajes usando la función cut()
datos <- datos %>%
  mutate(across(
    .cols = c(puntaje_C, puntaje_A, puntaje_P),
    .fns = ~ cut(
      .x,
      breaks = quantile(.x, probs = c(0, 0.25, 0.75, 1), na.rm = TRUE),
      labels = c("Bajo", "Medio", "Alto"),
      include.lowest = TRUE,
      ordered_result = TRUE
    ),
    .names = "cat_{.col}" # Crea nuevas columnas: cat_puntaje_C, cat_puntaje_A, etc.
  ))

# --- Verificación ---
# Veamos cómo quedaron distribuidas las nuevas categorías para 'puntaje_C'
# (Notarás que 'Bajo' tiene aprox. el 50% de los datos)
tabla_verificacion <- table(datos$cat_puntaje_C)

kable(tabla_verificacion, col.names = c("Categoría de Conocimiento", "Frecuencia"))
Categoría de Conocimiento Frecuencia
Bajo 174
Medio 321
Alto 20
# Calcular el promedio de dosis para cada categoría de puntaje de CONOCIMIENTOS
promedio_dosis_C <- datos %>%
  group_by(cat_puntaje_C) %>%
  summarise(Promedio_Dosis = mean(dosis, na.rm = TRUE))

kable(promedio_dosis_C, 
      caption = "Promedio de Dosis de Vacuna por Nivel de Conocimiento",
      digits = 2)
Promedio de Dosis de Vacuna por Nivel de Conocimiento
cat_puntaje_C Promedio_Dosis
Bajo 2.29
Medio 2.44
Alto 1.60
# Podemos hacer lo mismo para Actitudes y Prácticas
promedio_dosis_A <- datos %>% group_by(cat_puntaje_A) %>% summarise(Promedio_Dosis = mean(dosis, na.rm = TRUE))
promedio_dosis_P <- datos %>% group_by(cat_puntaje_P) %>% summarise(Promedio_Dosis = mean(dosis, na.rm = TRUE))

kable(promedio_dosis_A, caption = "Promedio de Dosis de Vacuna por Nivel de Actitud", digits = 2)
Promedio de Dosis de Vacuna por Nivel de Actitud
cat_puntaje_A Promedio_Dosis
Bajo 1.87
Medio 2.38
Alto 2.95
kable(promedio_dosis_P, caption = "Promedio de Dosis de Vacuna por Nivel de Práctica", digits = 2)
Promedio de Dosis de Vacuna por Nivel de Práctica
cat_puntaje_P Promedio_Dosis
Bajo 2.26
Medio 2.23
Alto 2.71

Recepción de Mensajes vs. Promedio de Puntajes

str(datos$mensajes)

# Dicotomizar la variable 'mensajes' en "Sí = Sí" y "No = No/No recuerda".
datos <- datos %>%
  mutate(
    mensajes_dicot = factor(
      ifelse(mensajes == "Sí", "Sí", "No/No recuerda")
    )
  )

# --- Verificación ---
# Esta tabla cruzada te mostrará de la variable mensajes dicotomizada.
kable(
  table(
    `Original (mensajes)` = datos$mensajes,
    `Nueva (mensajes_dicot)` = datos$mensajes_dicot
  ),
  caption = "Verificación de la Recodificación"
)
Verificación de la Recodificación
No/No recuerda
No 182 0
No recuerda 92 0
0 241
# Verificar con tabla de frecuencias absolutas
table(datos$mensajes_dicot)
## 
## No/No recuerda             Sí 
##            274            241
# Calcular el promedio de puntajes para cada categoría de 'mensajes_dic'
promedio_puntajes <- datos %>%
  group_by(mensajes_dicot) %>%
  summarise(
    Promedio_Conocimiento = mean(puntaje_C, na.rm = TRUE),
    Promedio_Actitud = mean(puntaje_A, na.rm = TRUE),
    Promedio_Practica = mean(puntaje_P, na.rm = TRUE)
  )

kable(promedio_puntajes, 
      caption = "Promedio de Puntajes según Exposición a Mensajes educativos",
      digits = 2)
Promedio de Puntajes según Exposición a Mensajes educativos
mensajes_dicot Promedio_Conocimiento Promedio_Actitud Promedio_Practica
No/No recuerda 7.43 16.08 9.26
6.79 17.68 14.28

Ahora, vamos a identificar el promedio de conocimientos según exposición a mensajes educativos en los últimos 3 meses.

# Calcular el promedio de puntajes para cada categoría de 'mensajes_dicot'
promedio_puntajes <- datos %>%
  group_by(mensajes_dicot) %>%
  summarise(
    Promedio_Conocimiento = mean(puntaje_C, na.rm = TRUE),
    Promedio_Actitud = mean(puntaje_A, na.rm = TRUE),
    Promedio_Practica = mean(puntaje_P, na.rm = TRUE)
  )

kable(promedio_puntajes, 
      caption = "Promedio de Puntajes según Recepción de Mensajes",
      digits = 2)
Promedio de Puntajes según Recepción de Mensajes
mensajes_dicot Promedio_Conocimiento Promedio_Actitud Promedio_Practica
No/No recuerda 7.43 16.08 9.26
6.79 17.68 14.28

4. Visualizaciones Gráficas

Las gráficas son una herramienta poderosa para entender patrones y relaciones en los datos. Usaremos el paquete ggplot2.

Gráfica de Barras

Ideal para visualizar la frecuencia de variables categóricas.

# Opciones globales para los chunks de código
ggplot(datos, aes(x = area, fill = genero)) +
  geom_bar(position = "dodge") +
  labs(title = "Distribución de Participantes por Área de Residencia y Género",
       x = "Área de Residencia",
       y = "Frecuencia") +
  theme_minimal()

## Histograma Muestra la distribución de una variable numérica continua.

# Opciones globales para los chunks de código
ggplot(datos, aes(x = edad)) +
  geom_histogram(binwidth = 5, fill = "skyblue", color = "black") +
  labs(title = "Distribución de Edades",
       x = "Edad (años)",
       y = "Frecuencia") +
  theme_minimal()

Polígono de Frecuencias

Similar al histograma, pero usa líneas en lugar de barras. Podemos superponerlo.

ggplot(datos, aes(x = edad, color = genero)) +
  geom_freqpoly(binwidth = 5) +
  labs(title = "Distribución de Edades por Género",
       x = "Edad (años)",
       y = "Frecuencia") +
  theme_minimal()

Gráfico de Puntos (Dot Plot)

Útil para visualizar la distribución de una variable numérica para diferentes categorías.

ggplot(datos, aes(x = nivel_edu, y = puntaje_C, fill = nivel_edu)) +
  geom_dotplot(binaxis = "y", stackdir = "center", dotsize = 0.8) +
  labs(title = "Puntaje de Conocimientos por Nivel Educativo",
       x = "Nivel Educativo",
       y = "Puntaje de Conocimientos") +
  theme_minimal() +
  theme(legend.position = "none") # Ocultamos la leyenda porque es redundante

Gráfico de Caja (Box Plot)

Excelente para comparar distribuciones de una variable numérica entre varios grupos.

ggplot(datos, aes(x = nivel_edu, y = puntaje_A, fill = nivel_edu)) +
  geom_boxplot() +
  labs(title = "Distribución del Puntaje de Actitudes por Nivel Educativo",
       x = "Nivel Educativo",
       y = "Puntaje de Actitudes") +
  theme_minimal() +
  theme(legend.position = "none")

Gráfico de Dispersión

Muestra la relación entre dos variables numéricas.

ggplot(datos, aes(x = puntaje_A, y = puntaje_P)) +
  geom_point(aes(color = area), alpha = 0.7) +
  geom_smooth(method = "lm", color = "red", se = FALSE) + # Añade una línea de tendencia
  labs(title = "Relación entre Puntaje de Actitudes y Prácticas",
       x = "Puntaje de Actitudes",
       y = "Puntaje de Prácticas") +
  theme_minimal()

Gráfico de Líneas

Es ideal para mostrar la evolución de una variable a lo largo del tiempo. Como no tenemos una variable de tiempo, crearemos un ejemplo simple que muestre el promedio de edad por número de dosis.

# Calcular el promedio de edad para cada número de dosis
datos_linea <- datos %>%
  group_by(dosis) %>%
  summarise(Edad_Promedio = mean(edad, na.rm = TRUE))

ggplot(datos_linea, aes(x = dosis, y = Edad_Promedio)) +
  geom_line(color = "navy") +
  geom_point(color = "navy", size = 3) +
  labs(title = "Edad Promedio según Dosis de Vacuna Recibidas",
       x = "Número de Dosis",
       y = "Edad Promedio (años)") +
  theme_minimal()

5. Análisis bivariado

Comparemos el puntaje de conocimientos según si las personas fueron expuestas a mensajes educativos o no (variable mensajes_dicot)

# Crear los histogramas comparativos
# Mapeamos la variable puntaje_C al eje x.Usamos facet_wrap(~ mensajes_recod, ncol = 1) para crear un histograma para cada categoría de la variable mensajes_recod y apilarlos uno debajo del otro.
ggplot(datos, aes(x = puntaje_C)) +
  geom_histogram(binwidth = 1, fill = "steelblue", color = "white", alpha = 0.8) +
  facet_wrap(~ mensajes_dicot, ncol = 1) +
  labs(
    title = "Comparación de la Distribución del Puntaje de Conocimientos",
    subtitle = "Según si el participante escuchó o no mensajes educativos",
    x = "Puntaje de Conocimientos (puntaje_C)",
    y = "Frecuencia (Conteo)"
  ) +
  theme_bw() # Usamos un tema diferente para variar

Ahora cambiemos por un gráfico de cajas

# Mapear las variables a los ejes
#    - x: La variable categórica para los grupos (mensajes_recod).
#    - y: La variable numérica cuya distribución queremos ver (puntaje_C).
#    - fill: Coloreamos las cajas según el grupo para mejorar la estética.
ggplot(datos, aes(x = mensajes_dicot, y = puntaje_C, fill = mensajes_dicot)) +
  geom_boxplot(alpha = 0.7) + # 2. Añadir la geometría del boxplot
   #geom_jitter(width = 0.2, alpha = 0.4) + # (Opcional) Añadir los puntos de datos reales con 
                                           #algo de dispersión (jitter). Esto ayuda a ver la 
                                           #distribución real de los datos.
  labs(
    title = "Comparación del Puntaje de Conocimientos",
    subtitle = "Según si el participante recibió o no mensajes educativos",
    x = "Recibió Mensajes Educativos",
    y = "Puntaje de Conocimientos"
  ) + # Añadir etiquetas y títulos claros
  theme_minimal() +
  theme(legend.position = "none") # Aplicar un tema y remover la leyenda (es redundante)

6. Ahora te toca a tí!

Reunidos en grupos, deberán elaborar la presentación de resultados del estudio Conocimientos, Actitudes y Prácticas hacia las medidas preventivas de COVID-19, siguiendo el protocolo del estudio que les fue compartido. El análisis descriptivo deberá contener una tabla, una gráfica y un párrafo describiendo los datos.

Fecha de entrega: domingo 21 de septiembre a más tardar a medianoche.