🚀 Taller: Visualización de datos con R y ggplot2

Encuentro 3: Anotaciones, Interactividad y Publicación

Pablo Tiscornia

Estación R

2025-09-09

Bienvenidos y bienvenidas al Encuentro 3 🚀

Repaso de encuentros anteriores

✅ Encuentro 1: Fundamentos

  • Gramática de los gráficos: data + aesthetics + geoms
  • Estructura básica de ggplot2
  • Diferencia entre mapeo y fijación
  • Variables continuas vs. categóricas


✅ Encuentro 2: Diseño y estética

  • Personalización visual: títulos, escalas, colores
  • Paletas profesionales y accesibilidad
  • Organización espacial: facetas y patchwork
  • Buenas prácticas de diseño


🎯 Hoy: Llevemos todo al siguiente nivel

  • Anotaciones inteligentes para contar historias
  • Interactividad básica con plotly
  • Publicación profesional y exportación

📝 Anotaciones inteligentes

¿Por qué anotar nuestros gráficos?


Las visualizaciones comunican mejor cuando:

  • Destacan elementos clave
  • Cuentan una historia clara
  • Guían la atención del lector
  • Proporcionan contexto adicional

Tipos de anotaciones:

  • Texto explicativo: geom_text(), geom_label()
  • Elementos gráficos: annotate()
  • Líneas de referencia: geom_hline(), geom_vline()
  • Formas y áreas: geom_rect(), geom_polygon()

geom_text() vs geom_label()


geom_text(): Texto simple

ggplot(tbl_desocup_2020_25, aes(x = Anio, y = n)) +
  geom_col(fill = "#4F7CFF") +
  geom_text(aes(label = n))

geom_label(): Texto con fondo

ggplot(tbl_desocup_2020_25, aes(x = Anio, y = n)) +
  geom_col(fill = "#4F7CFF") +
  geom_label(aes(label = n))

Control de posición con vjust y hjust


vjust (vertical)

  • 0 = ⬆️
  • 0.5 = centro
  • 1 = ⬇️

hjust (horizontal)

  • 0 = ➡️
  • 0.5 = centro
  • 1 = ⬅️

Ejempplo


ggplot(tbl_desocup_2020_25, aes(x = Anio, y = n)) +
  geom_col(fill = "#4F7CFF") +
  geom_label(
    aes(label = n),
    vjust = 1,
    hjust = 0,
    color = "black",
    size = 4,
    fontface = "bold"
  )

annotate(): Anotaciones flexibles

Agregando texto específico

ggplot(tbl_desocup_2020_25, aes(x = Anio, y = n)) +
  geom_line(color = "#4F7CFF", size = 1.5) +
  geom_point(color = "#4F7CFF", size = 3) +
  annotate(
    "text",
    x = 2021,
    y = max(tbl_desocup_2020_25$n),
    label = "Pico histórico\n2023",
    size = 4,
    color = "red",
    fontface = "bold"
  ) +
  theme_minimal()

annotate(): Elementos gráficos

Rectángulos y flechas

ggplot(tbl_desocup_2020_25, aes(x = Anio, y = n)) +
  geom_line(color = "#4F7CFF", size = 1.5) +
  geom_point(color = "#4F7CFF", size = 3) +
  annotate(
    "rect",
    xmin = 2019.5,
    xmax = 2022.5,
    ymin = 0,
    ymax = 3000,
    alpha = 0.2,
    fill = "red"
  ) +
  annotate(
    "text",
    x = 2021,
    y = 3000,
    label = "COVID-19",
    size = 4
  ) +
  theme_minimal()

ggrepel: Etiquetas que no se superponen

Instalación y uso básico

# install.packages("ggrepel")
library(ggrepel)
ggplot(mtcars, aes(x = disp, y = hp)) +
  geom_point(size = 4, color = "#4F7CFF") +
  geom_text_repel(
    aes(label = rownames(mtcars)),
    size = 3.5,
    fontface = "bold"
  ) +
  theme_minimal()

ggrepel: ejemplo

