Introducción a la visualización de datos

Cuadernos prácticos del Máster de Bioinformática (curso 2024-2025)

Author

Javier Álvarez Liébana

1 Visualización de datos

El paquete {ggplot2} se basa en la idea de Wilkinson en «Grammar of graphics»: dotar a los gráficos de una gramática propia. Una de las principales fortalezas de R es la visualización con {ggplot2}.

library(ggplot2)

La visualización de datos debería ser una parte fundamental de todo análisis de datos. No es solo una cuestión estética.

La filosofía detrás de {ggplot2} es entender los gráficos como parte del flujo de trabajo, dotándoles de una gramática. El objetivo es empezar con un lienzo en blanco e ir añadiendo capas a tu gráfico. La ventaja de {ggplot2} es poder mapear atributos estéticos (color, forma, tamaño) de objetos geométricos (puntos, barras, líneas) en función de los datos.

La documentación del paquete puedes consultarla en https://ggplot2-book.org/introduction.html

Un gráfico se podrá componer de capas

  • Datos (data)
  • Mapeado (aesthetics) de elementos estéticos: ejes, color, forma, etc (en función de los datos)
  • Geometría (geom): puntos, líneas, barras, polígonos, etc.
  • Componer gráficas (facet)
  • Transformaciones (stat): ordenar, resumir, etc.
  • Coordenadas (coord): coordenadas cartesianas, polares, grids, etc.
  • Temas (theme): fuente, tamaño de letra, subtítulos, captions, leyenda, ejes, etc.

1.1 Primer intento: scatter plot

Ahora que sabemos importar archivos, vamos a importar el conjunto de datos gapminder: un fichero con datos de esperanzas de vida, poblaciones y renta per cápita de distintos países en distintos momentos temporales.

library(readr)
gapminder <- read_csv(file = "./datos/gapminder.csv")
Rows: 1704 Columns: 6
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (2): country, continent
dbl (4): year, lifeExp, pop, gdpPercap

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
gapminder
# A tibble: 1,704 × 6
   country     continent  year lifeExp      pop gdpPercap
   <chr>       <chr>     <dbl>   <dbl>    <dbl>     <dbl>
 1 Afghanistan Asia       1952    28.8  8425333      779.
 2 Afghanistan Asia       1957    30.3  9240934      821.
 3 Afghanistan Asia       1962    32.0 10267083      853.
 4 Afghanistan Asia       1967    34.0 11537966      836.
 5 Afghanistan Asia       1972    36.1 13079460      740.
 6 Afghanistan Asia       1977    38.4 14880372      786.
 7 Afghanistan Asia       1982    39.9 12881816      978.
 8 Afghanistan Asia       1987    40.8 13867957      852.
 9 Afghanistan Asia       1992    41.7 16317921      649.
10 Afghanistan Asia       1997    41.8 22227415      635.
# ℹ 1,694 more rows

El fichero consta de 1704 registros y 6 variables: country, continent, year, lifeExp (esperanza de vida), pop (población) y gdpPercap (renta per cápita).

library(dplyr)

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
glimpse(gapminder)
Rows: 1,704
Columns: 6
$ country   <chr> "Afghanistan", "Afghanistan", "Afghanistan", "Afghanistan", …
$ continent <chr> "Asia", "Asia", "Asia", "Asia", "Asia", "Asia", "Asia", "Asi…
$ year      <dbl> 1952, 1957, 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1997, …
$ lifeExp   <dbl> 28.801, 30.332, 31.997, 34.020, 36.088, 38.438, 39.854, 40.8…
$ pop       <dbl> 8425333, 9240934, 10267083, 11537966, 13079460, 14880372, 12…
$ gdpPercap <dbl> 779.4453, 820.8530, 853.1007, 836.1971, 739.9811, 786.1134, …

Para empezar con algo sencillo filtraremos solo los datos de 1997

gapminder_1997 <- gapminder[gapminder$year == 1997, ]

Imagina que queremos dibujar un scatter plot (diagrama de dispersión de puntos, que enfrenta a una variable x con una variable y).

Si tuviéses que construir capa a capa el gráfico: ¿qué necesitarías?

1.1.1 Ingredientes: datos

¿Qué elementos necesitamos para realizar un diagrama de puntos? Para iniciar el lienzo VACÍO (de momento) necesitamos llamar una base de datos con ggplot(datos)

  • Datos (data): conjunto gapminder_1997.
ggplot(gapminder_1997)

1.1.2 Ingredientes: mapeo (x, y)

