Visualización

Una de las herramientas más potentes que tenemos para mostrar las conclusiones a las que podemos llegar tras realizar un análisis de datos o tras aplicar ciertas transformaciones a nuestros datos brutos es su visualización mediante gráficos. En esta lección exploraremos las posibilidades que nos ofrece la base de R mediante la función plot y en segundo lugar seguiremos descubriendo las posibilidades que nos ofrece el entorno tidyverse mediante la librería ggplot2. Esta librería implementa una gramática de gráficos, es decir, un sistema coherente para describir y construir gráficos.

R base

El núcleo de R provee tres funciones básicas para dibujar gráficos que nos pueden servir para dibujar nuestros primeros gráficos. Los ejemplos usados en esta sección están extraídos del siguiente enlace

Plot

Es la función de dibujo más versátil de todas. En el libro AprendeR hay una descripción muy extensa, en concreto el capítulo 7 se dedica solamente a esta función. Es altamente recomendable leer este capítulo si se necesita/quiere realizar gráficos complejos con esta función. Nosotros simplemente describiremos su sintaxis más básica y veremos algunos ejemplos.

Con la función plot, podemos crear una amplia gama de gráficos, según los datos que esta reciba como parámetros. En la siguiente tabla resumimos todas las posibilidades disponibles para la función de trazado base R.

Esta función tiene un comportamiento especial, pues dependiendo del tipo de dato que le demos como argumento, generará diferentes tipos de gráfica. Además, para cada tipo de gráfico, podremos ajustar diferentes parámetros que controlan su aspecto, dentro de esta misma función.

Esta función tiene un argumento x, que corresponde al eje X de una gráfica. Este parámetro requiere un vector y si no especificamos este argumento, obtendremos un error. El resto de los argumentos de plot son opcionales puedes consultarlo con la expresión ?plot, pero el más importante es y que corresponde al eje Y de nuestra gráfica.

Dependiendo del tipo de dato que pasemos como parámetros obtendremos diferentes tipos de gráficos de acuerdo a las siguientes reglas:

Entrada

Salida

plot(x)

Diagrama de dispersión de vectores numéricos x

plot(x,y)

Diagrama de dispersión de vectores numéricos x e y

plot(factor)

Diagrama de barras del factor

plot(factor, y)

Diagrama de caja del vector numérico y los niveles del factor

plot(time_series)

Gráfico de la serie temporal

plot(data_frame)

Gráfica de correlación de todos columnas del dataframe (más de dos columnas)

plot(function, lower, upper)

Gráfico de la función entre el valor lower y el valor upper especificado

A continuación veremos diferentes ejecuciones de esta función usando el ya conocido conjunto de datos de Iris.

[93]:
data(iris)

Si solamente recibe un único vector, vemos que muestra cada valor respecto a su índice:

[94]:
plot(iris$Sepal.Length)
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_3_0.png

Por otra parte, si recibe 2 vectores vemos el diagrama de dispersión. Por ejemplo podemos observar la relación entre las variables Petal.Length y Petal.Width. Esta función puede recibir multitud de parámetros diferentes, por ejemplo aquí vemos como poner un título:

[95]:
plot(iris$Petal.Length, iris$Petal.Width, main="Relación medidas pétalo")
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_5_0.png

Esta función también es útil con factores. A continuación veremos como nos muestra el número de ocurrencias de cada categoría:

[96]:
plot(iris$Species)
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_7_0.png

Por otra parte, la combinación de un factor y un vector nos proporciona un diagrama de cajas de cada uno de los valores de los factores. Este diagrama muestra el resumen de cinco números de un conjunto de datos. El resumen de cinco números es el valor mínimo, el primer cuartil (Q1), la mediana (Q2), el tercer cuartil (Q3) y el valor máximo.

En el ejemplo vemos el diagrama de cajas de la característica Sepal.Lenght según la variable Species como cambiamos las leyendas de los ejes con los parámetros: xlab y ylab:

[97]:
plot(iris$Species, iris$Sepal.Length, xlab="Especies", ylab="Longitud Sépalo")
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_9_0.png

Veamos que sucede cuando esta función recibe un dataframe:

[98]:
plot(iris)
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_11_0.png

Hist

Con la función hist podemos dibujar un histograma. Un histograma es un gráfico que permite descubrir y mostrar la distribución de frecuencia de un conjunto de datos, además permite la inspección de valores atípicos, asimetría…

Veamos el histograma de la longitud del sépalo, en este caso añadimos un nuevo parámetro: col.

[99]:
hist(iris$Sepal.Length, main="Histograma de longitud del sépalo",
     xlab="cm", ylab="Frecuencia", col="green")
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_13_0.png

Podemos personalizar los intervalos que queremos mostrar indicando el parámetro breaks:

[100]:
hist(iris$Sepal.Length, breaks=seq(0,10, 0.5),
main="Histograma de longitud del sépalo",
     xlab="cm", ylab="Frecuencia", col="green")
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_15_0.png

Boxplot

La función boxplot realiza el diagrama de cajas, tanto de un vector como de un dataframe, los valores que muestra son los 5 mismos que con la función plot cuando recibía un factor y un vector. Veamos un sencillo ejemplo en el que aprovechamos para ilustrar como asignar un color a cada sub diagrama:

[101]:
boxplot(iris[1:4], col=c("green", "blue", "red", "yellow"))
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_17_0.png

ggplot2

Para la realización de los ejemplos de esta sección usaremos el conjunto de datos Palmer Penguins con el que ya hemos trabajado anteriormente. Recordemos como podemos instalar y obtener los datos de este conjunto:

> install.packages("palmerpenguins")
> library(palmerpenguins)

> str(penguins)
> View(penguins)

La instalación de la libreria se puede realizar con la siguiente instrucción. También podríamos instalar todo el paquete de tidyverse que ya incluye ggplot2:

[102]:
install.packages("ggplot2")
library(ggplot2)
library(dplyr)
install.packages("palmerpenguins")
library(palmerpenguins)

penguins = penguins %>% filter(!is.na(sex))  # eliminamos los valores NA
Installing package into ‘/home/uib/R/x86_64-pc-linux-gnu-library/4.2’
(as ‘lib’ is unspecified)

Installing package into ‘/home/uib/R/x86_64-pc-linux-gnu-library/4.2’
(as ‘lib’ is unspecified)

ggplot

El funcionamiento de esta libreria se basa en el uso de la función ggplot. Esta crea un sistema de coordenadas al que podemos agregar las capas gráficas que consideremos necesarias. El primer argumento de ggplot es el conjunto de datos que se usará en el gráfico. La instrucción ggplot(data = penguins) crea un gráfico vacío, a continuación aprenderemos como podemos añadir información.

La función geom_point agrega una capa de puntos a nuestro diagrama, lo que crea un gráfico de dispersión. ggplot2 nos permite usar muchas funciones del tipo geom, cada una de las cuales agrega un tipo diferente de capa a nuestro gráfico. Durante esta lección veremos algunas de ellas, podemos encontrarlas todas en la documentación oficial de la libreria.

Cada función del tipo geom en la librería ggplot2 recibe un argumento de mapeo. Este define cómo las variables en su conjunto de datos se asignan a las diferentes propiedades visuales. El argumento de mapeo siempre se combina con la función aes, y los argumentos x e y de aes especifican qué variables asignar a los ejes x e y. ggplot2 se encarga de buscar las variables asignadas en el argumento de datos.

Veamos un primer ejemplo de como dibujar la relación entre su peso y la amplitud del pico de los pingüinos. Debemos fijarnos en el mensaje de alerta que recibimos tras llamar a la función de dibujado:

[103]:
ggplot(data = penguins) +
  geom_point(mapping = aes(x = body_mass_g, y = bill_depth_mm))
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_22_0.png

Ahora que hemos realizado nuestro primer gráfico, veamos como podemos generalizar este conocimiento para poder construir una plantilla que nos sirva en múltiples ocasiones:

ggplot(data = <DATA>) +
  <GEOM_FUNCTION>(mapping = aes(<MAPPINGS>))

Para hacer un gráfico, debemos remplazar las secciones entre llaves en el código anterior con un conjunto de datos (DATA), una función geom en el segundo caso (GEOM_FUNCTION) y realizar las asignaciones de variables en el caso de mapping (MAPPINGS).

Además, podemos agregar nuevas variables a la función aes de un diagrama de dispersión bidimensional para añadir/asignar estética. Una estética es una propiedad visual de los objetos en el gráfico. La estética incluye cosas como el tamaño, la forma o el color de los puntos que representan la información. Podemos realizar estos cambios, modificando los valores de sus propiedades estéticas.

Veamos como modificar el color de los puntos en el gráfico anterior según la variable species:

[104]:
ggplot(data = penguins) +
    geom_point(mapping = aes(x = body_mass_g, y = bill_depth_mm, color=species))
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_24_0.png

En general, para asignar una estética a un nivel del gráfico, debemos pasar como parámetro el nombre de la estética al nombre de la variable dentro de la función aes. ggplot2 asignará automáticamente un nivel único de la estética (en este caso, un único color) a cada valor único de la variable, este es un proceso conocido como escalado. Podemos observar como la propia librería se encarga de añadir la leyenda según la estética que hemos elegido.

En el ejemplo anterior, hemos mapeado la clase a la estética del color, pero podríamos haber mapeado la clase a la estética del tamaño. En este caso, el tamaño exacto de cada punto revelaría su afiliación de clase.