geom_text(): Etiquetas superpuestas

geom_text_repel(): Etiquetas legibles

🚀 Gráficos interactivos con plotly

¿Qué es plotly?

Una librería para visualizaciones interactivas

  • Tooltips (información al hacer hover)
  • Zoom y pan para explorar datos
  • Selección de elementos
  • Animaciones suaves
  • Compatible con ggplot2 a través de ggplotly()

Instalación

# install.packages("plotly")
library(plotly)

Uso básico

# Convertir ggplot a interactivo
p <- ggplot(data, aes(x, y)) +
  geom_point()

ggplotly(p)

ggplotly(): De estático a interactivo

Conversión simple

p <- ggplot(tbl_desocup_2020_25, aes(x = Anio, y = n)) +
  geom_line(group = 1, color = "#4F7CFF", size = 1.5) +
  geom_point(color = "#4F7CFF", size = 3) +
  scale_y_continuous(labels = scales::comma) +
  theme_minimal() +
  labs(title = "Desocupación interactiva", x = "Año", y = "Desocupados")

ggplotly(p)

Personalizando tooltips

Información adicional al hacer hover

p <- ggplot(
  tbl_desocup_2020_25,
  aes(
    x = Anio,
    y = n,
    text = paste(
      "Año:",
      Anio,
      "<br>Desocupados:",
      scales::comma(n),
      "<br>Período: 2020-2025"
    )
  )
) +
  geom_line(color = "#4F7CFF", size = 1.5) +
  geom_point(color = "#4F7CFF", size = 4) +
  theme_minimal() +
  labs(
    title = "Desocupación con tooltips personalizados",
    x = "Año",
    y = "Desocupados"
  )

ggplotly(p, tooltip = "text")

Gráficos con múltiples series

Interactividad con grupos

df_estados <- df_eph |>
  filter(ESTADO %in% c(1, 2)) |>
  count(ANO4, ESTADO) |>
  mutate(ESTADO = factor(ESTADO, labels = c("Ocupados", "Desocupados")))

p <- ggplot(df_estados, aes(x = ANO4, y = n, color = ESTADO)) +
  geom_line(size = 1.2) +
  geom_point(size = 3) +
  scale_color_manual(
    values = c("Ocupados" = "#4F7CFF", "Desocupados" = "#FF6B6B")
  ) +
  scale_y_continuous(labels = scales::comma) +
  theme_minimal() +
  labs(title = "Ocupados vs Desocupados", x = "Año", y = "Población")

ggplotly(p)

Configuraciones avanzadas de plotly

Control de la interactividad

p <- ggplot(tbl_desocup_2020_25, aes(x = Anio, y = n)) +
  geom_col(fill = "#4F7CFF") +
  theme_minimal()

ggplotly(p) %>%
  layout(
    title = list(
      text = "Desocupación - Configuración avanzada",
      font = list(size = 18)
    ),
    xaxis = list(title = "Año"),
    yaxis = list(title = "Desocupados")
  ) %>%
  config(
    displayModeBar = TRUE
  )

💾 Exportación y publicación

ggsave(): Guardando nuestras visualizaciones

Formatos y configuraciones

# PNG para web y presentaciones
ggsave(
  "grafico.png",
  plot = mi_grafico,
  width = 12,
  height = 8,
  dpi = 300,
  units = "cm"
)

# PDF para publicaciones académicas
ggsave("grafico.pdf", plot = mi_grafico, width = 8, height = 6, units = "in")

# SVG para máxima calidad y escalabilidad
ggsave("grafico.svg", plot = mi_grafico, width = 10, height = 7, units = "cm")

Configurando dimensiones y resolución

Guías por tipo de uso

📱 Redes sociales

  • Instagram: 1080x1080 px (cuadrado)
  • Twitter: 1024x512 px (2:1)
  • LinkedIn: 1200x627 px (~2:1)
ggsave("insta.png", width = 1080, height = 1080, units = "px", dpi = 96)

📊 Publicaciones

  • Presentaciones: 1280x720 px (16:9)
  • Artículos: 300 DPI, 6-8 pulgadas
  • Web: 96 DPI, tamaños flexibles