Ya tenemos linkada la base de datos pero necesitamos indicarle que variables de la tabla queremos que relacione con el gráfico (en este caso necesita mínimo una x y otra y).

  • Datos (data): conjunto gapminder_1997.

  • Mapeado: indicar dentro de aes() (aesthetics) las variables en cada coordenada. Todo dentro de aes() será mapeado de la base de datos

ggplot(gapminder_1997,
       aes(x = gdpPercap, y = pop))

1.1.3 Geometría: geom_point()

De momento sigue vacío ya que aún no le hemos indicado lo más importante: ¿qué tipo de gráfica queremos hacer?. Para eso usaremos las capas geométricas que empiezan por geom_xxx().

  • Geometría (geom): optaremos por puntos usando geom_point().
ggplot(gapminder_1997,
       aes(x = gdpPercap, y = pop)) +
  geom_point()

1.1.4 Rol de los ejes: (x, y)

Vamos a profundizar en ese mapeado: ¿cómo cambiar el rol de los ejes (población en el eje X y renta per cápita en el eje Y)?

  • Eje X: población (variable pop)
  • Eje Y: renta per cápita (variable gdpPercap)
ggplot(gapminder_1997,
       aes(x = pop, y = gdpPercap)) +
  geom_point() 

¿Y un scatter plot con esperanza de vida en eje X frente a renta per cápita?

  • Eje X: esperanza de vida (variable lifeExp)
  • Eje Y: renta per cápita (variable gdpPercap)
ggplot(gapminder_1997,
       aes(y = gdpPercap, x = lifeExp)) +
  geom_point()

1.1.5 Color, size, shape: fijos

Dentro de geom_point() tenemos varios parámetros estéticos a configurar:

  • na.rm = ...: si queremos que nos quite ausentes.

  • color = ...: color (si tiene dimensión, color del contorno)

  • fill = ...: color del relleno (si tiene dimensión)

Empezaremos por un color fijo, por ejemplo "red" (existen otros como "blue", "black", "yellow", etc)

ggplot(gapminder_1997,
       aes(y = gdpPercap, x = lifeExp)) +
  geom_point(color = "red")

  • size = ...: tamaño de la geometría (en este caso el tamaño de los puntos), cuanto mayor sea el número, mayor será el tamaño de la geometría.
ggplot(gapminder_1997,
       aes(y = gdpPercap, x = lifeExp)) +
  geom_point(color = "red", size = 7) 

  • alpha = ...: grado de opacidad del color (1 totalmente opaco, 0 totalmente transparente)
ggplot(gapminder_1997,
       aes(y = gdpPercap, x = lifeExp)) +
  geom_point(color = "red", size = 7,
             alpha = 0.4)

  • shape = ...: forma de la geometría, en este caso del «punto» (ver todas las opciones en vignette("ggplot2-specs"))
ggplot(gapminder_1997,
       aes(y = gdpPercap, x = lifeExp)) +
  geom_point(color = "red",
             fill = "black",
             size = 7,
             alpha = 0.4,
             shape = 23)

  • stroke = ...: tamaño del contorno
ggplot(gapminder_1997,
       aes(y = gdpPercap, x = lifeExp)) +
  geom_point(color = "red", size = 7,
             alpha = 0.4, stroke = 3)

Los colores también podemos asignárselos por su código hexadecimal, consultando en https://htmlcolorcodes.com/es/, eligiendo el color que queramos. El código hexadecimal siempre comenzará con #

# Color en hexadecimal
# https://htmlcolorcodes.com/es/
ggplot(gapminder_1997,
       aes(y = gdpPercap, x = lifeExp)) +
  geom_point(color = "#A02B85",
             alpha = 0.4, size = 7) 

1.2 Mapeado estético: aes()

Hasta ahora los atributos estéticos se los hemos pasado fijos y constantes. Pero la verdadera potencia y versatilidad de ggplot es que podemos mapear los atributos estéticos en función de los datos en aes() para que dependan de variables de los datos.

. . .

Por ejemplo, vamos a asignar un color a cada dato en función de su continente con aes(color = continent)

# Tamaño fijo
# Color por continentes
ggplot(gapminder_1997,
       aes(y = gdpPercap, x = lifeExp,
           color = continent)) +
  geom_point(size = 5)

Podemos combinarlo con lo que hemos hecho anteriormente:

  • color en función del continente.

  • tamaño en función de la población.

  • transparencia la fijamos constante del 50%.

ggplot(gapminder_1997,
       aes(y = gdpPercap, x = lifeExp,
           color = continent, size = pop)) +
  geom_point(alpha = 0.7)

A este scatter plot particular se le conoce BUBBLE CHART

Reflexionemos sobre el gráfico anterior:

  • color en función del continente.
  • tamaño en función de la población
  • transparencia fija del 50%