[105]:
ggplot(data = penguins) +
    geom_point(mapping = aes(x = body_mass_g, y = bill_depth_mm, size=species))
Warning message:
“Using size for a discrete variable is not advised.”
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_26_1.png

Otras estéticas muy usadas son alpha o shapeque controlan la transparencia y forma del punto respectivamente. Podemos combinar diferentes en el mismo gráfico, aunque hay que tener en cuenta la claridad del gráfico antes de añadir más información:

[106]:
ggplot(data = penguins) +
    geom_point(mapping = aes(x = body_mass_g, y = bill_depth_mm, color=species, size=body_mass_g))
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_28_0.png

Una vez que hemos mapeado una estética, ggplot2 se encarga del resto. Selecciona una escala razonable para usar con la estética y construye una leyenda que explica el mapeo entre niveles y valores. Para la estética de los parámetros x e y, ggplot2 no crea una leyenda, pero crea líneas tanto en el eje horizontal como en el vertical con marcas y una etiqueta. La línea del eje actúa como una leyenda; explica el mapeo entre ubicaciones y valores.

Por otra parte, también es posible establecer las propiedades estéticas de la variable geom de forma manual. Por ejemplo vamos a hacer que en lugar de puntos, el gráfico se muestre con cruces rojas:

[107]:
ggplot(data = penguins) +
    geom_point(mapping = aes(x = body_mass_g, y = bill_depth_mm),  color="red", shape=3)
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_30_0.png

La diferencia radica en que en este último ejemplo los parámetros estéticos pertenecen a la función geom, en concreto a geom_point.

Objetos geométricos

Como ya hemos comentado anteriormente, geom es el objeto geométrico que utiliza un gráfico para representar datos. Por ejemplo, los gráficos de barras utilizan la función geom_bar, los gráficos de líneas utilizan geom_line, los diagramas de caja utilizan geom_boxplot, etc. Ya sabemos que los diagramas de dispersión utilizan geom_point.

La idea general es que podemos usar diferentes geoms para mostrar los mismos datos. Para cambiar la geom de un gráfico, es suficiente con modificar la función geom que añadimos a la función ggplot. Por ejemplo, para hacer los diagramas de arriba, puedes usar este código:

La primera gráfica usa la función geom_point y la segunda gráfica usa la función geom_smooth que básicamente consiste en una línea ajustada a los datos. En este caso cambiamos las variables que dibujamos para hacer el ejemplo más útil:

[108]:
ggplot(data = penguins) +
    geom_point(mapping = aes(x = body_mass_g, y = flipper_length_mm))
ggplot(data = penguins) +
    geom_smooth(mapping = aes(x = body_mass_g, y = flipper_length_mm))
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_32_1.png
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_32_2.png

Cada función geom debe tomar una argumento de mapeo. Sin embargo, no todas las estéticas funcionan con todas las geom que tenemos disponibles. Por ejemplo, podemos establecer que forma tomará un punto, pero no es posible decidir cuál es la forma de una línea pero si su estilo (linetype).

geom_smooth, usa un solo objeto geométrico para mostrar varias filas de datos, pero podemos usar el parámetro group de la estética para poder dibujar diversos objetos. En concreto ggplot2 dibujará un objeto separado para cada valor único de la variable que elijamos para la agrupación. En la práctica, ggplot2 agrupará automáticamente los datos de estos geoms siempre que asigne una estética a una variable discreta (como en el ejemplo del tipo de línea).

Veamos un ejemplo:

[109]:
ggplot(data = penguins) +
    geom_smooth(mapping = aes(x = body_mass_g, y = flipper_length_mm, group=species))
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_34_1.png

Finalmente, veamos como añadir diferentes capas a un mismo gráfico. De una forma general podemos añadir capas a nuestros gráficos de manera sencilla, básicamente agregamos una capa tras otra:

[110]:
ggplot(data = penguins) +
    geom_smooth(mapping = aes(x = body_mass_g, y = flipper_length_mm)) +
    geom_point(mapping = aes(x = body_mass_g, y = flipper_length_mm))
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_36_1.png

Si nos fijamos bien, el código anterior introduce cierta duplicación. Si quisiéramos cambiar la variable que ahora tenemos en el eje y para mostrar una diferente, tendríamos que cambiarla en dos lugares y existe la posibilidad que nos podamos olvidar de actualizar uno de ellos. Podemos evitar este tipo de repetición pasando un conjunto de asignaciones a la función ggplot. Esta tratará estas asignaciones como asignaciones globales que se aplican a cada geom del gráfico. En otras palabras, este código producirá la misma trama que el código anterior:

[111]:
ggplot(data = penguins, mapping = aes(x = body_mass_g, y = flipper_length_mm)) +
    geom_smooth() +
    geom_point()
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_39_1.png

