highcharter es un paquete de R creado por Joshua Kunst que conecta R con Highcharts, una librería de JavaScript para gráficos. El resultado son gráficos interactivos que se ven en el navegador: al pasar el mouse aparece la información del punto, se puede hacer zoom, ocultar series desde la leyenda y exportar el gráfico como imagen.
Se puede trabajar de dos formas:
| Función | Para qué sirve |
|---|---|
hchart() |
Hacer un gráfico rápido a partir de un data frame, parecido a
qplot() de ggplot2 |
highchart() + hc_*() |
Armar el gráfico por partes (series, ejes, títulos, tooltip),
uniendo cada paso con %>% |
hcaes() |
Indicar qué columna va en cada eje, como aes() en
ggplot2 |
hcmap() |
Hacer mapas usando los mapas que ya trae Highcharts |
Los gráficos se pueden usar en R Markdown, Quarto y Shiny.
Licencia: highcharter es libre, pero Highcharts solo es gratis para uso personal, académico o sin fines de lucro. Para usarlo en una empresa se necesita comprar la licencia.
| ggplot2 | plotly | highcharter | |
|---|---|---|---|
| Interactivo | No | Sí | Sí |
| Mapas incluidos | No (necesita sf) |
Limitado | Sí, por país y región |
| Series de tiempo | Básico | Bueno | Muy bueno (zoom y selector de rango) |
| Licencia | Libre | Libre | Gratis solo para uso no comercial |
Para la aplicación se usa el dataset Vigilancia Epidemiológica de dengue, publicado en la Plataforma Nacional de Datos Abiertos. Lo administra el Centro Nacional de Epidemiología, Prevención y Control de Enfermedades (CDC Perú) del Ministerio de Salud. Los casos llegan a través de la Red Nacional de Epidemiología (RENACE), formada por 10 272 establecimientos de salud del MINSA, EsSalud y otras instituciones del sector. De ellos, 8 536 son unidades notificantes que reportan los casos cada semana.
Cada fila de la base es un caso de dengue notificado. Cubre desde el año 2000 hasta el 2025 e indica dónde se habría contagiado la persona (departamento, provincia y distrito), la semana en que empezaron los síntomas, la gravedad del caso, el tipo de diagnóstico, la edad y el sexo. El CDC aclara que los datos son provisionales y pueden cambiar con las actualizaciones. Las variables que se usan en este trabajo son las siguientes:
| Variable | Descripción |
|---|---|
departamento, provincia,
distrito |
Lugar probable de infección |
enfermedad |
Sin signos de alarma, con signos de alarma o grave |
ano, semana |
Año y semana epidemiológica de inicio de síntomas |
diagnostic |
Código CIE-10 |
tipo_dx |
C = confirmado, P = probable, S = sospechoso |
edad, tipo_edad |
Edad y su unidad (A = años, M = meses, D = días) |
sexo |
M = masculino, F = femenino |
Con esta base se va a probar qué tan bien funciona highcharter con datos reales y de gran tamaño. El archivo tiene más de un millón de registros y pesa más de 100 MB, así que no se puede graficar fila por fila. Primero se agrupan los casos según lo que se quiera mostrar y luego se arma el gráfico.
La idea es ver si el paquete permite responder preguntas concretas sobre el dengue en el Perú: en qué años hubo más casos, en qué semanas aparecen los brotes, qué departamentos son los más afectados y a quiénes afecta más. También se evaluará si la interactividad (zoom, tooltips, mapas y drilldown) ayuda a entender los datos mejor que un gráfico estático.
library(data.table)
library(dplyr)
library(tidyr)
library(highcharter)
El archivo pesa más de 100 MB, por eso se lee con
fread(), que es mucho más rápido que
read.csv().
ruta <- "datos_abiertos_vigilancia_dengue_2000_2025_0.csv"
dengue <- fread(ruta, colClasses = "character", encoding = "UTF-8")
dim(dengue)
## [1] 1065274 15
head(dengue)
## departamento provincia distrito localidad
## <char> <char> <char> <char>
## 1: HUANUCO LEONCIO PRADO LUYANDO SANTA ROSA DE SHAPAJILLLA
## 2: HUANUCO LEONCIO PRADO LUYANDO NARANJILLO
## 3: HUANUCO LEONCIO PRADO JOSE CRESPO Y CASTILLO AUCAYACU
## 4: HUANUCO LEONCIO PRADO JOSE CRESPO Y CASTILLO AUCAYACU
## 5: HUANUCO LEONCIO PRADO MARIANO DAMASO BERAUN BELLA
## 6: HUANUCO LEONCIO PRADO LUYANDO NARANJILLO
## enfermedad ano semana diagnostic tipo_dx diresa ubigeo
## <char> <char> <char> <char> <char> <char> <char>
## 1: DENGUE SIN SIGNOS DE ALARMA 2000 47 A97.0 P 10 100605
## 2: DENGUE SIN SIGNOS DE ALARMA 2000 40 A97.0 P 10 100605
## 3: DENGUE SIN SIGNOS DE ALARMA 2000 48 A97.0 C 10 100604
## 4: DENGUE SIN SIGNOS DE ALARMA 2000 37 A97.0 P 10 100604
## 5: DENGUE SIN SIGNOS DE ALARMA 2000 42 A97.0 C 10 100606
## 6: DENGUE SIN SIGNOS DE ALARMA 2000 40 A97.0 P 10 100605
## localcod edad tipo_edad sexo
## <char> <char> <char> <char>
## 1: 9 A M
## 2: 18 A F
## 3: 32 A F
## 4: 40 A F
## 5: 16 A M
## 6: 7 A F
La base viene con todas las columnas como texto, así que antes de graficar hay que ordenarla. Estos son los cambios que se hacen:
tipo_edad. Por eso se pasa
todo a años antes de formar los grupos.dengue <- dengue %>%
mutate(
ano = as.integer(ano),
semana = as.integer(semana),
edad = as.numeric(edad),
edad_anios = case_when(
tipo_edad == "A" ~ edad,
tipo_edad == "M" ~ edad / 12,
tipo_edad == "D" ~ edad / 365,
TRUE ~ NA_real_
),
grupo_edad = cut(edad_anios,
breaks = c(-Inf, 4, 11, 17, 29, 59, Inf),
labels = c("0-4", "5-11", "12-17", "18-29", "30-59", "60+")),
sexo = recode(sexo, "M" = "Masculino", "F" = "Femenino"),
tipo_dx = recode(tipo_dx, "C" = "Confirmado", "P" = "Probable",
"S" = "Sospechoso"),
enfermedad = paste0(substr(enfermedad, 1, 1),
tolower(substr(enfermedad, 2, nchar(enfermedad)))),
fecha = as.Date(paste0(ano, "-01-01")) + (semana - 1) * 7
) %>%
filter(!is.na(ano), !is.na(semana))
# Revisión rápida
dengue %>% count(enfermedad, sort = TRUE)
## enfermedad n
## <char> <int>
## 1: Dengue sin signos de alarma 944670
## 2: Dengue con signos de alarma 116407
## 3: Dengue grave 4197
dengue %>% count(tipo_dx, sort = TRUE)
## tipo_dx n
## <char> <int>
## 1: Confirmado 955112
## 2: Probable 110162
Las dos tablas siguientes sirven para comprobar que la limpieza funcionó. Muestran cuántos casos hay en cada nivel de gravedad y en cada tipo de diagnóstico, y permiten ver si quedó algún valor raro o vacío antes de pasar a los gráficos.
Con la base lista, antes de hacer los gráficos se define cómo se van
a ver. En highcharter esto se hace una sola vez con
hc_theme(): se crea un tema con la paleta de colores, la
tipografía, el estilo de los ejes y del tooltip, y luego se aplica a
todo el documento. Así no hay que repetir el mismo formato en cada
gráfico y todos mantienen el mismo estilo.
La paleta tiene cuatro colores que combinan entre sí: azul, naranja, verde agua y violeta. Los colores se usan con un criterio. Cuando el gráfico compara grupos distintos, como hombres y mujeres, cada grupo lleva un color diferente. Cuando muestra cantidades, como en el mapa o el mapa de calor, se usa un solo color que va de claro a oscuro, porque así se entiende rápido que más oscuro significa más casos. El naranja se guarda para resaltar lo que más importa, como el año con más casos.
Además, se crea la función grad_v() para los degradados.
En Highcharts un degradado se escribe como una lista con
linearGradient, el color de inicio y el de fin. También se
pasan a español los textos de los botones y los nombres de los meses,
que por defecto vienen en inglés.
# Paleta: cuatro colores que combinan entre sí
pal <- c(azul = "#2a78d6", naranja = "#eb6834", aqua = "#1baf7a", violeta = "#6250d6")
# Degradado de un color a otro. En barras verticales va de arriba hacia abajo;
# en barras horizontales (type = "bar") Highcharts lo gira y va a lo largo de la barra
grad_v <- function(c1, c2) list(linearGradient = list(x1 = 0, y1 = 0, x2 = 0, y2 = 1),
stops = list(list(0, c1), list(1, c2)))
tema <- hc_theme(
colors = unname(pal),
chart = list(backgroundColor = "transparent",
style = list(fontFamily = "Poppins")),
title = list(align = "left",
style = list(color = "#1f2937", fontWeight = "600", fontSize = "17px")),
subtitle = list(align = "left", style = list(color = "#6b7280", fontSize = "12px")),
xAxis = list(lineColor = "#e5e7eb", tickColor = "#e5e7eb",
labels = list(style = list(color = "#6b7280")),
title = list(style = list(color = "#6b7280"))),
yAxis = list(gridLineColor = "#eef0f3", gridLineDashStyle = "Dash",
labels = list(style = list(color = "#6b7280")),
title = list(style = list(color = "#6b7280"))),
legend = list(itemStyle = list(color = "#374151", fontWeight = "500")),
tooltip = list(backgroundColor = "rgba(255,255,255,0.97)", borderWidth = 0,
borderRadius = 10, shadow = TRUE,
style = list(color = "#1f2937", fontSize = "12px")),
plotOptions = list(
series = list(animation = list(duration = 1200)),
column = list(borderRadius = 5, borderWidth = 0),
bar = list(borderRadius = 5, borderWidth = 0),
pie = list(borderWidth = 3, borderColor = "#ffffff")
),
credits = list(style = list(color = "#9ca3af"))
)
# Textos de Highcharts en español
lang_es <- list(
thousandsSep = ",", decimalPoint = ".",
drillUpText = "◁ Volver a {series.name}", rangeSelectorZoom = "Periodo",
resetZoom = "Quitar zoom",
months = c("Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio",
"Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"),
shortMonths = c("Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago",
"Sep", "Oct", "Nov", "Dic"),
weekdays = c("Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado")
)
# Se aplica a todos los gráficos del documento
options(highcharter.theme = tema, highcharter.lang = lang_es)
fuente <- "Fuente: CDC Perú – MINSA"
Desde aquí, todos los gráficos toman este tema automáticamente. En cada uno solo se agregan los detalles propios, como el título o un color especial.
La primera pregunta es cómo cambió el número de casos a lo largo del tiempo. Para responderla se cuentan los casos por año y se muestran en un gráfico de barras. El año con más casos se pinta de naranja para que resalte apenas se abre el gráfico. Al pasar el mouse sobre una barra se ve el total exacto de ese año.
por_anio <- dengue %>% count(ano, name = "casos")
anio_max <- por_anio$ano[which.max(por_anio$casos)]
casos_max <- max(por_anio$casos)
puntos <- lapply(seq_len(nrow(por_anio)), function(i) {
p <- list(x = por_anio$ano[i], y = por_anio$casos[i])
if (por_anio$ano[i] == anio_max) p$color <- grad_v("#f59e6b", pal[["naranja"]])
p
})
highchart() %>%
hc_chart(type = "column") %>%
hc_add_series(data = puntos, name = "Casos",
color = grad_v(pal[["azul"]], pal[["violeta"]])) %>%
hc_title(text = "Casos de dengue notificados por año, Perú 2000–2025") %>%
hc_subtitle(text = paste0("En naranja el año con más casos (", anio_max, ")")) %>%
hc_xAxis(title = list(text = "Año"), allowDecimals = FALSE) %>%
hc_yAxis(title = list(text = "Número de casos")) %>%
hc_legend(enabled = FALSE) %>%
hc_tooltip(pointFormat = "<b>{point.y:,.0f}</b> casos") %>%
hc_credits(enabled = TRUE, text = fuente)
El dengue en el Perú no creció de forma pareja, sino por brotes. Entre 2000 y 2009 los casos casi siempre estuvieron por debajo de 20 mil al año. Desde 2010 empezaron a subir y aparecieron años con picos marcados, como 2017, con alrededor de 65 mil casos, y 2022, con cerca de 60 mil. Después de cada pico los casos bajan, como en 2018, que fue uno de los años con menos casos de todo el periodo.
El cambio más fuerte llegó en 2023 y 2024. El año con más casos fue 2024, con 271,531 casos notificados, y 2023 quedó muy cerca. Entre los dos suman 528,172 casos, más que todos los años de 2000 a 2022 juntos (501,249). En 2025 los casos bajaron a niveles parecidos a los de 2015.
Los picos de 2017 y 2023 coinciden con el Niño Costero, que trajo lluvias intensas y calor a la costa norte. Estas condiciones favorecen la reproducción del zancudo Aedes aegypti, que transmite el dengue [falta fuente].
El gráfico anterior muestra cuántos casos hubo cada año, pero no
cuándo aparecieron dentro del año. Para eso se cuentan los casos por
semana y se arma una serie de tiempo con
highchart(type = "stock"). Este tipo de gráfico es uno de
los puntos fuertes de highcharter: trae botones para elegir el periodo
(1, 3, 5 o 10 años) y una barra en la parte de abajo para moverse en el
tiempo y acercarse a un brote.
semanal <- dengue %>%
count(fecha, name = "casos") %>%
arrange(fecha)
highchart(type = "stock") %>%
hc_add_series(semanal, "areaspline", hcaes(x = fecha, y = casos),
name = "Casos", color = pal[["violeta"]], lineWidth = 2,
fillColor = grad_v("rgba(98,80,214,0.45)", "rgba(42,120,214,0.02)")) %>%
hc_title(text = "Casos semanales de dengue, Perú 2000–2025") %>%
hc_subtitle(text = "Elegir el periodo") %>%
hc_rangeSelector(
selected = 4,
inputEnabled = FALSE,
buttonTheme = list(r = 8, fill = "#f1f2f6", style = list(color = "#374151"),
states = list(select = list(fill = pal[["violeta"]],
style = list(color = "#ffffff")))),
buttons = list(
list(type = "year", count = 1, text = "1a"),
list(type = "year", count = 3, text = "3a"),
list(type = "year", count = 5, text = "5a"),
list(type = "year", count = 10, text = "10a"),
list(type = "all", text = "Todo")
)
) %>%
hc_navigator(maskFill = "rgba(98,80,214,0.12)",
series = list(color = pal[["violeta"]], fillOpacity = 0.2)) %>%
hc_scrollbar(enabled = FALSE) %>%
hc_tooltip(pointFormat = "<b>{point.y:,.0f}</b> casos") %>%
hc_credits(enabled = TRUE, text = fuente)
Vista semana a semana, la serie pasa casi todo el tiempo cerca de cero y se dispara en momentos puntuales. Cada brote dura pocas semanas: sube rápido, llega a su punto más alto y vuelve a bajar. Hasta 2016 los picos casi nunca pasaron de 2 mil casos por semana. En 2017 el pico llegó a cerca de 5 mil.
Lo que no se veía en el gráfico por año es que 2023 y 2024 fueron dos brotes separados y no una sola ola larga. El primero llegó a cerca de 17 mil casos en una semana y el segundo superó los 19 mil. Entre ambos, los casos bajaron casi a cero durante la segunda mitad de 2023. Con el botón “3a” se puede ver este periodo de cerca.
La serie semanal mostró que los brotes duran pocas semanas, pero los años con más casos tapan a los demás. Para ver si los casos se repiten en la misma época todos los años, se usa un mapa de calor. Cada fila es un año y cada columna una semana. El color no indica la cantidad de casos, sino qué porcentaje de los casos de ese año se notificó en esa semana. Así un año con pocos casos se puede comparar con uno de epidemia. Al pasar el mouse se ven los casos de la semana y su porcentaje.
calor <- dengue %>%
filter(semana <= 52) %>%
count(ano, semana, name = "casos") %>%
complete(ano, semana = 1:52, fill = list(casos = 0)) %>%
group_by(ano) %>%
mutate(pct = round(100 * casos / sum(casos), 2)) %>%
ungroup()
hchart(calor, "heatmap", hcaes(x = semana, y = ano, value = pct),
name = "% del año", borderColor = "#ffffff", borderWidth = 1) %>%
hc_colorAxis(min = 0, stops = color_stops(6, c("#f4f8fe", "#cde2fb", "#86b6ef",
"#3987e5", "#1c5cab", "#0d366b")),
labels = list(format = "{value}%")) %>%
hc_title(text = "¿En qué semanas se concentran los casos de cada año?") %>%
hc_subtitle(text = "Porcentaje de los casos del año notificados en cada semana") %>%
hc_xAxis(title = list(text = "Semana epidemiológica"), gridLineWidth = 0) %>%
hc_yAxis(title = list(text = "Año"), reversed = TRUE, gridLineWidth = 0) %>%
hc_legend(layout = "horizontal", align = "right", symbolWidth = 260) %>%
hc_tooltip(formatter = JS("function(){ return 'Año ' + this.point.y + ', semana ' + this.point.x + '<br><b>' + Highcharts.numberFormat(this.point.casos, 0) + '</b> casos (' + this.point.value.toFixed(1) + '% del año)'; }"))
En casi todos los años las celdas más oscuras están en la primera mitad del año, entre febrero y mayo, que en el Perú son los meses de más calor y lluvias. En la segunda mitad del año los casos son muy pocos, lo que confirma que el dengue en el país es estacional.
La época del brote no fue la misma en los dos años de epidemia. En 2023 la mayor parte de los casos se notificó entre fines de mayo e inicios de junio (semanas 19 a 24), más tarde de lo normal. En 2024 el brote se adelantó y se concentró entre marzo y abril (semanas 10 a 17).
Después de ver cuándo aparecen los casos, la siguiente pregunta es
dónde. Para eso se suman los casos de 2000 a 2025 por departamento y se
muestran en un mapa del Perú con highchart(type = "map").
highcharter trae los mapas listos, así que solo hay que unir la data con
el mapa usando un código por departamento. El mapa de Highcharts separa
Lima Metropolitana (pe-lp) de Lima Provincias
(pe-lr), por eso los casos de Lima se reparten según la
provincia. El mapa se puede acercar con los botones + y -.
codigos <- c(
"AMAZONAS" = "pe-am", "ANCASH" = "pe-an", "APURIMAC" = "pe-ap",
"AREQUIPA" = "pe-ar", "AYACUCHO" = "pe-ay", "CAJAMARCA" = "pe-cj",
"CALLAO" = "pe-cl", "CUSCO" = "pe-cs", "HUANCAVELICA" = "pe-hv",
"HUANUCO" = "pe-hc", "ICA" = "pe-ic", "JUNIN" = "pe-ju",
"LA LIBERTAD" = "pe-ll", "LAMBAYEQUE" = "pe-lb", "LORETO" = "pe-lo",
"MADRE DE DIOS" = "pe-md", "MOQUEGUA" = "pe-mq", "PASCO" = "pe-pa",
"PIURA" = "pe-pi", "PUNO" = "pe-pu", "SAN MARTIN" = "pe-sm",
"TACNA" = "pe-ta", "TUMBES" = "pe-tu", "UCAYALI" = "pe-uc"
)
por_depto <- dengue %>%
mutate(depto = toupper(iconv(departamento, to = "ASCII//TRANSLIT")),
codigo = case_when(
depto == "LIMA" & toupper(provincia) == "LIMA" ~ "pe-lp",
depto == "LIMA" ~ "pe-lr",
TRUE ~ unname(codigos[depto])
)) %>%
filter(!is.na(codigo)) %>%
count(codigo, name = "value") # highcharts colorea el mapa con la columna "value"
mapa_peru <- download_map_data("countries/pe/pe-all")
highchart(type = "map") %>%
hc_add_series(mapData = mapa_peru, data = list_parse(por_depto),
joinBy = c("hc-key", "codigo"), name = "Casos",
nullColor = "#eef0f3", borderColor = "#ffffff", borderWidth = 1,
states = list(hover = list(color = pal[["naranja"]])),
tooltip = list(pointFormat = "{point.name}: <b>{point.value:,.0f}</b> casos"),
dataLabels = list(enabled = TRUE, format = "{point.name}",
style = list(fontSize = "9px", fontWeight = "500",
color = "#1f2937", textOutline = "1px #ffffff"))) %>%
hc_colorAxis(type = "logarithmic",
stops = color_stops(5, c("#e3f4ee", "#a8dcc9", "#5cbf9f",
"#1b8f6a", "#0b5a43"))) %>%
hc_mapNavigation(enabled = TRUE, buttonOptions = list(verticalAlign = "bottom")) %>%
hc_legend(title = list(text = "Casos (escala log)"), symbolWidth = 220) %>%
hc_title(text = "Casos acumulados de dengue por departamento, 2000–2025") %>%
hc_subtitle(text = "Pasa el mouse sobre un departamento para ver el total") %>%
hc_credits(enabled = TRUE, text = fuente)
Los colores usan una escala logarítmica, donde cada paso de la leyenda multiplica los casos por 100. Se eligió así porque la diferencia entre departamentos es enorme: con una escala normal, solo los más afectados tendrían color y el resto se vería igual.
El mapa muestra dos zonas con más casos: la costa norte (Tumbes, Piura y Lambayeque) y la selva (Loreto, San Martín, Ucayali y Madre de Dios). Las dos son cálidas y húmedas, y allí vive el zancudo Aedes aegypti. En la sierra y en el sur los colores son mucho más claros. Arequipa, Moquegua y Puno tienen pocos casos, y Huancavelica y Tacna no registran casos como lugar de contagio. Ica es la excepción en la costa sur, con un color más oscuro que sus vecinos.
El mapa muestra las zonas más afectadas, pero con la escala logarítmica cuesta comparar un departamento con otro. Por eso aquí se ordenan los 15 departamentos con más casos en un gráfico de barras. Este gráfico usa el drilldown de highcharter: al hacer clic en una barra, el gráfico cambia y muestra las 10 provincias con más casos de ese departamento. Para regresar se usa el botón “Volver” de la esquina superior derecha. Así se puede bajar de nivel sin hacer un gráfico aparte para cada departamento.
top_depto <- dengue %>%
count(departamento, name = "y", sort = TRUE) %>%
slice_head(n = 15) %>%
mutate(name = departamento, drilldown = departamento)
series_prov <- lapply(top_depto$departamento, function(d) {
prov <- dengue %>%
filter(departamento == d) %>%
count(provincia, name = "y", sort = TRUE) %>%
slice_head(n = 10) %>%
rename(name = provincia)
list(id = d, name = d, type = "bar", data = list_parse(prov),
color = grad_v(pal[["violeta"]], "#f59e6b"))
})
hchart(top_depto, "bar", hcaes(x = name, y = y, drilldown = drilldown),
name = "Casos", color = grad_v(pal[["aqua"]], pal[["azul"]])) %>%
hc_drilldown(allowPointDrilldown = TRUE, series = series_prov,
activeAxisLabelStyle = list(color = "#1f2937", textDecoration = "none"),
drillUpButton = list(theme = list(r = 8, fill = "#f1f2f6",
stroke = "none"))) %>%
hc_title(text = "Top 15 departamentos con más casos") %>%
hc_subtitle(text = "Haz clic en una barra para ver sus provincias") %>%
hc_xAxis(type = "category", title = list(text = NULL)) %>%
hc_yAxis(title = list(text = "Número de casos")) %>%
hc_legend(enabled = FALSE) %>%
hc_tooltip(pointFormat = "<b>{point.y:,.0f}</b> casos")
Piura es por mucho el departamento con más casos, con cerca de 240 mil entre 2000 y 2025. Eso es casi el doble que Lima, que está en segundo lugar, y equivale al 22.4% de todos los casos del país. Después vienen Loreto y La Libertad, ambos con más de 100 mil casos.
El orden cambia un poco respecto al mapa. Lima y La Libertad no se notaban tanto con la escala de colores, pero en número de casos están entre los cuatro primeros. Hay que tener en cuenta que estas cifras son casos y no tasas: Lima tiene muchos casos en parte porque es el departamento con más población. Tumbes, en cambio, aparece en el noveno lugar, pero tiene muy pocos habitantes. Si se calculara la tasa por cada 100 mil habitantes, probablemente estaría entre los primeros.
Además de cuántos casos hubo y dónde, importa qué tan graves fueron. En 2009 la OMS propuso clasificar el dengue en tres niveles: sin signos de alarma, con signos de alarma (por ejemplo, dolor abdominal fuerte, vómitos persistentes o sangrado de encías) y dengue grave. En esta base, la clasificación se puede comparar recién desde 2011, por eso el gráfico empieza ese año. Las barras están apiladas al 100 %, así que cada año muestra cómo se repartieron sus casos entre los tres niveles, sin importar si fue un año con muchos o pocos casos.
gravedad <- dengue %>%
filter(ano >= 2011) %>%
count(ano, enfermedad, name = "casos") %>%
mutate(enfermedad = factor(enfermedad, levels = c(
"Dengue sin signos de alarma", "Dengue con signos de alarma", "Dengue grave")))
hchart(gravedad, "column", hcaes(x = ano, y = casos, group = enfermedad)) %>%
hc_colors(c("#9ec5f4", "#6250d6", "#eb6834")) %>%
hc_plotOptions(column = list(stacking = "percent", borderRadius = 0,
borderWidth = 1, borderColor = "#ffffff")) %>%
hc_title(text = "Distribución de casos según gravedad, 2011–2025") %>%
hc_yAxis(title = list(text = "% de casos"), labels = list(format = "{value}%"),
max = 100) %>%
hc_xAxis(title = list(text = "Año")) %>%
hc_tooltip(shared = TRUE,
pointFormat = "<span style='color:{series.color}'>●</span> {series.name}: <b>{point.y:,.0f}</b> ({point.percentage:.1f}%)<br/>") %>%
hc_legend(itemDistance = 40)
La mayoría de los casos son leves. En todos los años, entre el 73 % y el 91 % fueron dengue sin signos de alarma. El dengue grave es muy poco frecuente y no pasa del 2 % en ningún año. En el gráfico casi no se ve, pero se puede revisar con el tooltip.
El porcentaje de casos con signos de alarma cambia bastante de un año a otro. Fue más alto en 2013, 2014 y 2018, con alrededor de un cuarto de los casos, y más bajo en 2021 y 2024, con cerca del 10 %. Llama la atención que 2018, uno de los años con menos casos, tenga uno de los porcentajes más altos. En cambio, 2023 y 2024, los años de epidemia, tienen porcentajes bajos. Una posible explicación es que en los años de epidemia se notifican muchos más casos leves, mientras que en los años tranquilos llegan sobre todo los casos que necesitan atención médica.
Falta ver a quiénes afecta más el dengue. Para eso se usa una
pirámide, que en highcharter se arma con dos series de barras
horizontales: los hombres van hacia la izquierda (con valores negativos)
y las mujeres hacia la derecha. Para que no aparezcan números negativos
en el eje ni en el tooltip, se usa una pequeña función de JavaScript con
JS() que muestra el valor absoluto. Esto muestra que
highcharter permite modificar detalles del gráfico que no vienen en las
opciones básicas.
piramide <- dengue %>%
filter(!is.na(grupo_edad), sexo %in% c("Masculino", "Femenino")) %>%
count(grupo_edad, sexo, name = "casos") %>%
mutate(casos = ifelse(sexo == "Masculino", -casos, casos))
grupos <- levels(piramide$grupo_edad)
highchart() %>%
hc_chart(type = "bar") %>%
hc_xAxis(list(categories = grupos, reversed = FALSE),
list(categories = grupos, opposite = TRUE, reversed = FALSE, linkedTo = 0)) %>%
hc_yAxis(title = list(text = "Número de casos"),
labels = list(formatter = JS("function(){ return Highcharts.numberFormat(Math.abs(this.value), 0); }"))) %>%
hc_plotOptions(series = list(stacking = "normal", pointPadding = 0.05)) %>%
hc_add_series(name = "Masculino", color = grad_v("#86b6ef", pal[["azul"]]),
data = piramide %>% filter(sexo == "Masculino") %>%
arrange(grupo_edad) %>% pull(casos)) %>%
hc_add_series(name = "Femenino", color = grad_v("#f59e6b", pal[["naranja"]]),
data = piramide %>% filter(sexo == "Femenino") %>%
arrange(grupo_edad) %>% pull(casos)) %>%
hc_tooltip(formatter = JS("function(){ return '<b>' + this.series.name + ', ' + this.point.category + ' años</b><br/>Casos: ' + Highcharts.numberFormat(Math.abs(this.point.y), 0); }")) %>%
hc_title(text = "Casos de dengue por grupo de edad y sexo, 2000–2025")
El grupo de 30 a 59 años es el que tiene más casos, seguido por el de 18 a 29. Hay que tener cuidado al compararlos, porque los grupos no tienen el mismo tamaño: el de 30 a 59 abarca 30 años, mientras que el de 0 a 4 solo cinco. Aun así, los niños menores de 5 años son el grupo con menos casos.
La diferencia entre hombres y mujeres depende de la edad. En los niños y adolescentes los casos están casi parejos, e incluso hay algo más de hombres. Desde los 18 años las mujeres pasan adelante, y entre los 30 y 59 años tienen cerca de 228 mil casos frente a unos 172 mil de los hombres. En total, el 53.7% de los casos son mujeres. La base no explica esta diferencia. Una posibilidad es que el zancudo pica de día y dentro o cerca de las casas, donde muchas mujeres adultas pasan más tiempo [falta fuente]. Otra es que las mujeres acuden más a los establecimientos de salud y por eso más casos suyos llegan a notificarse.
Para terminar el análisis, se revisa qué tan confiables son los casos
de la base. Un caso confirmado es el que tiene una prueba de laboratorio
positiva. Un caso probable es el de una persona con síntomas de dengue
que vive o estuvo en una zona con transmisión, pero que no tiene
confirmación de laboratorio. Para mostrar esta proporción se usa un
gráfico de dona, que en highcharter es un gráfico circular con el
argumento innerSize.
tipo <- dengue %>% count(tipo_dx, name = "casos", sort = TRUE)
hchart(tipo, "pie", hcaes(name = tipo_dx, y = casos), name = "Casos",
innerSize = "62%") %>%
hc_colors(list(grad_v("#4f95e6", pal[["azul"]]),
grad_v("#2fd096", pal[["aqua"]]),
grad_v("#8a7cf0", pal[["violeta"]]))) %>%
hc_plotOptions(pie = list(dataLabels = list(
enabled = TRUE, distance = 18,
format = "<b>{point.name}</b><br>{point.percentage:.1f}%",
style = list(fontWeight = "400", textOutline = "none")))) %>%
hc_title(text = "Casos según tipo de diagnóstico") %>%
hc_subtitle(text = paste0("<span style='font-size:22px;font-weight:600'>",
format(sum(tipo$casos), big.mark = ","),
"</span><br>casos"),
align = "center", verticalAlign = "middle", floating = TRUE,
y = 20, useHTML = TRUE) %>%
hc_tooltip(pointFormat = "<b>{point.y:,.0f}</b> casos ({point.percentage:.1f}%)")
El 89.7 % de los casos de la base son confirmados y el 10.3 % son probables. No hay casos sospechosos, aunque el diccionario de datos sí incluye esa categoría. Esto quiere decir que casi nueve de cada diez casos analizados en este trabajo tienen respaldo de laboratorio, lo que da más confianza a los resultados de las secciones anteriores.
hc_theme() se definió el estilo de todos los
gráficos una sola vez. Cuando algo no venía en las opciones básicas,
como quitar los signos negativos en la pirámide, se pudo resolver con
unas líneas de JavaScript usando JS().