library(learnr)
library(ggplot2)
library(dplyr)
knitr::opts_chunk$set(echo = FALSE, comment = "")
gapminder <- dataviz::gapminder

color_continentes <- c("Europe" = "darkorange", "Asia" = "red", "Africa" = "blue",
                       "Americas" = "yellow", "Oceania" = "purple")

Introducción

Cuando genera un gráfico, ggplot nos hace la vida más fácil tomando un montón de decisiones por nosotros: el color de los ítems con los que representamos los datos, el rango numérico de los ejes (en que número empiezan y terminan), el color de fondo, etc. Esto nos permite ver como lucen los datos tipeando lo menos posible, pero a la hora de compartir nuestras visualizaciones con el mundo vale la pena hacer ajustes para obtener aún mejores mejores resultados.

knitr::include_graphics("https://miro.medium.com/max/3900/0*DDRU1L5LIJkgDBxd.png")

visualizaciones realizadas con ggplot2 por el equipo de visualización y periodismo de datos de la BBC

Ahora iremos más allá del aspecto que tienen los gráficos de ggplot por defecto, puliéndolos para para su publicación.

Para empezar, activamos ggplot2:

library(ggplot2)

El data set con el que practicaremos fue compilado por Gapminder, una ONG sueca dedicada a explicar el mundo con datos. Contiene indicadores de desarrollo de países en todo el mundo, con observaciones en intervalos de 5 años:

gapminder

Inspiración con un gráfico famoso

Los gráficos de dispersión, o scatterplots, son quizás el tipo de visualización más conocido. Consisten en puntos proyectados en un eje de coordenadas, donde cada punto representa una observación. Son útiles para mostrar la correlación entre dos variables numéricas.

Por ejemplo, podríamos comparar la relación entre la riqueza de los países (medida como PBI per capita) y la salud de sus habitantes (como expectativa de años de vida). Esa es la visualización de datos que popularizó Hans Rosling.

knitr::include_graphics("https://bitsandbricks.github.io/img/hans_rosling.png")

Hans Rosling en su salsa

Hans Rosling fue el médico, fanático de la estadística, optimista y entusiasta comunicador que fundó el proyecto Gapminder, cuya misión declarada es "luchar contra la ignorancia devastadora con una visión del mundo basada en hechos, que todo el mundo pueda entender".

Antes de seguir, dediquemos cuatro minutos a mirar "200 años, 200 países, 4 minutos", un resumen ilustrado de los últimos dos siglos de desarrollo económico que Rosling preparó con ayuda de la BBC.

Intentemos reproducir la visualización de Rosling. Con lo que aprendimos hasta aquí, deberíamos poder realizar un gráfico de dispersión básico -con geom_point()- ubicando la variable "PBIpc" en el eje de las $x$, y "expVida" en el eje de las $y$:

ggplot(gapminder, ___) +
  ___()
ggplot(gapminder, aes(___)) +
  geom_point()
ggplot(gapminder, aes(x = PBIpc, y = ___)) +
  geom_point()
ggplot(gapminder, aes(x = PBIpc, y = expVida)) +
  geom_point()

Hmmm... ¡no se ve muy parecido!. Lo que ocurre es que Rosling utiliza un truco : usa una escala logarítmica. Si revisamos la imagen de Rosling presentando que aparece más arriba, vemos que en el eje de las $y$ la escala salta de 400 a 4.000, y luego a 40.000: en segmentos del mismo largo, los valores no crecen proporcionalmente sino en potencias de 10. Esto se logra tomando el logaritmo en base 10 de los datos antes de proyectarlos, con lo cual lo que estamos mostrando es 4 x 10^1^, 4 x 10^2^, 4 x 10^3^, etc. ¿Para que sirve esto? Para visualizar datos en los que existen grandes disparidades en una variable, porque permite que los valores pequeños tengan espacio para diferenciarse, y a la vez que los muy grandes no aparezcan tan alejados, logrando un gráfico más compacto y en general más legible. Cuando hay dinero involucrado, suele ser necesario invocar a la escala logarítmica para mejorar la legibilidad... ¡las grandes disparidades abundan!

En todo caso, ggplot2 incluye varias funciones para transformar las escala de las $x$ o de las $y$, entre ellas las que pasan las variables a escala logarítmica de base 10: scale_x_log10() y scale_x_log10(). ¿Cual usaríamos aquí?