Si elegimos realizar la estrategia anterior, ggplot2 tratará el mapping de la función ggplot como asignaciones locales para cada capa. Además, utilizará estas asignaciones locales para extender o sobrescribir las asignaciones globales, esto nos posibilita mostrar diferentes estéticas en diferentes capas. Veamos un ejemplo:

[112]:
ggplot(data = penguins, mapping = aes(x = body_mass_g, y = flipper_length_mm)) +
    geom_smooth(color = "yellow") +
    geom_point(mapping = aes(color=species))
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_42_1.png

Diagramas de barras y histogramas

Una de las geom que quizás pueda ser más útil son los diagramas de barra: geom_bar. V eremos un ejemplo que nos servirá para comprobar que todo el conocimiento que hemos adquirido en las secciones anteriores es aplicable a un nuevo tipo de gráfico.

Esta geom nos puede servir para dibujar el número de elementos agrupados según un factor o una variable categórica. Veamos el diagrama de barras de los pingüinos según su isla de procedencia, es importante fijarse que la lógica de dibujo es idéntica a la que hemos seguido hasta el momento. Solamente cambiamos la geom y nos adaptamos a sus particularidades. En este caso solamente necesitamos especificar que queremos en el eje x, la información del eje y será calculada por el programa.

[113]:
ggplot(penguins) + geom_bar(mapping = aes(x=island, colour=island))
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_44_0.png

Si nos fijamos bien usando geom_bar el parámetro estético color no sirve para rellenar las barras, en este caso podemos usar el parámetro fill:

[114]:
ggplot(penguins) + geom_bar(mapping = aes(x=island, fill=island))
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_46_0.png

Finalmente, veamos como generar un histograma. En este caso observaremos la distribución que sigue la propiedad bill_length_mm:

[115]:
ggplot(data = penguins) +
    geom_histogram(mapping = aes(x = bill_length_mm))
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_48_1.png

La ejecución anterior nos recomienda usar un parámetro diferente al que ha calculado, bins = 30 para obtener una mejor visualización. Seguiremos su recomendación y aprovecharemos para cambiar el color del gráfico. Para saber los colores que tenemos a nuestra disposición podemos emplear la función color() y obtendremos un listado con sus nombres.

[117]:
 ggplot(data = penguins) +
    geom_histogram(mapping = aes(x = bill_length_mm), bins=15, fill="skyblue")
../../../_images/notebooks_Part1_03_R_Descripcion_04_Visualizacion_50_0.png

Ejercicios

  1. Carga el conjunto de datos mtcars y transforma a factores aquellas columnas que representen categorías. Recuerda a cargar las librerías ggplot2 y dplyr.

  2. Muestra en un gráfico la relación entre el peso del vehículo (wt) en el eje y y la cantidad de millas por combustible (mpg) en el eje x. Debes usar la geometría geom_point.

  3. Vamos a añadir color al gráfico anterior, veamos si el hecho que el coche sea automático puede ayudar a tener más información.

  4. Vamos a cambiar la geometría, usaremos geom_smooth. Queremos poner la línea de color verde. En el mismo gráfico añadiremos los puntos que teníamos antes.

  5. Dibuja un diagrama de barras en el que veamos el número de coches de cada cilindrada. Cada barra debe ser de un color. Para asignar manualmente el color de cada barra se puede usar la función scale_manual.

  6. Busca en la documentación oficial de ggplot2 como dibujar un boxplot. Haz un diagrama de este tipo con según el número de carburadores. Rellena el boxplot con el color skyblue.

  7. Para repasar: Cuantas ocurrencias hay de cada tipo de carb diferente. Usa las funciones group_by y summarise para mostrarlas. Ya que estamos en faena, añadiremos el peso medio de cada categoría.

Recursos

En esta lección hemos aprendido los elementos básicos para el dibujo en R, mi recomendación es intentar usar la librería ggplot2 siempre que sea posible, ya que nos permite tener una estructura estándar y reusable. Existen diversos materiales que nos pueden ayudar a profundizar en este tema:

Tutoriales:

  • R for data science enlace.

  • Cookbook for R enlace.

  • R Graph Gallery enlace: Contiene multitud de ejemplos con el código que los genera.

Enlaces

  • Estilos de geometrías disponibles: enlace

Lecturas:

  • A Layered Grammar of Graphics enlace : Una gramática de gráficos es una herramienta que nos permite describir de manera concisa los componentes de un gráfico. Tal gramática nos permite ir más allá de los gráficos con nombre y obtenga información sobre la estructura profunda que subyace a los gráficos estadísticos. Este artículo se basa en Wilkinson, Anand y Grossman (2005), que describe extensiones y refinamientos desarrollados mientras se construía una implementación de código abierto de la gramática de gráficos para R, ggplot2.