Usando los datos hemos conseguido dibujar en un gráfico bidimensional 4 variables: lifeExp y gdpPercap en los ejes , continent como color y pop como tamaño de la geometría, con muy pocas líneas de código.

1.3 Escalas (scale): ejes

Es importante entender que hasta ahora solo hemos indicado que un atribut estético A dependa de una variable B de los datos.

Por ejemplo, si aes(color = variable) le estoy diciendo que cada modalidad distinta de la variable adopte un color distinto pero…¿qué color?. Para ello usaremos una de las capas más importantes, las capas de escalas scale_xxx().

1.3.1 scale_x y scale_y

Hasta ahora dentro de aes() solo le indicábamos que variable mapeamos pero no sus ajustes. Por ejemplo, vamos a configurar el eje x para tener marcas personalizadas cada 10 unidades (scale_x_continuous(breaks = ...))

ggplot(gapminder_1997, aes(y = gdpPercap, x = lifeExp, color = continent)) +
  geom_point(alpha = 0.7) +
  scale_x_continuous(breaks = seq(35, 85, by = 10)) +
  labs(x = "Esperanza de vida", y = "Renta per cápita", title = "Primer ggplot",
       caption = "J. Álvarez Liébana", color = "continente")

1.3.2 scale_color y scale_fill

La misma idea la podemos aplicar a otro atríbuto como los colores con scale_color_...() y scale_fill_...(): hemos indicado que mapeé dicho atributo por continente pero…¿qué colores usar?

