Gráficos con ggplot2 (C): Tipos de gráficos

Programación y manejo de datos con R (Slides 06-C)

Pedro J. Pérez

Galerías con ejemplos

En estas slides presentaremos ejemplos de algunos de los gráficos más utilizados en el análisis de datos. Puedes ver listados más completos en:


Evidentemente, el tipo de gráfico se ha de elegir en función de la información que se quiere mostrar. [Aquí] puedes encontrar ayuda para elegir el tipo de gráfico. Por ejemplo:

[From Data to Viz], [The Data Visualisation Catalogue], [The chart chooser], [The Chart Chooser: Interactive Version], etc …

¿Qué gráfico uso?

Depende de qué quieres mostrar y de qué tipo de variables tienes:

Qué quieres mostrar Variables Gráfico geom
La distribución de una variable 1 continua histograma, densidad geom_histogram(), geom_density()
Comparar distribuciones entre grupos 1 continua + 1 categórica boxplot, violín, ridges geom_boxplot(), geom_violin(), ggridges
Cuántas observaciones hay de cada categoría 1 categórica barras geom_bar()
Un valor (ya calculado) para cada categoría 1 categórica + 1 numérica barras geom_col()
La evolución en el tiempo fecha + 1 continua líneas geom_line()
La relación entre dos variables 2 continuas puntos (scatter) geom_point()

En las siguientes slides veremos un ejemplo de cada uno

6.1 Histogramas con geom_histogram()

ggplot(iris, aes(Sepal.Length)) +
ggplot(iris, aes(Sepal.Length)) +
geom_histogram(bins = 60) + labs(title = "60 intervalos (o bins)") +
ggplot(iris, aes(Sepal.Length)) +
geom_histogram(bins = 60) + labs(title = "60 intervalos (o bins)") +
geom_histogram(bins = 40) + labs(title = "40 intervalos") +
ggplot(iris, aes(Sepal.Length)) +
geom_histogram(bins = 60) + labs(title = "60 intervalos (o bins)") +
geom_histogram(bins = 40) + labs(title = "40 intervalos") +
geom_histogram(binwidth = 0.5) + labs(title = "En lugar de elegir el nº de intervalos, \n se puede elegir la anchura del intervalo: \n binwidth =  0.5") +
ggplot(iris, aes(Sepal.Length)) +
geom_histogram(bins = 60) + labs(title = "60 intervalos (o bins)") +
geom_histogram(bins = 40) + labs(title = "40 intervalos") +
geom_histogram(binwidth = 0.5) + labs(title = "En lugar de elegir el nº de intervalos, \n se puede elegir la anchura del intervalo: \n binwidth =  0.5") +
geom_histogram(binwidth = 0.5,
               color = "black",
               fill = "tomato") + labs(title = "binwidth =  0.5, color = black, fill = tomato") +
ggplot(iris, aes(Sepal.Length)) +
geom_histogram(bins = 60) + labs(title = "60 intervalos (o bins)") +
geom_histogram(bins = 40) + labs(title = "40 intervalos") +
geom_histogram(binwidth = 0.5) + labs(title = "En lugar de elegir el nº de intervalos, \n se puede elegir la anchura del intervalo: \n binwidth =  0.5") +
geom_histogram(binwidth = 0.5,
               color = "black",
               fill = "tomato") + labs(title = "binwidth =  0.5, color = black, fill = tomato") +
geom_histogram(binwidth = 0.5,
               aes(fill = Species),
               color = "black") + labs(title = "binwidth =  0.5, color = black, aes(fill = Species)") +
ggplot(iris, aes(Sepal.Length)) +
geom_histogram(bins = 60) + labs(title = "60 intervalos (o bins)") +
geom_histogram(bins = 40) + labs(title = "40 intervalos") +
geom_histogram(binwidth = 0.5) + labs(title = "En lugar de elegir el nº de intervalos, \n se puede elegir la anchura del intervalo: \n binwidth =  0.5") +
geom_histogram(binwidth = 0.5,
               color = "black",
               fill = "tomato") + labs(title = "binwidth =  0.5, color = black, fill = tomato") +
geom_histogram(binwidth = 0.5,
               aes(fill = Species),
               color = "black") + labs(title = "binwidth =  0.5, color = black, aes(fill = Species)") +
facet_grid(cols = vars(Species))

¿Por qué elegir bins o binwidth? Si no lo hacemos, ggplot2 usa 30 intervalos y nos avisa: stat_bin() using bins = 30. Pick better value with binwidth. El número de intervalos cambia mucho la forma del histograma: hay que probar varios.

6.2 geom_density() y Joy Division plots

ggplot(iris, aes(Sepal.Length, fill = Species)) + geom_density(alpha = 0.5)
ggplot(iris, aes(x = Sepal.Length, y = Species)) +
      ggridges::geom_density_ridges(aes(fill = Species), alpha = 0.5)