ggsave("articulo.png", width = 8, height = 6, units = "in", dpi = 300)

Ejemplo práctico: Guardando múltiples versiones

mi_grafico <- ggplot(tbl_desocup_2020_25, aes(x = Anio, y = n)) +
  geom_line(group = 1, color = "#4F7CFF", size = 1.5) +
  geom_point(color = "#4F7CFF", size = 3) +
  geom_text_repel(aes(label = scales::comma(n)), size = 3, fontface = "bold") +
  scale_y_continuous(labels = scales::comma) +
  theme_minimal() +
  labs(
    title = "Evolución de la desocupación en Argentina",
    subtitle = "Período 2020-2025",
    x = "Año",
    y = "Desocupados",
    caption = "Fuente: EPH - INDEC | Elaborado por Estación R"
  )

# Versión para web
ggsave(
  "desocupacion_web.png",
  plot = mi_grafico,
  width = 12,
  height = 8,
  units = "cm",
  dpi = 150
)

# Versión para imprimir
ggsave(
  "desocupacion_print.png",
  plot = mi_grafico,
  width = 8,
  height = 6,
  units = "in",
  dpi = 300
)

Publicación profesional

Elementos esenciales

Siempre incluir

  • Título descriptivo y claro
  • Subtítulo con contexto adicional
  • Fuente de datos en el caption
  • Ejes etiquetados apropiadamente
  • Leyendas cuando sea necesario
  • Créditos de elaboración

📐 Consideraciones técnicas

  • Resolución apropiada según el medio
  • Colores accesibles para daltonismo
  • Tipografía legible en el tamaño final
  • Proporciones adecuadas (16:9, 4:3, etc.)
  • Espaciado suficiente entre elementos

Ejercicio integrador final 🏆

Creá una visualización completa para publicación que incluya:

  1. Datos: Usar df_eph con una variable de tu elección
  2. Visualización: Gráfico apropiado para los datos elegidos
  3. Anotaciones: Al menos 2 elementos (texto, línea de referencia, etc.)
  4. Personalización: Paleta profesional, títulos, etiquetas
  5. Interactividad: Convertir a plotly con tooltips personalizados
  6. Exportación: Guardar en formato PNG de alta resolución

Datasets sugeridos:

# Ejemplo 1: Salarios por educación
df_salarios <- df_eph |>
  filter(!is.na(P21), NIVEL_ED %in% 1:7) |>
  group_by(ANO4, NIVEL_ED) |>
  summarise(salario_promedio = mean(P21, na.rm = TRUE))

# Ejemplo 2: Desocupación por edad
df_edad <- df_eph |>
  filter(ESTADO == 2) |>
  count(ANO4, CH06, name = "desocupados")

⏰ Tiempo: 15 minutos

Solución - Ejercicio integrador

df_edad_desocup <- df_eph |>
  filter(ESTADO == 2, CH06 >= 14, CH06 <= 65) |>
  mutate(
    grupo_edad = case_when(
      CH06 >= 14 & CH06 <= 24 ~ "14-24 años",
      CH06 >= 25 & CH06 <= 34 ~ "25-34 años",
      CH06 >= 35 & CH06 <= 54 ~ "35-54 años",
      CH06 >= 55 & CH06 <= 65 ~ "55-65 años"
    )
  ) |>
  count(ANO4, grupo_edad, name = "desocupados") |>
  drop_na()

promedio_total <- mean(df_edad_desocup$desocupados)