ggplot(gapminder, aes(x = PBIpc, y = expVida)) +
  geom_point() +
  scale__
ggplot(gapminder, aes(x = PBIpc, y = expVida)) +
  geom_point() +
  scale_x_log10()
ggplot(gapminder, aes(x = PBIpc, y = expVida)) +
  geom_point() +
  scale_x_log10()

Ahora se vislumbra una disposición similar a la del gráfico del video, pero en nuestro caso parece haber demasiados puntos. Esto es porque nuestra data incluye 12 observaciones para cada país (cada 5 años desde 1952 hasta 2007). ¡O sea que cada país aparece 12 veces! El Dr. Rosling usaba sólo un punto por país, usando animación para mostrar distintos años.

Vamos a filtrar los datos para quedarnos sólo con la observación más reciente disponible, la de 2007. Hay muchas, pero muchas maneras de hacer eso en R, pero aquí vamos a usar la función filter() del paquete dplyr. Si no conocen esta herramienta, o necesitan un repaso rápido, pueden revisar "transformando los datos". Por ahora la vamos a usar de forma muy simple: en lugar de llamar a la variable gapminder, vamos a usar el resultado de filtrar su contenido de forma tal que solo usemos las filas donde la variable "año" toma el valor 2007.

Activamos el paquete dplyr

library(dplyr)

Y ahora ya podemos usar su función filter() para modificar nuestro dataset:

ggplot(filter(gapminder, año == 2007), aes(x = PBIpc, y = expVida)) +
  geom_point() +
  scale_x_log10()

Ahí va queriendo. Necesitamos dos ajustes más para que el parecido sea evidente. Rosling usa el tamaño de los puntos para indicar la población del país, y el color para diferenciar entre continentes. Con lo que aprendimos en la clase anterior, podemos animarnos: las variables que que se mostrarán con esos atributos estéticos son "pobl" y "continente"

ggplot(filter(gapminder, año == 2007), aes(x = PBIpc, y = expVida, ___ = ___, ___ = ___)) +
  geom_point() +
  scale_x_log10()
ggplot(filter(gapminder, año == 2007), aes(x = PBIpc, y = expVida, size = ___, color = ___)) +
  geom_point() +
  scale_x_log10()
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl, color = continente)) +
  geom_point() +
  scale_x_log10()

¡Eso si es un plot gapmindereano!. Pero esto no termina aquí. Quedan bastantes cosas para ajustar antes de que nuestro querido gráfico esté listo para ser compartido con el mundo.

Ajustar los detalles

Eliminando leyendas

En el video de la BBC, la visualización se presenta sin leyendas para reducir al mínimo el texto en pantalla, y que se pueda mirar como casi un cuadro. Para eliminar leyendas en nuestro ggplot, podemos usar guides, y dentro asignar el valor "none" a cada atributo estético para el cual no queremos leyenda - por ejemplo, color = "none". ¿Cómo eliminamos la leyenda de los atributos color y tamaño?

ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl, color = continente)) +
  geom_point() +
  scale_x_log10() +
  guides(___ = ___, ___ = ___)
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl, color = continente)) +
  geom_point() +
  scale_x_log10() +
  guides(color = ___, ___ = ___)
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl, color = continente)) +
  geom_point() +
  scale_x_log10() +
  guides(color = "none", ___ = ___)
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl, color = continente)) +
  geom_point() +
  scale_x_log10() +
  guides(size = "none", color = "none")

Lidiando con números grandes

Con frecuencia preferimos quedarnos con la leyenda, para invitar a la audiencia a que compare los valores representados. En ese caso vale la pena asegurarse de que los valores mostrados son razonablemente fáciles de interpretar.

En nuestro gráfico, algo que podemos mejorar es la representación de los números muy grandes: esos millones de personas de la población. Por defecto, R usa notación científica para abreviar los números muy grandes (o muy anchos, deberíamos decir: los números ínfimos, con muchos espacios decimales en cero, también se abrevian). Por eso en nuestro gráfico aparecen valores como "1.25e+09", que significa "1,25 * 10 elevado a la 9", o sea 1.250 millones. La brevedadad es buena a la hora de comunicar, pero no cuando se trata de números - la escala para la variable de población, en notación científica, resulta muy difícil de interpretar para la mayoría de las personas:

ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl, color = continente)) +
  geom_point() +
  scale_x_log10() 