¿Por qué “Joy Division plots”?

  • Los gráficos de ridges (crestas) se llaman así por la portada del disco Unknown Pleasures (1979) del grupo Joy Division

  • La portada era un gráfico de las señales de un púlsar, con muchas curvas apiladas. Lo podemos imitar con ggridges 😎

set.seed(1979)
pulsar <- tibble(linea = rep(1:40, each = 300)) |>
  mutate(pico = if_else(runif(n()) < 0.3,
                        rnorm(n(), 0, 0.3), 0),
         x = rnorm(n()) + pico)

ggplot(pulsar, aes(x, y = linea, group = linea)) +
  ggridges::geom_density_ridges(fill = "black",
        color = "white", scale = 4, linewidth = 0.4) +
  theme_void() +
  theme(plot.background = element_rect(fill = "black"))

6.3 Gráficos de caja (Boxplots)

ggplot(iris, aes(x = Species,  y = Sepal.Length)) +
ggplot(iris, aes(x = Species,  y = Sepal.Length)) +
geom_boxplot() +
ggplot(iris, aes(x = Species,  y = Sepal.Length)) +
geom_boxplot() +
geom_boxplot(aes(fill = Species),
             outlier.colour = "purple") +
ggplot(iris, aes(x = Species,  y = Sepal.Length)) +
geom_boxplot() +
geom_boxplot(aes(fill = Species),
             outlier.colour = "purple") +
geom_jitter(width = 0.15, alpha = 1/4, color = "tomato") +
ggplot(iris, aes(x = Species,  y = Sepal.Length)) +
geom_boxplot() +
geom_boxplot(aes(fill = Species),
             outlier.colour = "purple") +
geom_jitter(width = 0.15, alpha = 1/4, color = "tomato") +
stat_summary(fun = "mean", geom = "point", color = "purple", size = 2.5) +
ggplot(iris, aes(x = Species,  y = Sepal.Length)) +
geom_boxplot() +
geom_boxplot(aes(fill = Species),
             outlier.colour = "purple") +
geom_jitter(width = 0.15, alpha = 1/4, color = "tomato") +
stat_summary(fun = "mean", geom = "point", color = "purple", size = 2.5) +
geom_violin(aes(fill = Species), alpha = 0.6) +
ggplot(iris, aes(x = Species,  y = Sepal.Length)) +
geom_boxplot() +
geom_boxplot(aes(fill = Species),
             outlier.colour = "purple") +
geom_jitter(width = 0.15, alpha = 1/4, color = "tomato") +
stat_summary(fun = "mean", geom = "point", color = "purple", size = 2.5) +
geom_violin(aes(fill = Species), alpha = 0.6) +
coord_flip()
  • Ojo: al añadir geom_jitter() los outliers salen dos veces (como outlier del boxplot y como punto). Se evita con geom_boxplot(outlier.shape = NA)

  • Las capas se dibujan en el orden en que las añadimos: por eso el violín tapa la caja

6.4 Gráficos de barras

ggplot(mpg, aes(class)) +
ggplot(mpg, aes(class)) +
geom_bar() +
ggplot(mpg, aes(class)) +
geom_bar() +
geom_bar(fill = "steelblue") +
ggplot(mpg, aes(class)) +
geom_bar() +
geom_bar(fill = "steelblue") +
coord_flip() +
ggplot(mpg, aes(class)) +
geom_bar() +
geom_bar(fill = "steelblue") +
coord_flip() +
geom_bar(aes(forcats::fct_rev(forcats::fct_infreq(class))),
         fill = "steelblue") +
ggplot(mpg, aes(class)) +
geom_bar() +
geom_bar(fill = "steelblue") +
coord_flip() +
geom_bar(aes(forcats::fct_rev(forcats::fct_infreq(class))),
         fill = "steelblue") +
labs(x = NULL)
  • fct_infreq() ordena las categorías por frecuencia y fct_rev() les da la vuelta. Como el nombre del eje sería toda esa expresión, lo quitamos con labs(x = NULL)

  • Hoy en día no hace falta coord_flip(): basta con poner la variable en el eje Y: ggplot(mpg, aes(y = class)) + geom_bar()

6.4 geom_bar() vs geom_col() [ 🌟 ]

¿Cuántos pingüinos hay de cada especie? El mismo gráfico se puede hacer de dos formas:

geom_bar(): R cuenta por ti. Le das los datos originales (una fila por pingüino) y sólo x

ggplot(penguins, aes(x = species)) +
  geom_bar()

geom_col(): tú le das la altura. Primero cuentas (count() crea la columna n) y luego le das x e y

penguins |> count(species) |>
  ggplot(aes(x = species, y = n)) +
  geom_col()

Regla: ¿una fila por observación? → geom_bar(). ¿Ya tienes calculado el valor de cada barra (un recuento, una media, un %…)? → geom_col()

6.5 Gráficos de líneas con geom_line()