p_final <- ggplot(
  df_edad_desocup,
  aes(
    x = ANO4,
    y = desocupados,
    color = grupo_edad,
    text = paste(
      "Año:",
      ANO4,
      "<br>Grupo:",
      grupo_edad,
      "<br>Desocupados:",
      scales::comma(desocupados)
    )
  )
) +
  geom_line(size = 1.2) +
  geom_point(size = 3) +
  geom_hline(
    yintercept = promedio_total,
    linetype = "dashed",
    color = "gray40",
    size = 0.8
  ) +
  annotate(
    "text",
    x = 2021.5,
    y = promedio_total + 5000,
    label = paste("Promedio general:", scales::comma(round(promedio_total))),
    color = "gray40",
    fontface = "bold",
    size = 3.5
  ) +
  scale_color_brewer(type = "qual", palette = "Set2") +
  scale_y_continuous(labels = scales::comma) +
  theme_minimal() +
  theme(
    legend.position = "bottom",
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(color = "gray50")
  ) +
  labs(
    title = "Desocupación por grupos etarios en Argentina",
    subtitle = "Evolución 2020-2025 por rangos de edad",
    x = "Año",
    y = "Desocupados",
    color = "Grupo etario",
    caption = "Fuente: EPH - INDEC | Elaborado por Estación R"
  )

# Versión interactiva
ggplotly(p_final, tooltip = "text")
# Guardar versión final
ggsave(
  "desocupacion_por_edad_final.png",
  plot = p_final,
  width = 12,
  height = 8,
  units = "in",
  dpi = 300
)

📚 Recursos avanzados y próximos pasos


Extensiones avanzadas de ggplot2

🎬 Animaciones

  • {gganimate}: Gráficos animados
  • {magick}: Procesamiento de imágenes

🗺️ Mapas y geoespaciales

  • {sf}: Datos espaciales
  • {maps}: Mapas base
  • {leaflet}: Mapas interactivos

📊 Gráficos especializados

  • {ggraph}: Redes y grafos
  • {ggforce}: Formas y geometrías adicionales
  • {ggridges}: Gráficos de cresta
  • {waffle}: Gráficos de waffle

🎨 Temas avanzados

  • {hrbrthemes}: Temas tipográficos
  • {ggthemes}: Temas inspirados en medios
  • {cowplot}: Layouts científicos

Flujo de trabajo recomendado

📋 Checklist para visualizaciones profesionales

  1. 📊 Análisis exploratorio: Entender los datos primero
  2. 🎯 Definir objetivo: ¿Qué historia querés contar?
  3. 📝 Boceto inicial: Sketch en papel o mental
  4. ⚙️ Código iterativo: Empezar simple, agregar capas
  5. 🎨 Diseño y estética: Colores, títulos, temas
  6. 📝 Anotaciones: Guiar la lectura e interpretación
  7. ⟳ Iterar: Feedback y mejoras constantes
  8. ✅ Revisión: Verificar claridad y precisión
  9. 💾 Exportación: Formato y resolución apropiados

Comunidad y recursos continuos

🌐 Para seguir aprendiendo

Desafíos y práctica

Comunidades activas

  • R4DS Community: Slack colaborativo
  • RStudio Community: Foro oficial
  • #RStats: Twitter/X y Mastodon
  • r/rstats: Reddit especializado
  • Grupos R-Ladies / R en Buenos Aires: Meetups locales

📖 Libros esenciales

  • “ggplot2: Elegant Graphics for Data Analysis” - Hadley Wickham
  • “Fundamentals of Data Visualization” - Claus Wilke
  • “Data Visualization: A Practical Introduction” - Kieran Healy

Tu certificación y próximos pasos

🏆 Para obtener el certificado

  1. Completa la evaluación final en la plataforma
  2. Sube tu proyecto integrador (1 visualización usando todo lo aprendido)
  3. Participa en la sesión de feedback (opcional pero recomendada)


Feedback y evaluación

📝 Ayudanos a mejorar

Tu opinión es valiosa para seguir mejorando el taller:

  • Encuesta de satisfacción
  • Feedback específico: ¿Qué fue más/menos útil?
  • Sugerencias de contenido: ¿Qué te gustaría ver en futuros talleres?


🤝 Mantengámonos conectados

  • Canal Slack: Seguimos disponibles para consultas
  • Newsletter: Recursos y novedades de R
  • LinkedIn: Conectemos profesionalmente
  • GitHub: Compartí tus proyectos
  • Twitter/X: Taggea @estacion_erre en tus visualizaciones

¡Gracias!