Se puede hacer que R se abstenga de abreviar usando la notación científica (ejecutando la línea options(scipen=999)). Pero una solución aún mejor, cuando de visualización se trata, es la de convertir unidades. Por ejemplo, si los valores de población alcanzan valores tan grandes, representemos millones de personas y con eso los valores van a ser mas pequeños: en lugar de mostrar 1.000.000, ahora veremos solo "1" (porque la unidad es "millones de habitantes").

Entonces nos quedamos con las leyendas (ya no las deshabilitamos con guides()), y le podemos pedirle a ggplot que el tamaño de los puntos ya no represente los valores de la columna "pobl", si no el resultado de los valores de la columna divididos por un millón. ¿Cómo sería?

ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = ___, color = continente)) +
  geom_point() +
  scale_x_log10()
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1______, color = continente)) +
  geom_point() +
  scale_x_log10()
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10()

Elijiendo colores a mano

Si comparamos nuestros resultados con los de la visualización televisada por la BBC, notaremos que los colores usados para representar la variable "continente" son distintos. Esto es porque Rosling y su equipo los eligieron a su gusto, mientras que nosotros estamos usando los colores que elige ggplot de forma automática. En la próxima clase veremos como usar escalas de colores pre-diseñadas, con colores elegidos por sus propiedades ideales para usar en visualización. Pero ahora mismo tenemos la oportunidad de elegir de forma explícita los colores que queremos usar, para replicar los del ejemplo. Al cumplirse un minuto del video, oímos a Hans nombrar los colores que usa: "Europe brown, Asia Red...". Con eso podemos armar una lista con valores y sus colores asignados para pasarle a ggplot(). Tomándonos nada menos que tres licencias: no vamos a asignarle un color específico a los países del Medio Oriente porque no aparecen diferenciados en nuestra data, vamos a elegir un color para los dos países de Oceanía que tenemos, y para Europa vamos a elegir un naranja oscuro, porque ese parece ser el color que usaron en lugar de marrón.

En R tenemos nada menos que 657 colores que podemos llamar por nombre, como "hotpink" o "aquamarine". COmo es imposible recordarlos a todos, se puede recurrir a una guía de colores para encontrar los que necesitamos. Habiendo identificado nuestros colores, definimos la escala manual así:

color_continentes <- c("Europe" = "darkorange", "Asia" = "red", "Africa" = "blue",
                       "Americas" = "yellow", "Oceania" = "purple")

Y luego podemos usarla para indicarle a ggplot los colores exactos que queremos usar. Para eso sirve scale_color_manual(), con su parámetro "values". Probemos:

ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale___(values = ___)
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_color_manual(values = ___)
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_colour_manual(values = color_continentes)

Cambiando el "tema"

Hasta aquí hemos podido ajustar los atributos gráficos que dependen estrechamente de los datos, como las escalas, los colores y los tamaños. ¿Pero que hay de otros componentes visuales que podríamos cambiar, como la tipografía y su tamaño o el color de fondo?

Esos atributos corresponden al "tema" (theme en inglés) que apliquemos. Los temas son como "packs" de parámetros que definen el aspecto de todos los componentes no relacionados con los datos, y pueden cambiar drásticamente el look de nuetros gráficos. El paquete ggplot2 incluye varias funciones que aplican temas predefinidos, como "theme_dark()" (la opción por defecto), "theme_dark", "theme_void", "theme_minimal", etc. Por eso para usar los temas alcanza con sumar una línea que llame a la función correspondiente. Por ejemplo, para usar "theme_dark":

ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_colour_manual(values = color_continentes) + 
  theme_dark()

Incluso podemos definir a mano cada componente visual de los gráficos, creando así nuestro propio tema. No vamos a entrar en detalles ahora, pero para quien tenga interés el camino comienza por el uso de la función theme. También existen paquetes de R que agregan a nuestro arsenal nuevos temas listos para usar, como ggthemes y hrbrthemes.

Para cerrar el tema de los temas (¡ja!) va una recomendación. El tema minimalista "theme_minimal" viene con ggplot2 por lo que ya lo tenemos disponible, y con sólo agregar la línea que lo aplica obtenemos un gráfico de aspecto más "limpio" que el del tema por default:

ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_colour_manual(values = color_continentes) + 
  theme_minimal()