economics |>
economics |>
ggplot(aes(date, unemploy)) +
economics |>
ggplot(aes(date, unemploy)) +
geom_line() +
economics |>
ggplot(aes(date, unemploy)) +
geom_line() +
geom_line(color = "steelblue", linewidth = 1) +
economics |>
ggplot(aes(date, unemploy)) +
geom_line() +
geom_line(color = "steelblue", linewidth = 1) +
scale_x_date(date_breaks = "10 years", date_labels = "%Y") +
economics |>
ggplot(aes(date, unemploy)) +
geom_line() +
geom_line(color = "steelblue", linewidth = 1) +
scale_x_date(date_breaks = "10 years", date_labels = "%Y") +
labs(x = NULL, y = "Desempleados (miles)",
     title = "Desempleo en EE.UU.")
#> # A tibble: 6 × 6
#>   date         pce    pop psavert uempmed unemploy
#>   <date>     <dbl>  <dbl>   <dbl>   <dbl>    <dbl>
#> 1 1967-07-01  507. 198712    12.6     4.5     2944
#> 2 1967-08-01  510. 198911    12.6     4.7     2945
#> 3 1967-09-01  516. 199113    11.9     4.6     2958
#> 4 1967-10-01  512. 199311    12.9     4.9     3143
#> 5 1967-11-01  517. 199498    12.8     4.7     3066
#> 6 1967-12-01  525. 199657    11.8     4.8     3018

6.5 Gráficos de líneas: varias series

  • Si los datos están en formato largo (una columna con el nombre de la serie y otra con su valor), basta con mapear esa columna a color
economics_long |>
  filter(variable %in% c("psavert", "uempmed")) |>
  ggplot(aes(date, value, color = variable)) +
  geom_line(linewidth = 0.8) +
  labs(x = NULL, y = NULL, color = NULL)

6.6 Scatter plots: el problema del overplotting

  • Con muchas observaciones (diamonds tiene casi 54.000) los puntos se tapan unos a otros y no vemos dónde hay más datos. Soluciones: transparencia (alpha) o contar puntos por zonas (geom_bin2d())
ggplot(diamonds, aes(carat, price)) + geom_point()
ggplot(diamonds, aes(carat, price)) + geom_point(alpha = 0.05)
ggplot(diamonds, aes(carat, price)) + geom_bin2d(bins = 60)

7. Gráficos interactivos

Permiten al usuario interactuar con el gráfico, abriéndose posibilidades como: zooming, highlight, o mostrar información adicional al pinchar en algún elemento del gráfico, etc …

7. Gráficos interactivos

  • Los gráficos interactivos permiten al usuario interactuar con el gráfico.

  • En general, JavaScript (JS) es el lenguaje utilizado para hacer gráficos interactivos con librerías como D3, Chart.js, Plotly, vis.js, Highcharts, …

  • Desde hace años, el paquete de R htmlwidgets facilita el uso de las librerías de JS en R.

  • Paquetes de R como leaflet, DT, dygraphs, networkD3 y muchos otros utilizan el framework de htmlwidgets para hacer disponibles los gráficos interactivos de JS en R. [Aquí] puedes ver los paquetes que usan htmlwidgets

  • [Aquí] puedes ver una galería con ejemplos.

7. Gráficos interactivos (plotly)

library(plotly)
p <- ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +  geom_point() + geom_smooth()
ggplotly(p)

7. Gráficos interactivos (plotly)

p1 <- p + facet_grid(cols = vars(Species))
ggplotly(p1)

7. Gráficos interactivos (ggiraph)

  • ggiraph es otra opción: en lugar de geom_point() usamos geom_point_interactive() y decidimos qué se ve al pasar el ratón (tooltip) y qué se resalta (data_id)
library(ggiraph)
p_g <- ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
  geom_point_interactive(aes(tooltip = paste(Species, Petal.Length), data_id = Species), size = 2)

girafe(ggobj = p_g, width_svg = 8, height_svg = 3.5)

7. Otros htmlwidgets: tablas (DT) y mapas (leaflet)

DT::datatable(iris, options = list(pageLength = 5))
library(leaflet)
leaflet() |>
  addTiles() |>
  addMarkers(lng = -0.3425, lat = 39.4785, popup = "Facultat d'Economia (UV)")

Para practicar: los pingüinos de Palmer

Tarea: con los datos penguins (vienen con R desde la versión 4.5):

  1. Haz un boxplot del peso (body_mass) para cada especie (species), con los puntos encima (geom_jitter()). ¡Que los outliers no salgan dos veces!

  2. Haz un gráfico de barras con el número de pingüinos de cada isla (island), coloreando las barras según la especie

ggplot(penguins, aes(species, body_mass)) +
  geom_boxplot(outlier.shape = NA) +
  geom_jitter(width = 0.15, alpha = 0.4, color = "steelblue")

ggplot(penguins, aes(island, fill = species)) +
  geom_bar()