Este documento constituye una guía metodológica y técnica orientada a la organización, tabulación y representación visual de datos en investigación biomédica y clínica. El diseño instruccional del tema vincula de forma sinérgica la teoría estadística descriptiva, la implementación computacional en el entorno RStudio y el compromiso con la integridad ética en la comunicación de resultados científicos.
Comprender los fundamentos teóricos y las metodologías de organización de datos mediante distribuciones de frecuencias (simples y agrupadas), dominando el cálculo técnico y consistencia lógica de los intervalos de clase, y discriminar con rigor científico la estructura o herramienta gráfica óptima según la escala de medición de la variable bajo estudio.
Desarrollar una sólida honestidad intelectual y precisión ética en la representación visual de datos, rechazando de manera sistemática deformaciones, omisiones o adornos que sesguen la interpretación de la evidencia en salud, valorando la reproducibilidad del código como pilar de la transparencia científica.
La organización de datos es la base de la estadística descriptiva. Consiste en condensar información caótica en estructuras ordenadas denominadas tablas o distribuciones de frecuencias.
Sea un conjunto de datos con un tamaño de muestra \(n\):
Se emplean para variables cualitativas (nominales u ordinales) o cuantitativas discretas con un rango estrecho de valores distintos. Cada categoría única se lista de forma individual con sus respectivas frecuencias.
Se requieren para variables cuantitativas continuas (ej. peso corporal, presión arterial, creatinina sérica) que asumen muchos valores distintos. El procedimiento matemático sistemático para agrupar datos incluye:
El rango es la distancia total entre la observación más grande (\(X_{\text{máx}}\)) y la más pequeña (\(X_{\text{mín}}\)): \[R = X_{\text{máx}} - X_{\text{mín}}\]
Existen diversos criterios para decidir la cantidad de clases. Una de las aproximaciones metodológicas más empleadas es la Regla de Sturges: \[k = 1 + 3.322 \log_{10}(n)\] El valor obtenido se aproxima al entero inmediato superior o inferior buscando la conveniencia analítica y la legibilidad.
Consiste en dividir el rango entre el número de clases estimadas: \[A = \frac{R}{k}\]
Los intervalos deben ser construidos bajo dos reglas lógicas fundamentales: 1. Exhaustivos: Deben cubrir la totalidad de las observaciones (desde el mínimo hasta el máximo). 2. Mutuamente Excluyentes: Una observación individual debe pertenecer a un único intervalo. Para evitar ambigüedades en las fronteras de clase (ej. si una clase termina en 30 y la siguiente inicia en 30, ¿dónde cae exactamente el valor 30?), se definen límites de clase reales o continuos utilizando decimales (ej. [20.5, 30.5), [30.5, 40.5)) o aplicando notación matemática de intervalos semiabiertos.
La elección del gráfico no es una decisión estética; es una decisión metodológica que está supeditada estrictamente a la escala de medición de la variable.
Histograma (Histogram): Es un gráfico de rectángulos verticales adyacentes específicos para representar distribuciones de frecuencias agrupadas. A diferencia de las barras cualitativas, en el histograma las barras se dibujan juntas, sin espacios de separación (adyacentes). El ancho del rectángulo representa la amplitud de la clase y su altura corresponde a la frecuencia o densidad. Refleja la naturaleza continua de la variable subyacente y permite caracterizar visualmente la simetría y dispersión de la muestra.
Diagrama de Caja y Bigotes (Boxplot): Es un dispositivo visual altamente descriptivo basado en el resumen de cinco números:
Polígono de Frecuencias (Frequency Polygon): Gráfico de líneas trazado sobre los puntos medios (marcas de clase) de las barras de un histograma. Es útil para superponer y comparar la distribución de dos o más poblaciones en un mismo plano espacial sin la interferencia visual de las barras sólidas.
Función de Distribución Acumulativa Empírica (ECDF): Grafica el porcentaje acumulado (\(P_i\)) en el eje de las ordenadas contra los valores de la variable en las abscisas. Muestra directamente la proporción de sujetos que se encuentran por debajo de un umbral específico.
La honestidad intelectual en la comunicación biomédica exige representar los datos de manera que reflejen la realidad sin generar sesgos cognitivos o falsas impresiones. Un gráfico distorsionado puede inducir a errores en diagnósticos epidemiológicos, exageración en la eficacia de fármacos o alarmismo innecesario.
En gráficos de magnitudes directas (como diagramas de barras), el eje vertical debe iniciar estrictamente en cero. Truncar el eje para iniciar cerca de la variación mínima de los datos exagera geométricamente diferencias insignificantes, induciendo a error al lector.
Nota Metodológica: El gran estadístico John Tukey señalaba que, en fases de Análisis Exploratorio de Datos (EDA), omitir el cero basal puede ser admisible para magnificar y analizar pequeños residuos o patrones sutiles. Sin embargo, en reportes de difusión científica final o publicaciones, esto constituye una violación ética a menos que esté debidamente señalada la escala y justificada en variables de intervalo específicas (como la temperatura en grados Celsius).
Los efectos 3D, sombras o cilindros en diagramas bidimensionales actúan como ruido visual decorativo (chartjunk). La perspectiva tridimensional altera las proporciones reales de las áreas y de las alturas de las barras, haciendo que el lector no pueda correlacionar de forma visualmente honesta la barra con el eje vertical de medida.
Sustituir barras por figuras (como jeringas, personas u hospitales) y escalarlas proporcionalmente al dato es una distorsión grave. Si el dato se duplica y el diseñador duplica el alto y ancho de la imagen, el área total de la figura se cuadruplica (\(2^2\)), y en el caso de figuras volumétricas se octuplica (\(2^3\)), distorsionando de forma no lineal la percepción de la magnitud real.
Variar de forma arbitraria o selectiva el ancho de los intervalos de clase o sus límites permite al investigador ocultar valles, asimetrías o fabricar falsas normalidades estéticas para sesgar deliberadamente la interpretación teórica.
El procesamiento y la visualización descriptiva reproducible se
realizarán mediante RStudio utilizando el motor gráfico contemporáneo
ggplot2 (basado en la gramática de gráficos) y herramientas
complementarias de tabulación.
ggplot2: Permite graficar capa por
capa de forma estructurada, garantizando la exactitud matemática de las
escalas y la exclusión de sombras o distorsiones 3D.dplyr: Fundamental para la
manipulación, selección y transformación de datos usando el operador
pipe (%>%).gtsummary: Proporciona funciones
avanzadas para imprimir resúmenes descriptivos con formato profesional
directamente exportable.descr: Proporciona descriptivos
rápidos de frecuencias absolutas y acumuladas.A continuación se modela la sintaxis que ejecutaremos en la sesión de laboratorio. Para esta práctica, utilizaremos el conjunto de datos limpios proveniente del tutorial 1.
# Carga de la base de datos adjunta
datos <- read.csv("datos_clinicos_limpios.csv")
# Conversión de las variables de texto a factores (categorías) para el análisis
datos$sexo_limpio <- factor(datos$sexo_limpio)
datos$uso_redes_noche <- factor(datos$uso_redes_noche,
levels = c("Nunca", "A veces", "Frecuentemente", "Siempre"),
ordered = TRUE)
# Estructura del dataset para verificar tipos de variables
str(datos)## 'data.frame': 30 obs. of 6 variables:
## $ id_paciente : int 2 3 4 5 7 8 9 10 13 14 ...
## $ edad : int 18 23 21 22 19 25 20 18 19 19 ...
## $ sexo_limpio : Factor w/ 2 levels "Femenino","Masculino": 2 2 1 2 2 1 1 2 2 1 ...
## $ nivel_estres : int 9 3 10 2 2 10 10 8 6 3 ...
## $ horas_sueno : int 8 3 7 8 8 4 4 4 3 9 ...
## $ uso_redes_noche: Ord.factor w/ 4 levels "Nunca"<"A veces"<..: 3 1 2 2 NA 3 NA 4 2 2 ...
##
## Femenino Masculino
## 17 13
##
## Femenino Masculino
## 0.5666667 0.4333333
##
## Femenino Masculino
## 56.66667 43.33333
# Cruce del Uso Nocturno de Redes Sociales según el Sexo
tabla_cruce <- table(datos$uso_redes_noche, datos$sexo_limpio)
addmargins(tabla_cruce) # Añade totales marginales##
## Femenino Masculino Sum
## Nunca 2 5 7
## A veces 5 3 8
## Frecuentemente 5 2 7
## Siempre 1 2 3
## Sum 13 12 25
Estructuraremos la variable edad determinando sus intervalos bajo la Regla de Sturges.
# Usamos solo los casos sin NA en edad para el cálculo
edades_validas <- na.omit(datos$edad)
n <- length(edades_validas)
# Sturges: k = 1 + 3.322 * log10(n)
k_sturges <- round(1 + 3.322 * log10(n))
min_edad <- min(edades_validas)
max_edad <- max(edades_validas)
rango_edad <- max_edad - min_edad
amplitud_edad <- ceiling(rango_edad / k_sturges)
# Generar puntos de corte para intervalos semiabiertos
cortes_edad <- seq(min_edad - 1, min_edad - 1 + (k_sturges * amplitud_edad),
by = amplitud_edad)
# Agrupación con cut()
datos$Edad_Grupo <- cut(datos$edad, breaks = cortes_edad, right = FALSE)
# Tabulación utilizando paquete 'descr'
descr::freq(datos$Edad_Grupo, plot = FALSE)## datos$Edad_Grupo
## Frequency Percent
## [17,19) 4 13.33
## [19,21) 10 33.33
## [21,23) 8 26.67
## [23,25) 5 16.67
## [25,27) 3 10.00
## [27,29) 0 0.00
## Total 30 100.00
gtsummary)# gtsummary y dplyr ya deben estar cargados
datos %>%
select(sexo_limpio, uso_redes_noche, edad, horas_sueno, nivel_estres) %>%
tbl_summary(
by = uso_redes_noche, # Agrupamos por uso de redes sociales
statistic = list(
all_continuous() ~ "{mean} (± {sd})",
all_categorical() ~ "{n} ({p}%)"
),
missing_text = "(Datos faltantes)"
) %>%
add_overall() %>%
bold_labels()| Characteristic | Overall N = 251 |
Nunca N = 71 |
A veces N = 81 |
Frecuentemente N = 71 |
Siempre N = 31 |
|---|---|---|---|---|---|
| sexo_limpio | |||||
| Femenino | 13 (52%) | 2 (29%) | 5 (63%) | 5 (71%) | 1 (33%) |
| Masculino | 12 (48%) | 5 (71%) | 3 (38%) | 2 (29%) | 2 (67%) |
| edad | |||||
| 18 | 3 (12%) | 0 (0%) | 0 (0%) | 2 (29%) | 1 (33%) |
| 19 | 6 (24%) | 1 (14%) | 4 (50%) | 0 (0%) | 1 (33%) |
| 20 | 2 (8.0%) | 0 (0%) | 1 (13%) | 0 (0%) | 1 (33%) |
| 21 | 4 (16%) | 1 (14%) | 1 (13%) | 2 (29%) | 0 (0%) |
| 22 | 4 (16%) | 2 (29%) | 1 (13%) | 1 (14%) | 0 (0%) |
| 23 | 2 (8.0%) | 2 (29%) | 0 (0%) | 0 (0%) | 0 (0%) |
| 24 | 2 (8.0%) | 1 (14%) | 1 (13%) | 0 (0%) | 0 (0%) |
| 25 | 2 (8.0%) | 0 (0%) | 0 (0%) | 2 (29%) | 0 (0%) |
| horas_sueno | |||||
| 3 | 3 (12%) | 2 (29%) | 1 (13%) | 0 (0%) | 0 (0%) |
| 4 | 3 (12%) | 1 (14%) | 0 (0%) | 1 (14%) | 1 (33%) |
| 5 | 4 (16%) | 0 (0%) | 2 (25%) | 2 (29%) | 0 (0%) |
| 6 | 1 (4.0%) | 0 (0%) | 1 (13%) | 0 (0%) | 0 (0%) |
| 7 | 5 (20%) | 2 (29%) | 2 (25%) | 0 (0%) | 1 (33%) |
| 8 | 4 (16%) | 2 (29%) | 1 (13%) | 1 (14%) | 0 (0%) |
| 9 | 5 (20%) | 0 (0%) | 1 (13%) | 3 (43%) | 1 (33%) |
| nivel_estres | 5 (± 3) | 5 (± 2) | 5 (± 3) | 6 (± 4) | 6 (± 2) |
| 1 n (%); Mean (± SD) | |||||
ggplot2 (Variable Cualitativa Ordinal)Las barras van separadas para denotar categorías cualitativas
discretas. Usaremos la función subset para evitar graficar
los valores NA de las redes.
ggplot(subset(datos, !is.na(uso_redes_noche)), aes(x = uso_redes_noche, fill = uso_redes_noche)) +
geom_bar(color = "black", width = 0.7) +
scale_fill_brewer(palette = "Blues", name = "Frecuencia de Uso") +
labs(
title = "Distribución de Pacientes según Uso Nocturno de Redes Sociales",
subtitle = "Base de datos clínica depurada",
x = "Uso de Redes Sociales en la Noche (Escala Ordinal)",
y = "Frecuencia Absoluta (Número de Estudiantes/Pacientes)"
) +
theme_minimal() +
theme(legend.position = "none") # Evita redundancia visualggplot2 (Variable Cuantitativa Continua)Las barras se dibujan adyacentes para denotar continuidad matemática.
ggplot(datos, aes(x = edad)) +
geom_histogram(breaks = cortes_edad, color = "black", fill = "#3182bd") +
labs(
title = "Distribución por Grupos de Edad en la Muestra",
subtitle = paste("Histograma de Frecuencias con intervalos de Sturges (Amplitud =", amplitud_edad, "años)"),
x = "Edad del Paciente (Años)",
y = "Frecuencia Absoluta"
) +
scale_x_continuous(breaks = cortes_edad) +
theme_classic()ggplot2Útil para comparar la variabilidad de una variable continua según grupos cualitativos. En este caso cruzaremos el Nivel de Estrés por Sexo.
ggplot(subset(datos, !is.na(sexo_limpio)), aes(x = sexo_limpio, y = nivel_estres, fill = sexo_limpio)) +
geom_boxplot(alpha = 0.7, outlier.color = "red", outlier.size = 2.5) +
geom_jitter(width = 0.15, alpha = 0.3, color = "gray20") + # Superpone puntos de datos reales
scale_fill_manual(values = c("Femenino" = "#e9a3c9", "Masculino" = "#a1d76a")) +
labs(
title = "Comparación de Niveles de Estrés según Sexo",
subtitle = "Diagrama de Caja con superposición de dispersión",
x = "Sexo del Paciente",
y = "Nivel de Estrés (Escala 1 a 10)"
) +
theme_bw() +
theme(legend.position = "none")Objetivo de la actividad: Evaluar tu capacidad de
transferencia del código de R analizando una variable distinta de la
base de datos datos_clinicos_limpios.csv.
En las secciones anteriores analizaste la distribución de
sexo_limpio, uso_redes_noche y
edad. Ahora, tu equipo debe aplicar las mismas funciones de
R pero enfocándose en las Horas de Sueño
(horas_sueno) y su relación con el Nivel
de Estrés (nivel_estres).
Sigue las instrucciones dentro de cada bloque de código y completa las líneas faltantes.
horas_suenoCalcula los intervalos de clase para la variable
horas_sueno mediante la Regla de Sturges y
genera su tabla de frecuencias agrupadas.
# 1. Filtra los valores NA de la variable horas_sueno
sueno_valido <- na.omit(datos$horas_sueno)
n_sueno <- length(sueno_valido)
# 2. Calcula el número de intervalos k según Sturges
# PISTA: Usa la fórmula 1 + 3.322 * log10(n_sueno)
k_sturges_sueno <- round(1 + 3.322 * log10(n_sueno))
# 3. Determina el rango y la amplitud
min_sueno <- min(sueno_valido)
max_sueno <- max(sueno_valido)
amplitud_sueno <- (max_sueno - min_sueno) / k_sturges_sueno
# 4. Genera los puntos de corte con seq()
cortes_sueno <- seq(min_sueno - 0.5,
min_sueno - 0.5 + (k_sturges_sueno * amplitud_sueno) + 1,
by = amplitud_sueno)
# 5. Agrupa la variable original usando cut()
# TU CÓDIGO AQUÍ: Completa la llamada a cut()
datos$Sueno_Grupo <- cut(datos$horas_sueno, breaks = cortes_sueno, right = FALSE)
# 6. Muestra la tabla de frecuencias con descr::freq()
# TU CÓDIGO AQUÍ:
descr::freq(datos$Sueno_Grupo, plot = FALSE)Ejercicio 2: Histograma Continuo de horas_sueno Crea un histograma estilizado con ggplot2 para observar la distribución empírica de las horas de sueño reportadas por los participantes.
# TU CÓDIGO AQUÍ: Modifica el gráfico para que la variable en el eje X sea 'horas_sueno'
# Cambia el color de relleno ('fill') por un tono distinto (ejemplo: "#2b8cbe" o "#31a354")
ggplot(subset(datos, !is.na(horas_sueno)), aes(x = horas_sueno)) +
geom_histogram(breaks = cortes_sueno, color = "black", fill = "#31a354") +
labs(
title = "Distribución de las Horas de Sueño Nocturno",
subtitle = "Histograma de Frecuencias (Intervalos de Sturges)",
x = "Horas de Sueño por Noche",
y = "Número de Estudiantes / Pacientes"
) +
scale_x_continuous(breaks = round(cortes_sueno, 1)) +
theme_classic()Ejercicio 3: Boxplot Bivariado (horas_sueno según
uso_redes_noche) Genera un gráfico de caja y bigotes que
compare las horas de sueño en función de la frecuencia con la que los
estudiantes usan redes sociales por la noche.
# TU CÓDIGO AQUÍ:
# 1. Ajusta los aes(): eje X = uso_redes_noche, eje Y = horas_sueno, fill = uso_redes_noche
# 2. Asegúrate de filtrar los valores NA con subset()
ggplot(subset(datos, !is.na(uso_redes_noche) & !is.na(horas_sueno)),
aes(x = uso_redes_noche, y = horas_sueno, fill = uso_redes_noche)) +
geom_boxplot(alpha = 0.7, outlier.color = "darkred", outlier.size = 2.5) +
geom_jitter(width = 0.15, alpha = 0.3, color = "gray20") +
scale_fill_brewer(palette = "Set2") +
labs(
title = "Comparación de Horas de Sueño según Uso Nocturno de Redes Sociales",
subtitle = "Análisis exploratorio bivariado",
x = "Uso Nocturno de Redes Sociales",
y = "Horas de Sueño Reportadas"
) +
theme_bw() +
theme(legend.position = "none")Preguntas de Reflexión e Interpretación Bioestadística 1. Evaluación de Asimetría : Al observar el histograma de horas_sueno, ¿se aprecia una distribución simétrica o sesgada? ¿Qué implicaciones clínicas o académicas tiene esta forma?
Análisis de Tendencia Bivariada: Al revisar el diagrama de caja del Ejercicio 3, ¿se observa alguna diferencia evidente en la mediana de horas de sueño entre quienes “Nunca” usan redes de noche frente a los que las usan “Siempre”?
Decisión Metodológica: Para reportar las horas_sueno en la Tabla 1 de tu manuscrito, ¿recomendarías usar la Media (± DE) o la Mediana [RIQ]? Justifica tu respuesta con base en la forma del gráfico generado.