Ya estamos listos para pasar a los toques finales.

Los toques finales

En el contexto de la exploración de los datos, lo importante es trabajar en forma rápida. Probamos una u otra técnica de visualización y refinamos nuestros resultados hasta hallar patrones interesantes, o sacarnos dudas acerca del contenido. No necesitamos ponerle título a las visualizaciones, porque ya sabemos de que tratan (¡acabamos de escribirlas!). No nos preocupa que los nombres de los ejes indiquen en forma clara la variable representan, porque ya lo sabemos de antemano.

Pero cuando queremos guardar un gráfico para compartir con otras personas, sea publicándolo en un paper o enviándolo por email a colegas, necesitamos tener más cuidado. Hemos pasado del ámbito de la exploración al de la comunicación. Ahora si nos importa la claridad, porque no sabemos de antemano cuánta familiaridad tiene con los datos la eventual audiencia.

Si bien la comunicación clara es un arte cuyas reglas dependen del contexto, y además cada quien tiene su estilo, podemos mencionar al menos tres elementos que no deberían faltar en un gráfico pensado para compartir:

y ya que estamos, dos opcionales:

Todo ello puede resolverse con la misma función: labs(). Esta se encarga de definir título y subtítulo, nombres de ejes y de leyendas, y nota al pie si hiciera falta.

Empecemos por título y subtítulo, que se asignan con los parámetros title y subtitle. Pidamos que el título sea "Riqueza vs. salud en los países del mundo", y el subtítulo, "según datos 2007":

ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_colour_manual(values = color_continentes) + 
  theme_minimal() +
  ___(___)
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_colour_manual(values = color_continentes) + 
  theme_minimal() +
  labs(title = ___, ___ = ___)
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_colour_manual(values = color_continentes) + 
  theme_minimal() +
  labs(title = "Riqueza vs. salud en los países del mundo", subtitle = "según datos 2007")

Ahora mejoremos las etiquetas de los ejes, y de la leyenda de tamaño. La leyenda dec color está bien así, la vamos a dejar en paz. Como podríamos habernos imaginado, el parámetro de labs() para definir el nombre que llevaran las $x$ es x (como labs(x = "nombre para el eje de las x")). El parámetro para las $y$ es y. El que fija el título de la leyenda de tamaño es size, y así con el resto de las leyendas: color, shape, o el atributo que hayamos definido dentro de aes() al hacer el gráfico.

Pongamos "población (millones)" como título de la leyenda de tamaño, "PBI per capita (USD)" en las $x$, y "expectativa de vida en años" en las $y$:

ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_colour_manual(values = color_continentes) + 
  theme_minimal() +
  labs(title = "Riqueza vs. salud en los países del mundo", subtitle = "según datos 2007",
       ___)
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_colour_manual(values = color_continentes) + 
  theme_minimal() +
  labs(title = "Riqueza vs. salud en los países del mundo", subtitle = "según datos 2007",
       size = ___, x = ___, ___ = ___)
ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_colour_manual(values = color_continentes) + 
  theme_minimal() +
  labs(title = "Riqueza vs. salud en los países del mundo", subtitle = "según datos 2007",
       size = "población (millones)", x = "PBI per capita (USD)", y = "expectativa de vida en años")

Y por último, una nota al pie con la fuente de los datos, vía caption :

ggplot(filter(gapminder, año == 2007), 
       aes(x = PBIpc, y = expVida, size = pobl/1000000, color = continente)) +
  geom_point() +
  scale_x_log10() +
  scale_colour_manual(values = color_continentes) + 
  theme_minimal() +
  labs(title = "Riqueza vs. salud en los países del mundo", subtitle = "según datos 2007",
       size = "población (millones)", 
       x = "PBI per capita (USD)", y = "expectativa de vida en años",
       caption = "fuente: Gapminder, www.gapminder.com")

Y con eso, terminamos por ahora. Para seguir practicando, podemos obtener los datos que hemos usado aquí con:

gapminder <- dataviz::gapminder

Y en Data Visualization - A practical introduction de Kieran Healy tenemos el capítulo: "Refine your plots":



bitsandbricks/dataviz documentation built on May 3, 2022, 5:28 p.m.