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.
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 efectos de reproducibilidad, primero crearemos un conjunto de datos clínicos sintético (\(n = 150\) pacientes).
# Fijar semilla para reproducibilidad de la muestra aleatoria
set.seed(2026)
# Creación de base de datos simulada de pacientes
clinicos <- data.frame(
ID = 1:150,
Sexo = factor(sample(c("Femenino", "Masculino"), 150, replace = TRUE)),
Estadio_Cancer = factor(sample(c("I", "II", "III", "IV"), 150, replace = TRUE,
prob = c(0.4, 0.3, 0.2, 0.1)), ordered = TRUE),
Fumador = factor(sample(c("No Fumador", "Fumador"), 150, replace = TRUE, prob = c(0.7, 0.3))),
Edad = round(rnorm(150, mean = 48, sd = 12)),
Colesterol = round(rlnorm(150, meanlog = 5.3, sdlog = 0.25))
)
# Estructura del dataset
str(clinicos)## 'data.frame': 150 obs. of 6 variables:
## $ ID : int 1 2 3 4 5 6 7 8 9 10 ...
## $ Sexo : Factor w/ 2 levels "Femenino","Masculino": 1 1 1 2 1 1 1 2 2 2 ...
## $ Estadio_Cancer: Ord.factor w/ 4 levels "I"<"II"<"III"<..: 1 1 4 2 3 3 1 2 3 1 ...
## $ Fumador : Factor w/ 2 levels "Fumador","No Fumador": 2 2 2 2 1 2 2 2 1 1 ...
## $ Edad : num 62 41 38 54 56 45 53 54 52 41 ...
## $ Colesterol : num 191 227 143 183 242 208 214 145 227 176 ...
##
## Femenino Masculino
## 84 66
##
## Femenino Masculino
## 0.56 0.44
##
## Femenino Masculino
## 56 44
# Cruce de Estadio de Cáncer según estado de tabaquismo
tabla_cruce <- table(clinicos$Estadio_Cancer, clinicos$Fumador)
addmargins(tabla_cruce) # Añade totales marginales##
## Fumador No Fumador Sum
## I 20 38 58
## II 12 32 44
## III 9 25 34
## IV 2 12 14
## Sum 43 107 150
Estructuraremos la variable Edad determinando sus intervalos bajo Sturges.
n <- nrow(clinicos)
# Sturges: k = 1 + 3.322 * log10(n)
k_sturges <- round(1 + 3.322 * log10(n))
min_edad <- min(clinicos$Edad)
max_edad <- max(clinicos$Edad)
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()
clinicos$Edad_Grupo <- cut(clinicos$Edad, breaks = cortes_edad, right = FALSE)
# Tabulación utilizando paquete 'descr'
descr::freq(clinicos$Edad_Grupo, plot = FALSE)## clinicos$Edad_Grupo
## Frequency Percent
## [19,26) 7 4.667
## [26,33) 5 3.333
## [33,40) 20 13.333
## [40,47) 30 20.000
## [47,54) 31 20.667
## [54,61) 31 20.667
## [61,68) 20 13.333
## [68,75) 6 4.000
## Total 150 100.000
gtsummary)# gtsummary y dplyr ya están cargados desde el setup inicial
clinicos %>%
select(Sexo, Estadio_Cancer, Fumador, Edad, Colesterol) %>%
tbl_summary(
by = Fumador,
statistic = list(
all_continuous() ~ "{mean} (± {sd})",
all_categorical() ~ "{n} ({p}%)"
)
) %>%
add_overall() %>%
bold_labels()| Characteristic | Overall N = 1501 |
Fumador N = 431 |
No Fumador N = 1071 |
|---|---|---|---|
| Sexo | |||
| Femenino | 84 (56%) | 25 (58%) | 59 (55%) |
| Masculino | 66 (44%) | 18 (42%) | 48 (45%) |
| Estadio_Cancer | |||
| I | 58 (39%) | 20 (47%) | 38 (36%) |
| II | 44 (29%) | 12 (28%) | 32 (30%) |
| III | 34 (23%) | 9 (21%) | 25 (23%) |
| IV | 14 (9.3%) | 2 (4.7%) | 12 (11%) |
| Edad | 49 (± 11) | 47 (± 12) | 49 (± 11) |
| Colesterol | 211 (± 60) | 215 (± 66) | 210 (± 57) |
| 1 n (%); Mean (± SD) | |||
ggplot2 (Variable Cualitativa Ordinal)Las barras van separadas para denotar categorías cualitativas discretas.
ggplot(clinicos, aes(x = Estadio_Cancer, fill = Estadio_Cancer)) +
geom_bar(color = "black", width = 0.7) +
scale_fill_brewer(palette = "Blues", name = "Estadio Clínico") +
labs(
title = "Distribución de Pacientes según Estadio de Cáncer",
subtitle = "Muestra simulada, n = 150 pacientes (2026)",
x = "Estadio del Cáncer (Escala Ordinal)",
y = "Frecuencia Absoluta (Número de Pacientes)"
) +
theme_minimal() +
theme(legend.position = "none") # Evita redundancia visualggplot2 (Variable Cuantitativa Continua)Las barras se dibujan adyacentes para denotar continuidad matemática.
ggplot(clinicos, aes(x = Edad)) +
geom_histogram(breaks = cortes_edad, color = "black", fill = "#3182bd") +
labs(
title = "Distribución por Grupos de Edad en Pacientes Clínicos",
subtitle = "Histograma de Frecuencias con intervalos de Sturges (Amplitud = 8 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.
ggplot(clinicos, aes(x = Sexo, y = Colesterol, fill = Sexo)) +
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 Colesterol según Sexo",
subtitle = "Diagrama de Caja con identificación de valores atípicos (en rojo)",
x = "Sexo del Paciente",
y = "Colesterol Sérico (mg/dL)"
) +
theme_bw() +
theme(legend.position = "none")