Con scale_color_manual() podemos indicar manualmente una paleta (puedes buscar en https://htmlcolorcodes.com/)

pal <- c("#A02B85", "#2DE86B", "#4FB2CA", "#E8DA2D", "#E84C2D")
ggplot(gapminder_1997, aes(y = gdpPercap, x = lifeExp, color = continent)) +
  geom_point(alpha = 0.7) +
  scale_x_continuous(breaks = seq(35, 85, by = 10)) +
  scale_color_manual(values = pal) +
  labs(x = "Esperanza de vida", y = "Renta per cápita",
       title = "Primer ggplot", caption = "J. Álvarez Liébana",
       color = "continente")

Otra opción es elegir alguna de las paletas de colores diseñadas en el paquete {ggthemes}:

  • scale_color_colorblind(): paleta de colores basada en los colores de daltónicos/as.
library(ggthemes)
ggplot(gapminder_1997, aes(y = gdpPercap, x = lifeExp, color = continent, size = pop)) +
  geom_point(alpha = 0.7) +
  scale_x_continuous(breaks = seq(35, 85, by = 10)) +
  scale_color_colorblind() +
  labs(x = "Esperanza de vida", y = "Renta per cápita", title = "Primer ggplot",
       caption = "J. Álvarez Liébana", color = "continente")

Incluso cargar paletas de colores diseñadas en base a películas o arte

  • películas: paquete {harrypotter} (repositorio de Github aljrico/harrypotter) usando scale_color_hp_d().

Paleta basada en la casa Ravenclaw
library(harrypotter)
ggplot(gapminder_1997, aes(y = gdpPercap, x = lifeExp, color = continent, size = pop)) +
  geom_point(alpha = 0.7) +
  scale_x_continuous(breaks = seq(35, 85, by = 10)) +
  scale_color_hp_d(option = "ravenclaw")+
  labs(x = "Esperanza de vida", y = "Renta per cápita", title = "Primer ggplot",
       caption = "J. Álvarez Liébana", color = "continente")

  • cuadros: paquete {MetBrewer} (repositorio de Github BlakeRMills/MetBrewer) usando scale_colour_manual(values = met.brewer(...)).
library(MetBrewer)
ggplot(gapminder_1997, aes(y = gdpPercap, x = lifeExp, color = continent, size = pop)) +
  geom_point(alpha = 0.7) +
  scale_x_continuous(breaks = seq(35, 85, by = 10)) +
  scale_color_manual(values = met.brewer("Monet")) +
  labs(x = "Esperanza de vida", y = "Renta per cápita", title = "Primer ggplot",
       caption = "J. Álvarez Liébana", color = "continente")

  • discos: paquete {peRReo} (repositorio de Github jbgb13/peRReo) usando scale_colour_manual(values = latin_palette()).
library(peRReo)
ggplot(gapminder_1997, aes(y = gdpPercap, x = lifeExp, color = continent, size = pop)) +
  geom_point(alpha = 0.7) +
  scale_x_continuous(breaks = seq(35, 85, by = 10)) +
  scale_color_manual(values = latin_palette("rosalia")) +
  labs(x = "Esperanza de vida", y = "Renta per cápita", title = "Primer ggplot",
       caption = "J. Álvarez Liébana", color = "continente")

1.3.3 scale_xxx

Lo mismo que hemos hecho para los ejes o colores podemos hacer para el resto de atríbutos estéticos

Por ejemplo, vamos a indicarle que mapeé el tamaño en función de población pero indicándole el rango de valores (continuo en este caso) entre los que moverse con scale_size_continuous()

ggplot(gapminder_1997, aes(y = gdpPercap, x = lifeExp, color = continent, size = pop)) +
  geom_point(alpha = 0.7) +
  scale_x_continuous(breaks = seq(35, 85, by = 10)) +
  ggthemes::scale_color_colorblind() +
  scale_size_continuous(range = c(3, 17)) +
  labs(x = "Esperanza de vida", y = "Renta per cápita", title = "Primer ggplot",
       caption = "J. Álvarez Liébana", color = "continente")

1.4 Etiquetas sencillas: labs()

Podemos también personalizar de manera sencilla haciendo uso de la capa labs():

  • title,subtitle: título/subtítulo
  • caption: pie de gráfica
  • x, y: nombres de los ejes
  • size, color, fill, ...: nombre en leyenda de las variables que codifiquen los atributos
ggplot(gapminder_1997,
       aes(y = gdpPercap, x = lifeExp, color = continent, size = pop)) +
  geom_point(alpha = 0.7) +
  ggthemes::scale_color_colorblind() +
  labs(x = "Esp. de vida",
       y = "Renta per cápita",
       title = "Primer ggplot",
       caption = "J. Álvarez Liébana",
       color = "continente",
       size = "población")

1.4.1 Eliminar de la leyenda

Podemos eliminar variables de la leyenda con guides(atributo = "none")

ggplot(gapminder_1997,
       aes(y = gdpPercap, 
           x = lifeExp,
           color = continent, 
           size = pop)) +
  geom_point(alpha = 0.7) +
  ggthemes::scale_color_colorblind() +
  guides(size = "none") +
  labs(x = "Esperanza de vida",
       y = "Renta per cápita",
       title = "Primer ggplot",
       caption = "J. Álvarez Liébana",
       color = "continente")

1.5 Tema (básico)

Por último en este primer gráfico, vamos personalizar el tema con alguna de las capas theme_...(). Por ejemplo, vamos a usar theme_minimal() para tener un tema “austero” y minimalista (aprenderemos a definir cada detalle de nuestro tema).

ggplot(gapminder_1997, aes(y = gdpPercap, x = lifeExp, color = continent, size = pop)) +
  geom_point(alpha = 0.7) +
  scale_x_continuous(breaks = seq(35, 85, by = 10)) +
  ggthemes::scale_color_colorblind() +
  labs(x = "Esperanza de vida", y = "Renta per cápita", title = "Primer ggplot",
       caption = "J. Álvarez Liébana", color = "continente") +
  theme_minimal()

2 Otros gráficos

El anterior ejemplo era un diagrama de dispersión (bivariante) que nos permite jugar mucho con los parámetros pero empezaremos aprendiendo a analizar una sola variable (estadística univariante).

Para ello algo fundamental será conocer la naturaleza de la variable ya que habrá cosas que podamos hacer con un tipo de variables y no con otras. Por ejemplo, vamos a tomar el fichero de datos starwars del paquete {dplyr} y vamos a empezar un ggplot

library(dplyr)
ggplot(starwars)

Si quiésemos hacer un diagrama de dispersión sencillo como antes entre peso y estatura sería así

ggplot(starwars) +
  geom_point(aes(x = mass, y = height)) +
  theme_minimal()
Warning: Removed 28 rows containing missing values or values outside the scale range
(`geom_point()`).

¿Qué línea habrá cambiar si en lugar de hacer un diagrama de dispersión queremos hacer un histograma de la estatura Lo que diferencia a un gráfico de otro es la capa geométrica, así que deberemos buscar un geom_xxx() distinto (en este caso geom_histogram())

2.1 Histogramas

Un histograma es una representación gráfica de la distribución de frecuencia de un conjunto de datos numéricos continuos. Consiste en un gráfico de barras PERO donde ahora la base de cada barra representa un intervalo de valores, y la altura de la barra indica la frecuencia o la densidad de datos dentro de ese intervalo: ahora la anchura de las barras sí es relevante (cuantifica la anchura del intervalo en el que estamos agrupando los datos).

Los histogramas son especialmente útiles para visualizar la forma y la dispersión de los datos aunque dependen de un parámetro crítico como es la cantidad de barras (o anchura de cada barra) de las que queramos disponer.

Fíjate que si lo cambiamos tal cual produce error. ¿Por qué?

ggplot(starwars) + geom_histogram(aes(x = mass, y = height))
Error in `geom_histogram()`:
! Problem while computing stat.
ℹ Error occurred in the 1st layer.
Caused by error in `setup_params()`:
! `stat_bin()` must only have an x or y aesthetic.

La capa usada (histograma) solo admite una x o un y –> habrás gráficos bivariantes y gráficos univariantes

ggplot(starwars) +
  geom_histogram(aes(x = height)) +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 6 rows containing non-finite outside the scale range
(`stat_bin()`).

Los histogramas dependen de una decisión crítica que cambiará por completo el gráfico: el número de barras (bins = ...).

Code
ggplot(starwars) +
  geom_histogram(aes(x = height), bins = 8) +
  theme_minimal()
Warning: Removed 6 rows containing non-finite outside the scale range
(`stat_bin()`).

2.2 Gráfico de densidad

Como hemos comentado una de las decisiones a tomar en un histograma es la anchura de las barras ya que dicho valor va a cambiar nuestra percepción de la distribución de los datos.

Una de las alternativas más conocidas son los conocidos como gráficos de densidad, una especie de aproximación continua de dichas barras, proporcionando la curva que se intuye que define la forma de un histograma si hacemos las barras cada vez más finas.

Para realizar un gráfico de densidad debemos de usar simplemente geom_density() como capa geométrica

Code
ggplot(starwars) +
  geom_density(aes(x = height)) +
  theme_minimal()
Warning: Removed 6 rows containing non-finite outside the scale range
(`stat_density()`).

Lo que hemo aprendido sobre los atributos estéticos de un gráfico podemos aplicarlo a cualquier capa. Por ejemplo, vamos a decirle que el color y relleno de la densidad dependa del sex (es decir, una densidad para cada uno).

Code
library(tidyverse)
ggplot(starwars |> drop_na(sex)) +
  geom_density(aes(x = height, fill = sex, color = sex),
               alpha = 0.3) +
  ggthemes::scale_color_colorblind() +
  ggthemes::scale_fill_colorblind() +
  theme_minimal()

El paquete {ggridges} y su función geom_density_ridges() nos permite visualizar densidades de distintos grupos en distintos niveles de altura: en lugar de tener todas una encima de otra, podremos indicarle una variable y

Code
library(ggridges)
ggplot(starwars |> drop_na(sex)) +
  geom_density_ridges(aes(x = height, y = sex,
                          fill = sex, color = sex),
               alpha = 0.3) +
  ggthemes::scale_color_colorblind() +
  ggthemes::scale_fill_colorblind() +
  theme_minimal()

2.3 Diagrama de sectores

Un gráfico de sectores, también conocido como gráfico circular o pie chart en inglés, es una representación visual de datos que muestra la proporción de cada categoría dentro de un conjunto de datos en forma de un círculo dividido en sectores. Cada sector representa una categoría y su tamaño angular es proporcional a la frecuencia o proporción de esa categoría en relación con el total.

2.4 Diagrama de barras

Utiliza barras rectangulares para mostrar la frecuencia, la magnitud o la proporción de diferentes categorías. Cada barra representa una categoría específica y su longitud es proporcional a la cantidad o frecuencia que representa. Es importante no confundir con un histograma: en un diagrama de barras la anchura de cada barra es una decisión meramente estética (incluso si dejas o no un hueco entre cada barra).

También pueden ser útiles para representar variables cuantitativas discretas (e.g., número de hijos).

2.5 Diagrama de cajas

Un diagrama de cajas (boxplots) es una representación gráfica que proporciona un resumen de varias características importantes de un conjunto de datos numéricos basado en medidas de posición.

El diagrama de cajas muestra la distribución de los datos a lo largo de un eje vertical, dividiendo el conjunto de datos en cuartiles y proporcionando información sobre la dispersión y la simetría de la distribución. Es importante tener en cuenta que en este gráfico, tanto la centralidad representada (mediana) como las medidas de posición (cuartiles) como los valores atípicos, se hace en relación a la mediana y no a la media.

Para realizarlos basta con usar geom_boxplot() como capa geométrica. Por ejemplo, vamos a realizar uno con la variable height

Code
ggplot(starwars |> drop_na(height)) +
  geom_boxplot(aes(x = height)) +
  theme_minimal()

Podemos invertir la posición de los boxplots indicándole la variable y en lugar de x.

Code
ggplot(starwars |> drop_na(height)) +
  geom_boxplot(aes(x = height)) +
  theme_minimal()

Podemos de nuevo incluir lo aprendido sobre atributos estéticos en estos gráficos. Por ejemplo, vamos a pintar un boxplot vertical (es decir y = height) por cada sexo (x = sex) distinguiendo los colores de cada uno.

Code
ggplot(starwars |> drop_na(height, sex)) +
  geom_boxplot(aes(x = sex, y = height, fill = sex,
                   color = sex), alpha = 0.5) +
  ggthemes::scale_color_colorblind() +
  ggthemes::scale_fill_colorblind() +
  theme_minimal()

La función geom_boxplot() nos permite incluso distinguir outliers en color, forma y tamaño (empiezan todos los parámetros por outlier...)

Code
ggplot(starwars |> drop_na(height, sex)) +
  geom_boxplot(aes(x = sex, y = height, fill = sex,
                   color = sex), alpha = 0.5,
               outlier.alpha = 0.8, outlier.shape = 23,
               outlier.size = 2, outlier.color = "#d72020",
               outlier.fill = "#d72020") +
  ggthemes::scale_color_colorblind() +
  ggthemes::scale_fill_colorblind() +
  theme_minimal()

Dichos gráficos podemos combinarlos con un gotelé de las observaciones haciendo uso de geom_jitter(), indicándole con width = ... la anchura del gotelé (a más valor, más disperso y confuso).

Code
ggplot(starwars |> drop_na(height, sex),
       aes(x = sex, y = height, fill = sex, color = sex)) +
  geom_boxplot(alpha = 0.5, outlier.alpha = 0) +
  geom_jitter(width = 0.3) +
  ggthemes::scale_color_colorblind() +
  ggthemes::scale_fill_colorblind() +
  theme_minimal()

2.6 Gráficos de violín

Los gráficos de cajas y bigotes o boxplot son una alternativa que mejora los clásicos gráficos dinámita o error bar plots (ver incovenientes de boxplots), uno de los gráficos más habituales en artículos científicos en el ámbito de la bioinformática y bioestadística.

A pesar de que son una mejor alternativa que los error bar plots, en muchas ocasiones puede ser interesante acompañar a nuestros boxplot con lo que se conoce como gráficos de violín (algo parecido a dos gráficos de densidad, uno pegado al otro de manera simétrica)

Para el gráfico de violín basta con geom_violin()

Code
ggplot(starwars |> drop_na(height, sex),
       aes(x = sex, y = height,
           fill = sex, color = sex)) +
  geom_violin(alpha = 0.5) +
  ggthemes::scale_color_colorblind() +
  ggthemes::scale_fill_colorblind() +
  theme_minimal()
Warning: Groups with fewer than two datapoints have been dropped.
ℹ Set `drop = FALSE` to consider such groups for position adjustment purposes.

Incluso puedes combinarlo con un boxplot

Code
ggplot(starwars |> drop_na(height, sex),
       aes(x = sex, y = height,
           fill = sex, color = sex)) +
  geom_violin(alpha = 0.5) +
  geom_boxplot(alpha = 0.2, width = 0.2,
               outlier.alpha = 0.8) +
  ggthemes::scale_color_colorblind() +
  ggthemes::scale_fill_colorblind() +
  theme_minimal()
Warning: Groups with fewer than two datapoints have been dropped.
ℹ Set `drop = FALSE` to consider such groups for position adjustment purposes.

3 Recursos para visualización

4 💻 Tu turno

Intenta realizar los siguientes ejercicios sin mirar las soluciones

📝 Toma el conjunto ya conocido de starwars del paquete {dplyr}. Realiza un diagrama de barras para contar las modalidades de la variable sex (solo eso, sin más opciones) eliminando antes los ausentes.

Code
starwars |> 
  drop_na(sex) |> 
  ggplot() +
  geom_bar(aes(x = sex))

📝 Añade lo que consideres del gráfico anterior para hacer que el relleno dependa de cada valor de sex. Personaliza tú los colores.

Code
starwars |> 
  drop_na(sex) |> 
  ggplot() +
  geom_bar(aes(x = sex, fill = sex), alpha = 0.7) +
  scale_fill_manual(values = c("#3b9559", "#e29c4d", "#4265b0", "#b0428b"))

📝 Añade lo que consideres del gráfico anterior para personalizar los títulos de los ejes, de la leyenda, poner un título al gráfico.

Code
starwars |> 
  drop_na(sex) |> 
  ggplot() +
  geom_bar(aes(x = sex, fill = sex), alpha = 0.7) +
  scale_fill_manual(values = c("#3b9559", "#e29c4d", "#4265b0", "#b0428b")) +
  labs(x = "Sexo", y = "Frec. absoluta",
       title = "Distribución de sexo en personajes\nde Starwars",
       fill = "Sexo")

📝 Añade lo que consideres del gráfico anterior para hacer que el eje y tenga una marca de 10 en 10 valores. Incluye un tema diferente al de por defecto

Code
starwars |> 
  drop_na(sex) |> 
  ggplot() +
  geom_bar(aes(x = sex, fill = sex), alpha = 0.7) +
  scale_fill_manual(values = c("#3b9559", "#e29c4d", "#4265b0", "#b0428b")) +
  scale_y_continuous(breaks = seq(0, 60, by = 10)) +
  labs(x = "Sexo", y = "Frec. absoluta",
       title = "Distribución de sexo en personajes\nde Starwars",
       fill = "Sexo") +
  theme_minimal()

📝 ¿Cómo podrías modificar el código anterior para que cada barra se divida en función de la variable género? Cada barra (para cada sexo) se dividirá en dos géneros: masculino y femenino.

Code
starwars |> 
  drop_na(sex) |> 
  ggplot() +
  geom_bar(aes(x = sex, fill = gender), alpha = 0.7) +
  scale_fill_manual(values = c("#e29c4d", "#4265b0")) +
  scale_y_continuous(breaks = seq(0, 60, by = 10)) +
  labs(x = "Sexo", y = "Frec. absoluta",
       title = "Distribución de sexo y género\nen personajes de Starwars",
       fill = "Género") +
  theme_minimal()

📝 Replica el gráfico del ejercicio 4 pero haciendo uso de las frecuencias relativas (cálculas antes para poder visualizarla)

Code
starwars |> 
  drop_na(sex) |> 
  count(sex) |> 
  mutate("frec_rel" = n/sum(n)) |> 
  ggplot() +
  geom_col(aes(x = sex, y = frec_rel, fill = sex), alpha = 0.7) +
  scale_fill_manual(values = c("#3b9559", "#e29c4d", "#4265b0", "#b0428b")) +
  scale_y_continuous(breaks = seq(0, 0.9, by = 0.1)) +
  labs(x = "Sexo", y = "Frec. absoluta",
       title = "Distribución de sexo en\n personajes de Starwars",
       fill = "Sexo") +
  theme_minimal()

📝 Visualiza un boxplot de la variable estatura distinguiendo por sexos

Code
starwars |> 
  drop_na(height, sex) |> 
  ggplot() + # le decimos que los outlier
  geom_boxplot(aes(x = sex, y = height, color = sex, fill = sex), alpha = 0.5) +
  scale_color_manual(values = c("#3b9559", "#e29c4d", "#4265b0", "#b0428b")) +
  scale_fill_manual(values = c("#3b9559", "#e29c4d", "#4265b0", "#b0428b")) +
  guides(color = "none") + # para eliminar color de leyenda
  labs(x = "Sexo", y = "Estatura",
       title = "Distribución de la estatura vs sexo en\n personajes de Starwars",
       fill = "Sexo") +
  theme_minimal()

5 🐣 Caso práctico: visualización covid

Vamos a practicar la visualización con la base de datos completa de covid del ISCIII que vamos a cargar directamente desde la web: en lugar de poner la ruta del .csv de nuestro ordenador vamos a poder usar el link directamente (clickando con botón derecho del ratón en los archivos de https://cnecovid.isciii.es/covid19/#documentaci%C3%B3n-y-datos)

library(readr)
datos <- read_csv(file = "https://cnecovid.isciii.es/covid19/resources/casos_hosp_uci_def_sexo_edad_provres.csv")
Rows: 1299030 Columns: 8
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (3): provincia_iso, sexo, grupo_edad
dbl  (4): num_casos, num_hosp, num_uci, num_def
date (1): fecha

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
datos
# A tibble: 1,299,030 × 8
   provincia_iso sexo  grupo_edad fecha      num_casos num_hosp num_uci num_def
   <chr>         <chr> <chr>      <date>         <dbl>    <dbl>   <dbl>   <dbl>
 1 A             H     0-9        2020-01-01         0        0       0       0
 2 A             H     10-19      2020-01-01         0        0       0       0
 3 A             H     20-29      2020-01-01         0        0       0       0
 4 A             H     30-39      2020-01-01         0        0       0       0
 5 A             H     40-49      2020-01-01         0        0       0       0
 6 A             H     50-59      2020-01-01         0        0       0       0
 7 A             H     60-69      2020-01-01         0        0       0       0
 8 A             H     70-79      2020-01-01         0        0       0       0
 9 A             H     80+        2020-01-01         0        0       0       0
10 A             H     NC         2020-01-01         0        0       0       0
# ℹ 1,299,020 more rows

5.1 Pregunta 1

Calcula en tidyverse un RESUMEN de tu tabla obteniendo el número de casos totales por fecha y sexo (es decir, sumando todos los casos para una fecha y un sexo dado, sin importar la provincia ni la edad)

Code
library(tidyverse)
resumen <-
  datos |> 
  summarise("casos" = sum(num_casos), .by = c(fecha, sexo))

5.2 Pregunta 2

Usando el resumen anterior crea otro de manera que tengas el total de casos por fecha (sin importar el sexo)

Code
resumen_total <-
  resumen |> 
  summarise("casos" = sum(casos), .by = fecha)

5.3 Pregunta 3

Con el dataset anterior, filtra entre 1 de marzo de 2020 y 30 de septiembre de 2020, y tras ello dibuja un diagrama de barras que nos visualice el número de casos por fecha

Code
resumen_total |> 
  filter(between(fecha, as_date("2020-03-01"), as_date("2020-09-30"))) |> 
  ggplot() +
  geom_col(aes(x = fecha, y = casos))

5.4 Pregunta 4

Personaliza el gráfico anterior para que las marcas del eje y vayan de 1000 en 1000 casos. Personaliza también las etiquetas de los eje, título, subtítulo y pie de figura. Añade un tema.

Code
resumen_total |> 
  filter(between(fecha, as_date("2020-03-01"), as_date("2020-09-30"))) |> 
  ggplot() +
  geom_col(aes(x = fecha, y = casos)) +
  scale_y_continuous(breaks = seq(0, 17000, by = 1000)) +
  labs(x = "Fecha", y = "Casos totales notificados",
       title = "Evolución del covid",
       subtitle = "Fechas: desde el 1 de marzo hasta el 30 de septiembre de 2020",
       caption = "Fuente: ISCIII") +
  theme_minimal()

5.5 Pregunta 5

Repite el gráfico anterior pero con el resumen inicial por sexo y fecha. Piensa como distinguir los casos notificados de cada sexo. Personaliza los colores que uses.

Code
resumen |> 
  filter(between(fecha, as_date("2020-03-01"), as_date("2020-09-30"))) |> 
  ggplot() +
  geom_col(aes(x = fecha, y = casos, fill = sexo),
           alpha = 0.5) +
  scale_y_continuous(breaks = seq(0, 17000, by = 1000)) +
  ggthemes::scale_fill_colorblind() +
  labs(x = "Fecha", y = "Casos totales notificados",
       title = "Evolución del covid",
       subtitle = "Fechas: desde el 1 de marzo hasta el 30 de septiembre de 2020",
       caption = "Fuente: ISCIII",
       fill = "sexo") +
  theme_minimal()

5.6 Pregunta 6

Guarda la gráfica anterior en una variable. Con la capa theme() podemos, con una gráfica ya hecha, modificar cada elemento de su tema. Por ejemplo, investiga theme(legend.position = ...) para poner la leyenda debajo de la gráfica en lugar de la derecha.

Code
grafica <-
  resumen |> 
  filter(between(fecha, as_date("2020-03-01"), as_date("2020-09-30"))) |> 
  ggplot() +
  geom_col(aes(x = fecha, y = casos, fill = sexo),
           alpha = 0.5) +
  scale_y_continuous(breaks = seq(0, 17000, by = 1000)) +
  ggthemes::scale_fill_colorblind() +
  labs(x = "Fecha", y = "Casos totales notificados",
       title = "Evolución del covid",
       subtitle = "Fechas: desde el 1 de marzo hasta el 30 de septiembre de 2020",
       caption = "Fuente: ISCIII",
       fill = "sexo") +
  theme_minimal()

grafica +
  theme(legend.position = "bottom")

5.7 Pregunta 7

Convierte la gráfica anterior a gráfica interactiva con el paquete {plotly}

Code
plotly::ggplotly(grafica)

5.8 Pregunta 8

Con el resumen por fecha y sexo, visualiza la distribución de casos diarios mediante un boxplot (uno por cada sexo). Marca los outliers de manera diferenciada.

Code
resumen |> 
  filter(between(fecha, as_date("2020-03-01"), as_date("2020-09-30"))) |> 
  ggplot() +
  geom_boxplot(aes(x = sexo, y = casos, color = sexo, fill = sexo),
               alpha = 0.5, outlier.alpha = 0.9, outlier.shape = 23,
               outlier.size = 2.5, outlier.fill = "#900000") +
  ggthemes::scale_color_colorblind() +
  ggthemes::scale_fill_colorblind() +
  labs(x = "Sexo", y = "Casos diarios notificados",
       title = "Evolución del covid",
       subtitle = "Fechas: desde el 1 de marzo hasta el 30 de septiembre de 2020",
       caption = "Fuente: ISCIII") +
  theme_minimal()