Visualización
Paquetes de R para construir, componer y animar gráficos de calidad publicable
Sobre visualización en R
R nace en estadística y la visualización ha sido siempre uno de sus puntos fuertes. Hoy el ecosistema gravita alrededor de ggplot2 y la grammar of graphics de Leland Wilkinson: una idea estructurada según la cual un gráfico se compone de capas (datos + mapeos estéticos + geometrías + escalas + facetas + tema). Esto contrasta con el sistema base (plot(), par(), points(), lines()), imperativo, rápido, útil para diagnósticos sueltos pero costoso de mantener, y con lattice, la implementación clásica de trellis graphics de Deepayan Sarkar, hoy en uso minoritario fuera de comunidades específicas (econometría, ciertos paquetes de mixed models).
En la práctica, un usuario senior:
- Usa base para diagnósticos rápidos dentro de la consola (
plot(model),hist(),pairs()) y para gráficos que están dentro de paquetes que no quieren depender de ggplot2. - Construye gráficos finales con ggplot2 y elige entre el océano de extensiones (
gg*) según el dominio:gggenespara genómica,ggsegpara neuroimagen,ggrepelpara etiquetas no superpuestas,ggtreepara árboles filogenéticos,ggdistpara distribuciones,ggplot2::geom_sfpara mapas. - Compone múltiples paneles con
patchwork(ocowplotsi necesita alineación más exigente) en lugar de pelearse congridExtra::grid.arrangeopar(mfrow = ...). - Sale de ggplot2 cuando hace falta interactividad:
plotly::ggplotly()para conversión rápida,htmlwidgets(leaflet, DT, dygraphs) cuando el target es HTML. - Anima con
gganimateo, si el output es 3D / científico, considerargl,plotlyorayshader.
Tres principios que conviene tener interiorizados:
- Datos tidy primero. ggplot2 espera long format. Tiempo invertido en
tidyr::pivot_longer()se recupera con creces frente a forzar la geometría sobre datos anchos. - Escalas, no
aes(). Personalizar colores, formas o tamaños se hace conscale_*_manual(),scale_*_brewer(),scale_*_viridis_c(). Hardcodear estéticas dentro deaes()rompe la lógica de leyendas y mapeos. - Tema al final. El orden idiomático es
ggplot(...) + geom_*() + scale_*() + facet_*() + labs() + theme_*(). Cambios de tema deben aplicarse al final, idealmente con untheme_set()global por proyecto.
Esta página cataloga los paquetes que estructuran la mayor parte de los flujos de visualización en R. El orden refleja jerarquía conceptual: primero el núcleo (ggplot2), después las extensiones específicas de dominio, y finalmente composición y animación.
ggplot2
ggplot2 es la implementación canónica de la grammar of graphics en R y, en la práctica, el sistema gráfico por defecto fuera del dibujo rápido en consola. Desarrollado por Hadley Wickham, hoy es parte del tidyverse y la base sobre la que se han construido cientos de extensiones específicas de dominio.
Su valor real no está en producir un gráfico concreto, sino en imponer una sintaxis composicional: si entiendes cómo encadenar capas, escalas y facetas, prácticamente cualquier visualización que veas publicada es reproducible con dos o tres líneas de código.
Cuándo usarlo
Por defecto, en cualquier visualización que vaya a aparecer en un informe, paper, dashboard o presentación. Especialmente fuerte cuando hay agrupaciones (color, fill, shape), facetas (facet_wrap, facet_grid) o múltiples geometrías superpuestas en el mismo lienzo.
Cuándo NO usarlo
- Diagnósticos efímeros dentro de la consola. Para
plot(model),hist(x),pairs(df), el sistema base es más rápido y no añade dependencia. - Volúmenes muy grandes (millones de puntos sin agregación). Considera
ggrastrpara rasterizar capas pesadas dentro de un ggplot,scattermorepara scatter eficiente, o herramientas fuera de R (datashaderen Python) cuando el render se vuelve inviable. - Gráficos interactivos por defecto. ggplot2 produce gráficos estáticos. Para interactividad, envuelve con
plotly::ggplotly()o salta aplotly/htmlwidgetsdirectamente. - Animaciones nativas. Usa
gganimate(extensión), no es funcionalidad core.
Conceptos clave
- Capas (
geom_*). Losgeom_point,geom_line,geom_col,geom_boxplot,geom_smoothson las unidades constructivas. Cada uno hereda mapeos delggplot(...)raíz o define los suyos propios. aes()mapea variables a estéticas. Los valores literales van fuera.aes(color = species)mapea la variable.color = "red"se pone como argumento del geom, no dentro deaes().- Escalas. Cualquier estética (
color,fill,x,y,size,alpha) tiene una familiascale_<estética>_<tipo>(). Usascale_color_viridis_d()para discreto perceptualmente uniforme.scale_color_brewer(palette = "Set2")para paletas categóricas estandarizadas. - Facetas.
facet_wrap(~ var)para una variable.facet_grid(rows ~ cols)para dos cruzadas.scales = "free_y"cuando los rangos no son comparables. - Temas (
theme_*).theme_minimal(),theme_bw(),theme_classic()son los más usados.theme()permite ajuste fino de cualquier elemento (element_text,element_rect,element_blank). - Datos largos. La gramática asume long format. Si tu gráfico requiere demasiados
aes()con columnas distintas, casi siempre la solución espivot_longer()previo.
Patrón mínimo
library(ggplot2)
ggplot(mpg, aes(x = displ, y = hwy, color = class)) +
geom_point(alpha = 0.7, size = 2) +
geom_smooth(method = "lm", se = FALSE, linewidth = 0.6) +
scale_color_viridis_d(option = "D") +
facet_wrap(~ drv, nrow = 1) +
labs(
x = "Cilindrada (L)",
y = "Consumo en carretera (mpg)",
color = "Clase de vehículo",
title = "Consumo vs cilindrada por tipo de tracción"
) +
theme_minimal(base_size = 12) +
theme(legend.position = "bottom")Trampas habituales
aes()con literales. Ponercolor = "red"dentro deaes()crea una variable llamada literalmente “red” y dibuja una leyenda absurda. Los literales fuera deaes(), los mapeos dentro.- Orden de factor. Las leyendas y los ejes se ordenan alfabéticamente por defecto. Para controlar el orden, convierte la variable a
factorconlevels = ...explícitos, o usaforcats::fct_reorder(). group =cuando no hay color. Si dibujas múltiples líneas sin mapear color/linetype, ggplot2 las une todas en una sola. Añadeaes(group = id)para separarlas visualmente sin que aparezca leyenda.scale_*_continuousvsscale_*_discrete. No son intercambiables. Deben coincidir con el tipo de la variable mapeada. Confundirlos es la causa más frecuente del error “Discrete value supplied to continuous scale”.
Enlaces
Relacionados en esta página
patchwork, composición de múltiples ggplots.gganimate, animación de ggplots.gggenes,ggseg, extensiones de dominio.
patchwork
patchwork es el paquete de composición de gráficos ggplot2 en layouts arbitrarios desarrollado por Thomas Lin Pedersen. Resuelve, con una sintaxis algebraica notablemente legible, el problema clásico de combinar varios paneles en una figura única, algo que históricamente se hacía con gridExtra::grid.arrange o cowplot::plot_grid, ambos funcionales pero verbosos.
Hoy es la opción por defecto en el tidyverse y la primera elección para figuras compuestas de paper.
Cuándo usarlo
- Figuras compuestas para publicación con dos o más paneles relacionados (p. ej. PCA + heatmap + barplot).
- Cuando necesitas alineación automática de ejes y leyendas entre paneles.
- Anotación global (un único título, una única leyenda compartida).
Cuándo NO usarlo
facet_wrap/facet_gridya resuelven el caso. Si los paneles muestran la misma geometría sobre subconjuntos de los datos, las facetas de ggplot2 son superiores: mismas escalas, mismo gráfico, código más corto.- Alineación pixel-perfect muy exigente entre paneles de orígenes muy distintos:
cowplot::plot_gridofrece control más fino sobre alineación de bordes.gridExtrada acceso directo a grobs si necesitas manipulación manual. - Composición con gráficos no-ggplot. patchwork funciona con ggplots. Mezclar
lattice,baseogridrequierecowploto conversión manual congridGraphics.
Conceptos clave
- Operadores.
+añade en el siguiente espacio libre.|coloca en horizontal./apila vertical.()agrupa subexpresiones.(p1 | p2) / p3produce dos arriba, uno abajo. plot_layout()controlancol,nrow, anchos relativos (widths = c(2, 1)), alturas, y guides (guides = "collect"consolida leyendas duplicadas).plot_annotation()añade título, subtítulo, caption y etiquetas automáticas de panel (tag_levels = "A"produce A, B, C, D).& theme(...)aplica un tema a todos los subplots simultáneamente.& scale_color_viridis_d()propaga una escala común.
Patrón mínimo
library(ggplot2)
library(patchwork)
p1 <- ggplot(mtcars, aes(wt, mpg)) + geom_point() + theme_minimal()
p2 <- ggplot(mtcars, aes(factor(cyl), mpg)) + geom_boxplot() + theme_minimal()
p3 <- ggplot(mtcars, aes(hp)) + geom_histogram(bins = 15) + theme_minimal()
(p1 | p2) / p3 +
plot_layout(heights = c(2, 1)) +
plot_annotation(
title = "Análisis exploratorio de mtcars",
tag_levels = "A"
) &
theme(plot.tag = element_text(face = "bold"))Trampas habituales
- Leyendas duplicadas. Sin
guides = "collect"enplot_layout(), cada panel muestra su propia leyenda aunque sea idéntica. Cuando el mapeo es el mismo, consolidar siempre. &vs+para temas.+aplica el tema solo al último plot añadido.&lo propaga a todos los subplots de la composición. Confundirlos da resultados estéticos inconsistentes.- Tamaños relativos olvidados. Por defecto todos los paneles tienen el mismo peso. Para paneles asimétricos usa
plot_layout(widths = ..., heights = ...).
Enlaces
Relacionados en esta página
ggplot2, patchwork solo compone ggplots.
gganimate
gganimate extiende ggplot2 con transiciones temporales y animación. Reemplaza el flujo histórico de generar frames manualmente y unirlos con ImageMagick, ofreciendo en su lugar verbos declarativos (transition_*, ease_aes, view_*) que se componen como cualquier otra capa de ggplot.
Está desarrollado por Thomas Lin Pedersen, igual que patchwork. Las animaciones se renderizan a GIF (gifski) o vídeo (av, ffmpeg) según el renderer configurado.
Cuándo usarlo
- Series temporales o secuencias donde la dimensión
tiempo(o cualquier variable ordinal) cuenta una historia que un facetado no transmite igual de bien. - Presentaciones y comunicación divulgativa: el movimiento captura la atención cuando el público no está dispuesto a leer un grid de paneles estáticos.
- Demostración de procesos iterativos (convergencia de un algoritmo, evolución de una simulación).
Cuándo NO usarlo
- Artículo científico estático. Las revistas siguen siendo PDF. Una animación no es citable como figura. Usa
facet_wrap(~ year)y deja la animación para la companion website. - Datos sin orden natural. Animar transiciones entre categorías sin un eje temporal o secuencial suele ser más confuso que informativo.
- Volúmenes grandes. Renderizar cientos de frames con miles de puntos cada uno es lento y produce GIFs enormes. Considera muestreo o agregación previa.
- Output interactivo. Para gráficos donde el usuario controla qué se muestra,
plotlyoshinyson la herramienta correcta,gganimateproduce vídeo, no interactividad.
Conceptos clave
transition_*define qué variable orquesta la animación:transition_time(year)para tiempo continuo,transition_states(group)para saltos discretos,transition_reveal(t)para acumulación incremental.ease_aes()controla la interpolación entre estados ("linear","cubic-in-out","quadratic-out"). Cambia drásticamente la percepción del movimiento.view_follow()hace que los ejes “persigan” los datos en cada frame. Útil cuando los rangos cambian mucho.shadow_*deja rastro de estados anteriores:shadow_mark()(puntos previos persistentes),shadow_trail()(cola que se desvanece),shadow_wake()(estela suavizada).anim_save()persiste la animación.animate(plot, renderer = gifski_renderer())controla resolución, FPS y duración.
Patrón mínimo
library(ggplot2)
library(gganimate)
library(gapminder)
p <- ggplot(gapminder,
aes(gdpPercap, lifeExp,
size = pop, color = continent)) +
geom_point(alpha = 0.7) +
scale_x_log10() +
scale_size(range = c(2, 12), guide = "none") +
scale_color_viridis_d() +
labs(
title = "Año: {frame_time}",
x = "PIB per cápita (log)", y = "Esperanza de vida"
) +
theme_minimal() +
transition_time(year) +
ease_aes("cubic-in-out")
animate(p, nframes = 100, fps = 20, width = 800, height = 500,
renderer = gifski_renderer())
anim_save("gapminder.gif")Trampas habituales
{frame_time}vs{closest_state}.transition_time()usa{frame_time}.transition_states()usa{closest_state}. Confundir las plantillas de string entitledeja el título vacío.- Tiempo de render. El render es lineal en frames × geoms. Empieza con
nframes = 20para iterar diseño. Solo después sube anframes = 100+para versión final. - Falta de
gifskioav. Sin un renderer instalado,animate()falla con error opaco.install.packages("gifski")resuelve el caso por defecto.
Enlaces
Relacionados en esta página
ggplot2, gganimate solo anima ggplots.
gggenes
gggenes es una extensión de ggplot2 para dibujar mapas de genes, flechas que representan la posición, tamaño y orientación de ORFs sobre una pista lineal. Desarrollado por David Wilkins, se ha convertido en el estándar de facto cuando hay que mostrar operones, regiones sintenicas, plásmidos o clústeres biosintéticos en R.
Su valor es operar dentro de la gramática de ggplot2: las flechas son una geom más (geom_gene_arrow) que se compone con el resto del lenguaje (facetas, escalas, temas) en lugar de ser un sistema gráfico aparte.
Cuándo usarlo
- Operones, clústeres biosintéticos (BGCs), regiones plasmídicas, elementos móviles representados como flechas con coordenadas y orientación.
- Comparación de sintenia entre genomas (
facet_wrap(~ molecule)conmake_alignment_dummies()). - Anotación de mutaciones, dominios o subregiones sobre una pista de genes con
geom_subgene_arrow().
Cuándo NO usarlo
- Visualización genómica multi-pista compleja (genes + cobertura + variantes + conservación apilados). Ahí entran
Gviz,karyoploteRoggbio, diseñados para esa lógica de tracks. - Vistas circulares (cromosomas bacterianos, plásmidos circulares estilo SnapGene). Usa
gggenomes,circlizeoBioCircos. - Conexiones de sintenia entre múltiples genomas con bloques homólogos.
gggenomes(del mismo autor, hereda de gggenes) está diseñado específicamente para eso.
Conceptos clave
geom_gene_arrow()dibuja la flecha. Mapeos típicos:xmin,xmax,y(molécula/contig),fill(familia funcional),forward(TRUE si en cadena +).geom_gene_label()etiqueta cada flecha. Usa la fuente especificada y se recorta si no cabe.make_alignment_dummies()crea puntos invisibles que alinean genes homólogos entre facetas, pieza clave para comparar operones entre cepas.theme_genes()ajusta el tema base (sin grid de y, eje y discreto limpio) para que el resultado parezca un mapa genómico, no un scatter.geom_subgene_arrow()dibuja subregiones (dominios, mutaciones) dentro de una flecha de gen, útil para anotación fina.
Patrón mínimo
library(ggplot2)
library(gggenes)
ggplot(example_genes,
aes(xmin = start, xmax = end, y = molecule,
fill = gene, forward = orientation)) +
geom_gene_arrow(arrowhead_height = unit(3, "mm"),
arrowhead_width = unit(2, "mm")) +
geom_gene_label(aes(label = gene), align = "left") +
facet_wrap(~ molecule, scales = "free", ncol = 1) +
scale_fill_brewer(palette = "Set3") +
theme_genes()Trampas habituales
- Orientación.
forward = TRUE/FALSEcontrola la dirección de la flecha. Si tu columna codifica la cadena como"+"/"-", conviértela antes (orientation = strand == "+"). - Facetas sin alineación. Si los genes homólogos no caen en la misma
xentre facetas, la comparación visual no funciona.make_alignment_dummies()resuelve esto al precio de generar filas auxiliares. - Etiquetas que se solapan. Cuando los genes son cortos y muchos,
geom_gene_label()recorta agresivamente. Para regiones densas, considera dejar etiquetas fuera y mapearfilla familia funcional con leyenda.
Enlaces
Relacionados en esta página
ggplot2, gggenes hereda toda su gramática.
ggseg
ggseg es una extensión de ggplot2 para visualizar segmentaciones anatómicas del cerebro sobre atlas estándar (Desikan-Killiany, Destrieux, aseg, glasser). Desarrollado por Athanasia Mowinckel y Didac Vidal-Piñeiro, su nicho es traducir vectores de valores por región (espesor cortical, volumen, t-stat, índice de conectividad) en mapas cerebrales reproducibles dentro de la gramática de ggplot2.
El paquete principal contiene los atlas más comunes. Familias relacionadas (ggsegExtra, ggseg3d, ggsegAtlas) extienden el catálogo de atlas y añaden vistas 3D.
Cuándo usarlo
- Resultados de morfometría (FreeSurfer, FSL) por región anatómica que necesitan visualización rápida en figuras 2D estándar (lateral / medial, ambos hemisferios).
- Comparación entre grupos o tratamientos con un valor por región y atlas (t-stat, Cohen’s d, diferencia de medias).
- Cualquier output ROI-based donde la geometría real del cerebro aporta más que una tabla.
Cuándo NO usarlo
- Mapas voxel-wise (estadísticos por voxel, no por región). Eso es trabajo de
freesurferformats+ visores nativos (Freeview, FSLeyes) o paquetes comooro.nifti/neurobase. - Conectividad y grafos cerebrales complejos (matrices de conectividad, circular plots). Usa
circlize,brainGrapho herramientas dedicadas (BrainNet Viewer). - Renders 3D interactivos rotables. Aunque
ggseg3dexiste, para análisis 3D serio (proyecciones, vistas inflated) lo natural es exportar a FreeSurfer/PySurfer. - Atlas no incluidos. Si tu parcelación es custom o muy específica (Schaefer 400, Yeo 17-network, AAL3), revisa primero
ggsegExtraantes de asumir que está disponible.
Conceptos clave
- El paquete contiene polígonos por región, no imágenes. Esto permite usar
geom_brain()como una geom de ggplot, conaes(fill = mi_metrica)mapeas valores a regiones de forma estándar. - Datos en formato largo. Cada fila es una región × hemisferio × valor. La columna
region(olabel, según atlas) debe coincidir con los nombres del atlas, los mismatches silenciosos son la fuente principal de figuras vacías. - Atlas disponibles.
dk(Desikan-Killiany),aseg(subcortical),destrieux,glasser,yeo7/yeo17. Cada uno expone su propio data frame con polígonos. position = "stacked"vsposition = "dispersed". Controla si los hemisferios se apilan o se separan en la figura.
Patrón mínimo
library(ggplot2)
library(ggseg)
# data.frame con una fila por región y un valor a mapear
df <- tibble::tibble(
region = c("superior frontal", "precuneus", "insula"),
value = c(0.6, -0.3, 0.8)
)
ggplot(df) +
geom_brain(atlas = dk, aes(fill = value),
position = position_brain(hemi ~ side)) +
scale_fill_gradient2(low = "blue", mid = "white", high = "red",
midpoint = 0, na.value = "grey90") +
theme_void() +
labs(fill = "Efecto (z)")Trampas habituales
- Nombres de región que no casan con el atlas. ggseg no produce error si un valor no encuentra región, simplemente no se pinta. Verifica con
dk$data$region |> unique()antes de asumir que la figura está mal renderizada. - Hemisferios y vistas.
position_brain(hemi ~ side)separa hemisferios (izquierda/derecha) y vistas (lateral/medial). Sinposition_brain, la disposición por defecto puede mezclarlos. - Conflictos con scales discretas. Si
valuees categórico, usascale_fill_manual(). Si es continuo,scale_fill_gradient*(). Confundirlos hace que las regiones queden grises.
Enlaces
Relacionados en esta página
ggplot2, base sobre la que opera.
Recursos auxiliares
Pequeñas referencias prácticas que recurren constantemente al trabajar con cualquiera de los paquetes anteriores.
Paletas de colores
Elegir paletas no es decorativo: una paleta mal escogida puede invertir la interpretación de un gradiente o dejar daltónicos sin información. Recomendaciones:
- Continuas perceptualmente uniformes:
viridis,magma,cividis(scale_*_viridis_c()). Diseñadas explícitamente para ser correctas en escala de grises y daltonismo. - Continuas divergentes (valores con signo, centro en 0):
RColorBrewer::brewer.pal(11, "RdBu")oscale_*_gradient2(midpoint = 0). - Categóricas:
scale_*_brewer(palette = "Set2")para hasta 8 categorías. Más allá, conviene replantear el agrupamiento. - Referencia visual de paletas R: r-charts.com/es/colores.
Símbolos de punto (pch) en base R
Para gráficos en sistema base (no ggplot), pch controla la forma del marcador. Valores típicos: 0 (cuadrado), 1 (círculo), 2 (triángulo), 16 (círculo relleno), 17 (triángulo relleno), 19 (círculo relleno sólido), 21-25 (formas con fill + color separados).
Referencia visual con todos los códigos: sthda.com, pch symbols.
En ggplot2 el equivalente es la estética shape y se controla con scale_shape_manual(values = c(16, 17, 15)).