Chapter 12 Visualización de datos con ggplot2

Data Visualization with ggplot2 (Part 1)

12.1 Introducción

Vamos a aprender a realizar visualizaciones con sentido en R. Vamos a comprender esta visualización como una herramienta de comunicación, lo que requiere que pensemos en la audiencia a la que va dirigida. Vamos a hacer una introducción de lo básico de ggplot2, incluyendo los 7 elementos gramaticales y los aestethic mappings.

La visualización de datos es una combinación de Estadística y Diseño, ofrenciendo modos apropiados y con significado. Hay que dotar a la figura de significado, incluyendo una buena comprensión de los datos, pero también que sean fácilmente comunicativos.

Es importante distinguir entre figuras exploratorias y explicativas (Exploratory vs Explanatory). Las visualizaciones exploratorias deben ser fáciles de generar, con gran cantidad de datos y con pretensión de llegar a una audiencia especializada y normalmente pequeña. En este punto no nos interesa especialmente la belleza y personalización de la figura. Las visualizaciones explicativas son normalmente labor-intensive, data-specific, y con pretensión de llegar a una audiencia mayor, generalmente en publicaciones o presentaciones. Este último forma parte de los procesos de comunicación. Como analista de datos, nuestro trabajo requiere explorar los datos, pero también explicárselos a una audiencia específica.

ggplot2 se basa en la gramática de la filosofía de la representación de gráficos, haciéndola más flexible e intuitiva para entender la relación entre sus visuales y sus datos.

Este tutorial de ggplot2 se basa en su conocimiento desde el primer curso para producir gráficos explicativos significativos. Exploraremos las últimas cuatro capas opcionales. Las estadísticas se calcularán sobre la marcha y veremos cómo las Coordenadas y Facetas ayudan en la comunicación. Los gráficos de calidad de publicación se producirán directamente en R utilizando la capa Temas. También discutiremos los detalles sobre las mejores prácticas de visualización de datos con ggplot2 para ayudar a asegurarnos de que usted tenga una comprensión sólida de lo que funciona y por qué. Al final del curso, tendrás todas las herramientas necesarias para realizar una función de trazado personalizada para explorar un gran conjunto de datos, combinando estadísticas y excelentes imágenes.

12.1.1 Explorando ggplot2

Para explicar el paquete ggplot2, vamos a emplear los datos mtcars, que agrupa la información sobre 32 coches de una revista de 1973.

data("mtcars")   # cargamos los datos
library(ggplot2) # cargamos el paquete
str(mtcars)      # vemos la estructura de los datos
## 'data.frame':    32 obs. of  11 variables:
##  $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
##  $ cyl : num  6 6 4 6 8 6 8 4 4 6 ...
##  $ disp: num  160 160 108 258 360 ...
##  $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
##  $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
##  $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
##  $ qsec: num  16.5 17 18.6 19.4 17 ...
##  $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
##  $ am  : num  1 1 1 0 0 0 0 0 0 0 ...
##  $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
##  $ carb: num  4 4 1 1 2 1 4 2 2 4 ...
# hacemos una figura rápida
ggplot(mtcars, aes(x = cyl, y = mpg)) +
  geom_point()

En la figura anterior observamos que el número de cilindros (cyl) se ha tratado como una variable continua, pudiendo indicar que existen coches con 5 y 7 cilindros. Este no es el caso. Tenemos que convertir la variable cyl en factorial mediante el uso de factor().

ggplot(mtcars, aes(x = factor(cyl), y = mpg)) +
  geom_point()

12.1.2 Gramática de los gráficos

En el libro de Leland Wilkinson de 1999 habla sobre la gramática de los gráficos, donde define que hay 2 principios:

  • Gráficos: diferentes capas de elementos gramaticales. Actuarían como adjetivos y nombres en una frase. Existen 7 elementos gramaticales en total y tres de ellos son esenciales:
    • Data (esencial): el conjunto de datos a representar gráficamente.
    • Aesthetics (esencial): las esclas con la que vamos a mapear nuestros datos.
    • Geometrías (esencial): los elementos visuales usados en nuestros datos.
    • Facets: representar varias figuras
    • Statistics: datos relacionados con la estadística
    • Coordinates: el espacio de visualización
    • Themes: los temas
  • Figuras con significado a través de aesthetic mapping. Actuarían como las reglas gramaticales para poder unir ese vocabulario.
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point()

# Introducimos color = disp
ggplot(mtcars, aes(x = wt, y = mpg, color = disp)) +
  geom_point()

# Introducimos size = disp
ggplot(mtcars, aes(x = wt, y = mpg, size = disp)) +
  geom_point()

Vemos que el segundo y tercer código han generado automáticamente una leyenda a la derecha. De hecho disp se ha representado como un gradiente de color o una escala continua de tamaño.

library(ggplot2)
data(diamonds)
# Explore the diamonds data frame with str()
str(diamonds)
## 'data.frame':    53940 obs. of  10 variables:
##  $ carat  : num  0.23 0.21 0.23 0.29 0.31 0.24 0.24 0.26 0.22 0.23 ...
##  $ cut    : Factor w/ 5 levels "Fair","Good",..: 3 4 2 4 2 5 5 5 1 5 ...
##  $ color  : Factor w/ 7 levels "D","E","F","G",..: 2 2 2 6 7 7 6 5 2 5 ...
##  $ clarity: Factor w/ 8 levels "I1","IF","SI1",..: 4 3 5 6 4 8 7 3 6 5 ...
##  $ depth  : num  61.5 59.8 56.9 62.4 63.3 62.8 62.3 61.9 65.1 59.4 ...
##  $ table  : num  55 61 65 58 58 57 57 55 61 61 ...
##  $ price  : int  326 326 327 334 335 336 336 337 337 338 ...
##  $ x      : num  3.95 3.89 4.05 4.2 4.34 3.94 3.95 4.07 3.87 4 ...
##  $ y      : num  3.98 3.84 4.07 4.23 4.35 3.96 3.98 4.11 3.78 4.05 ...
##  $ z      : num  2.43 2.31 2.31 2.63 2.75 2.48 2.47 2.53 2.49 2.39 ...
# Add geom_point() and geom_smooth() with +
ggplot(diamonds, aes(x = carat, y = price)) +
  geom_point() +
  geom_smooth()
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

# 2 - Copy the above command but show only the smooth line
ggplot(diamonds, aes(x = carat, y = price)) +
  geom_smooth()
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

# 3 - Copy the above command and assign the correct value to col in aes()
ggplot(diamonds, aes(x = carat, y = price, color = clarity)) +
  geom_smooth()
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

# 4 - Keep the color settings from previous command. Plot only the points with argument alpha.
ggplot(diamonds, aes(x = carat, y = price, color = clarity)) +
  geom_point(alpha = 0.4)

12.1.2.1 Comprendiendo la gramática

# Create the object containing the data and aes layers: dia_plot
dia_plot <- ggplot(diamonds, aes(x = carat, y = price))

# Add a geom layer with + and geom_point()
dia_plot + geom_point()

# Add the same geom layer, but with aes() inside
dia_plot + geom_point(aes(color = clarity))

# 1 - The dia_plot object has been created for you
dia_plot <- ggplot(diamonds, aes(x = carat, y = price))

# 2 - Expand dia_plot by adding geom_point() with alpha set to 0.2
dia_plot <- dia_plot + geom_point(alpha = 0.2)

# 3 - Plot dia_plot with additional geom_smooth() with se set to FALSE
dia_plot + geom_smooth(se = FALSE)
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

# 4 - Copy the command from above and add aes() with the correct mapping to geom_smooth()
dia_plot + geom_smooth(aes(color = clarity), se = FALSE)
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

12.2 Datos

The structure of your data will dictate how you construct plots in ggplot2. In this chapter, you’ll explore the iris dataset from several different perspectives to showcase this concept. You’ll see that making your data conform to a structure that matches the plot in mind will make the task of visualization much easier through several R data visualization examples.

12.2.1 Objetos y capas

Los objetos se refieren a los elementos que queremos representar, mientras que las capas se refieren directamente a los diferentes tipos de representación que queremos hacer sobre los objetos. En ggplot2 son figuras aditivas donde vamos sumando capas, cada una con su función correspondiente.

Primero representamos al estilo común con plot().

# Use lm() to calculate a linear model and save it as carModel
carModel <- lm(mpg ~ wt, data = mtcars)

# Basic plot
mtcars$cyl <- as.factor(mtcars$cyl)
plot(mtcars$wt, mtcars$mpg, col = mtcars$cyl)

# Call abline() with carModel as first argument and set lty to 2
abline(carModel, lty = 2)

# Plot each subset efficiently with lapply
# You don't have to edit this code
plot(mtcars$wt, mtcars$mpg, col = mtcars$cyl)
lapply(mtcars$cyl, function(x) {
  abline(lm(mpg ~ wt, mtcars, subset = (cyl == x)), col = x)
  })
## [[1]]
## NULL
## 
## [[2]]
## NULL
## 
## [[3]]
## NULL
## 
## [[4]]
## NULL
## 
## [[5]]
## NULL
## 
## [[6]]
## NULL
## 
## [[7]]
## NULL
## 
## [[8]]
## NULL
## 
## [[9]]
## NULL
## 
## [[10]]
## NULL
## 
## [[11]]
## NULL
## 
## [[12]]
## NULL
## 
## [[13]]
## NULL
## 
## [[14]]
## NULL
## 
## [[15]]
## NULL
## 
## [[16]]
## NULL
## 
## [[17]]
## NULL
## 
## [[18]]
## NULL
## 
## [[19]]
## NULL
## 
## [[20]]
## NULL
## 
## [[21]]
## NULL
## 
## [[22]]
## NULL
## 
## [[23]]
## NULL
## 
## [[24]]
## NULL
## 
## [[25]]
## NULL
## 
## [[26]]
## NULL
## 
## [[27]]
## NULL
## 
## [[28]]
## NULL
## 
## [[29]]
## NULL
## 
## [[30]]
## NULL
## 
## [[31]]
## NULL
## 
## [[32]]
## NULL
# This code will draw the legend of the plot
# You don't have to edit this code
legend(x = 5, y = 33, legend = levels(mtcars$cyl),
       col = 1:3, pch = 1, bty = "n")

Ahora lo hacemos con ggplot2

# Convert cyl to factor (don't need to change)
mtcars$cyl <- as.factor(mtcars$cyl)

# Example from base R (don't need to change)
plot(mtcars$wt, mtcars$mpg, col = mtcars$cyl)
abline(lm(mpg ~ wt, data = mtcars), lty = 2)
lapply(mtcars$cyl, function(x) {
  abline(lm(mpg ~ wt, mtcars, subset = (cyl == x)), col = x)
  })
## [[1]]
## NULL
## 
## [[2]]
## NULL
## 
## [[3]]
## NULL
## 
## [[4]]
## NULL
## 
## [[5]]
## NULL
## 
## [[6]]
## NULL
## 
## [[7]]
## NULL
## 
## [[8]]
## NULL
## 
## [[9]]
## NULL
## 
## [[10]]
## NULL
## 
## [[11]]
## NULL
## 
## [[12]]
## NULL
## 
## [[13]]
## NULL
## 
## [[14]]
## NULL
## 
## [[15]]
## NULL
## 
## [[16]]
## NULL
## 
## [[17]]
## NULL
## 
## [[18]]
## NULL
## 
## [[19]]
## NULL
## 
## [[20]]
## NULL
## 
## [[21]]
## NULL
## 
## [[22]]
## NULL
## 
## [[23]]
## NULL
## 
## [[24]]
## NULL
## 
## [[25]]
## NULL
## 
## [[26]]
## NULL
## 
## [[27]]
## NULL
## 
## [[28]]
## NULL
## 
## [[29]]
## NULL
## 
## [[30]]
## NULL
## 
## [[31]]
## NULL
## 
## [[32]]
## NULL
legend(x = 5, y = 33, legend = levels(mtcars$cyl),
       col = 1:3, pch = 1, bty = "n")

# Plot 1: add geom_point() to this command to create a scatter plot
ggplot(mtcars, aes(x = wt, y = mpg, col = cyl)) +
  geom_point()  # Fill in using instructions Plot 1

# Plot 2: include the lines of the linear models, per cyl
ggplot(mtcars, aes(x = wt, y = mpg, col = cyl)) +
  geom_point() + # Copy from Plot 1
  geom_smooth(method = "lm", se = FALSE)   # Fill in using instructions Plot 2

# Plot 3: include a lm for the entire dataset in its whole
ggplot(mtcars, aes(x = wt, y = mpg, col = cyl)) +
  geom_point() + # Copy from Plot 2
  geom_smooth(method = "lm", se = FALSE) + # Copy from Plot 2
  geom_smooth(aes(group = 1), method = "lm", se = FALSE, lty = 2)   # Fill in using instructions Plot 3

12.2.2 Formato correcto de los datos

Dos formas de representar gráficamente el grosor y longitud de sépalos y pétalos. Para ello tenemos iris y iris.wide.

str(iris)
## 'data.frame':    150 obs. of  6 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ Flower      : int  1 2 3 4 5 6 7 8 9 10 ...

En la primera forma de representarlo, lanzamos dos capas con dos funciones geom_point(), una para sépalos y otra para pétalos.

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  geom_point(aes(x = Petal.Length, y = Petal.Width), col = "red")

Sin embargo, esta no es la forma correcta de hacerlo, ya que no aparece la leyenda, no se interpreta bien y hay excesivo código por no comprender completamente la gramática de los gráficos. Lo mejor es generar un nuevo conjunto de datos modificando el formato de los mismos. Este conjunto de datos se llama iris.wide. Ahora pétalos y sépalos están formando parte de una columna categórica (Part). Lo podemos representar correctamente del siguiente modo.

str(iris.wide)
## 'data.frame':    300 obs. of  4 variables:
##  $ Species: Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ Part   : chr  "Petal" "Petal" "Petal" "Petal" ...
##  $ Length : num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Width  : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
ggplot(iris.wide, aes(x = Length, y = Width, col = Part)) +
  geom_point()

Ahora, si queremos representar cada una de las figuras anteriores separando por medida (Length y Width), podríamos ejecutarlas dos veces con cada una de las dos medidas. Sin embargo, si una de las variables es la variable categórica Measure, en vez de ejecutarla dos veces podemos utilizar la función facet_grid() sobre el conjunto de datos llamado iris.tidy.

str(iris.tidy)
## 'data.frame':    600 obs. of  4 variables:
##  $ Species: Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ Part   : chr  "Sepal" "Sepal" "Sepal" "Sepal" ...
##  $ Measure: chr  "Length" "Length" "Length" "Length" ...
##  $ Value  : num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
ggplot(iris.tidy, aes(x = Species, y = Value, col = Part)) +
  geom_jitter() +
  facet_grid(. ~ Measure)

Este reordenamiento de datos para adaptarlos a la gramática precisa de ggplot2 requiere de la intervación, entre otros, del paquete tidyr. Por ejemplo, la formación del conjunto de datos iris.tidy se realizó empleando las funciones gather() y separate().

library(tidyr)

iris.tidy <- iris %>%
  gather(key, Value, -Species) %>%
  separate(key, c("Part", "Measure"), "\\.")
## Warning: Expected 2 pieces. Missing pieces filled with `NA` in 150 rows
## [601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615,
## 616, 617, 618, 619, 620, ...].
# Add column with unique ids (don't need to change)
iris$Flower <- 1:nrow(iris)

# Fill in the ___ to produce to the correct iris.wide dataset
iris.wide <- iris %>%
  gather(key, value, -Species, -Flower) %>%
  separate(key, c("Part", "Measure"), "\\.") %>%
  spread(Measure, value)

12.3 Aesthetics

Los mapas estéticos son la piedra angular de la gramática del concepto de trazado de gráficos. Aquí es donde ocurre la magia: convertir datos continuos y categóricos en escalas visuales que proporcionan acceso a una gran cantidad de información en muy poco tiempo. En este capítulo comprenderá cómo elegir los mejores mapeos estéticos para sus datos.

Los aesthetics se refiere a cómo se mapean, cómo se representan los datos. Tiene normalmente varios argumentos dentro de la función que lo controla aes():

  • x
  • y
  • colour
  • fill
  • size
  • alpha
  • linetype
  • labels
  • shape

En este caso, muchos de los argumentos anteriores pueden ser también atributos de cómo representar datos, además de aesthetics. Por ejemplo, si queremos hacer que los puntos sean rojos, podemos poner "red" en colour. Sin embargo, colour dentro de aes() puede incorporar una variable, haciendo que ggplot() muestre cada factor dentro de la variable categórica de un color diferente. Con esto queda claro las diferencias entre un atributo y un aesthetics / mapping.

In this chapter you saw aesthetics and attributes. Variables in a data frame are mapped to aesthetics in aes(). (e.g. aes(col = cyl)) within ggplot(). Visual elements are set by attributes in specific geom layers (geom_point(col = “red”)). Don’t confuse these two things - here you’re focusing on aesthetic mappings.

12.3.1 Mapear aesthetics

# x e y de mtcars
ggplot(mtcars, aes(x = mpg, y = cyl)) +
  geom_point()

# Cambiamos aesthetics de datos
ggplot(mtcars, aes(x = wt, y = mpg, color = cyl)) +
  geom_point()

# Cambiamos atributos de los puntos
ggplot(mtcars, aes(x = wt, y = mpg, color = cyl)) +
  geom_point(shape = 1, size = 4)

# Mapea cyl en fill
ggplot(mtcars, aes(x = wt, y = mpg, fill = cyl)) +
  geom_point(shape = 1, size = 4)

# Cambiamos forma y transparencia en los puntos
ggplot(mtcars, aes(x = wt, y = mpg, fill = cyl)) +
  geom_point(shape = 21, size = 4, alpha = 0.6)

# Mapeamos cyl y am como aesthetics
ggplot(mtcars, aes(x = wt, y = mpg, fill = cyl, col = am)) +
  geom_point(shape = 21, size = 4, alpha = 0.6)

# Mapea cyl a size
ggplot(mtcars, aes(x = wt, y = mpg, size = cyl)) + 
  geom_point()
## Warning: Using size for a discrete variable is not advised.

# Mapea cyl a alpha
ggplot(mtcars, aes(x = wt, y = mpg, alpha = cyl)) + 
  geom_point()
## Warning: Using alpha for a discrete variable is not advised.

# Mapea cyl a shape 
ggplot(mtcars, aes(x = wt, y = mpg, shape = cyl)) + 
  geom_point()

# Mapea cyl a label
ggplot(mtcars, aes(x = wt, y = mpg, label = cyl)) + 
  geom_text() +
  geom_point() 

12.3.2 Representar atributos

En la segunda figuras se ve cómo tiene más importancia el último código ejecutado en torno al color.

# Define a hexadecimal color
my_color <- "#4ABEFF"

# Draw a scatter plot with color *aesthetic*
ggplot(mtcars, aes(x = wt, y = mpg, color = cyl)) +
  geom_point()

# Same, but set color *attribute* in geom layer 
ggplot(mtcars, aes(x = wt, y = mpg, color = cyl)) +
  geom_point(color = my_color)

# Set the fill aesthetic; color, size and shape attributes
ggplot(mtcars, aes(x = wt, y = mpg, fill = cyl)) +
  geom_point(color = my_color, size = 10, shape = 23)

# Expand to draw points with alpha 0.5
ggplot(mtcars, aes(x = wt, y = mpg, fill = cyl)) +
  geom_point(alpha = 0.5)

# Expand to draw points with shape 24 and color yellow
ggplot(mtcars, aes(x = wt, y = mpg, fill = cyl)) +
  geom_point(shape = 24, color = "yellow")

# Expand to draw text with label rownames(mtcars) and color red
ggplot(mtcars, aes(x = wt, y = mpg, fill = cyl, label = rownames(mtcars))) + 
  geom_text()

# Map mpg onto x, qsec onto y and factor(cyl) onto col
ggplot(mtcars, aes(x = mpg, y = qsec, color = factor(cyl))) +
  geom_point()

# Add mapping: factor(am) onto shape
ggplot(mtcars, aes(x = mpg, y = qsec, color = factor(cyl), shape = factor(am))) +
  geom_point()

# Add mapping: (hp/wt) onto size
ggplot(mtcars, aes(x = mpg, y = qsec, color = factor(cyl), shape = factor(am), size = (hp/wt))) +
  geom_point()

12.3.3 Modificar aesthetics

Una vez vistos los aesthetics y los atributos, vamos directamente a ver cómo modificar aesthetics. Podemos modificar básicamente dos elementos: las posiciones y las escalas.

Las posiciones (position) especifican cómo ggplot() debe adjustar el solapamiento de los elementos representados en una capa. Pueden ser de varios tipos y se sitúan dentro de la capa (por ejemplo, geom_point(position = "identity")):

  • identity = el valor del dataframe está exactamente donde el valor va estar localizado en la figura. Es el valor por defecto.
  • dodge
  • stack
  • fill
  • jitter: cuando hay mucho solapamiento en los datos representados, suele ser común introducir algún pequeño ruido para poder distribuirlos en las proximidades y así poder evaluar áreas de concentración.
  • jitterdodge

Las escalas (scales) pueden modificarse para cada uno de los valores de aesthetics, que son escalas. Se escribiría utilizando tres elementos unidos por guiones bajos. El primero es scales, seguido del aesthetics en concreto que estemos buscando:

  • scale_x...
  • scale_y...
  • scale_color...
  • scale_fill...
  • scale_shape...
  • scale_linetype...

El tercer elemento después de los puntos suspensivos anteriores es el tipo de datos que se emplean, por ejemplo:

  • scale_x_continuous
  • scale_color_discrete

En las funciones scale puede haber varios argumentos, entre los más empleados se encuentran limits, expand y breaks.

Setting a dummy aesthetic

# 1 - Create jittered plot of mtcars, mpg onto x, 0 onto y
ggplot(mtcars, aes(x = mpg, y = 0)) +
  geom_jitter()

# 2 - Add function to change y axis limits
ggplot(mtcars, aes(x = mpg, y = 0)) +
  geom_jitter() +
  scale_y_continuous(limits = c(-2, 2))

En el primer ejemplo anterior pusimos y = 0 como dummy variable porque, aunque para el gráfico no es necesario especificarlo, si no ponemos y nos da error. Por eso ponemos como dummy variable es valor de 0.

12.3.4 Overplotting

Hay que intentar mantener y mostrar la información específica que sea fácil de interpretar.

In the previous section you saw that there are lots of ways to use aesthetics. Perhaps too many, because although they are possible, they are not all recommended. Let’s take a look at what works and what doesn’t.

So far you’ve focused on scatter plots since they are intuitive, easily understood and very common. A major consideration in any scatter plot is dealing with overplotting. You’ll encounter this topic again in the geometries layer, but you can already make some adjustments here.

You’ll have to deal with overplotting when you have:

Large datasets,
Imprecise data and so points are not clearly separated on your plot (you saw this in the video with the iris dataset),
Interval data (i.e. data appears at fixed values), or
Aligned data values on a single axis.

One very common technique that I’d recommend to always use when you have solid shapes it to use alpha blending (i.e. adding transparency). An alternative is to use hollow shapes. These are adjustments to make before even worrying about positioning. This addresses the first point as above, which you’ll see again in the next exercise.

12.4 Geometrías

La geometría de una gráfica dicta qué elementos visuales se utilizarán. En este capítulo, le familiarizaremos con las geometrías utilizadas en los tres tipos de gráficos más comunes que encontrará: gráficos de dispersión, gráficos de barras y gráficos de líneas. Vamos a ver una variedad de diferentes maneras de construir estas parcelas.

Las geometrías son una de las capas más importantes de ggplot2. Existen 37 geometrías disponibles, cada una de las cuales comienza con geom_. Cada una de estas geometrías tiene unos argumentos obligatorios y otros optativos, así como unas especificaciones concretas de aesthetic mappings.

Uno de los aspectos interesantes de ggplot2 es que cada capa o función geom_...() puede tener su propio aesthetics, por lo que de este modo pueden controlarse independientemente.

12.4.1 Diagramas de dispersión: geom_point()

Los argumentos obligatorios son x e y, mientras que optativos son alpha, color, fill, shape y size.

ggplot(iris, aes(x = Petal.Length, y = Petal.Width, col = Species)) +
  geom_point()

# Controlando el aesthetics de geom_point()
ggplot(iris, aes(x = Petal.Length, y = Petal.Width)) +
  geom_point(aes(col = Species))

Si queremos representar también la media de cada grupo, primero calculamos sus medias y generamos un conjunto de datos nuevo, llamado iris.medias.

iris.medias <- aggregate(iris[1:4], list(iris$Species), mean)
names(iris.medias)[1] <- "Species"

Representamos ahora todo los datos conjuntos, medidas y medias. Observamos que los aesthetics que faltan de la segunda función geom_point() se heredan de los aesthetics del principio.

ggplot(iris, aes(x = Petal.Length, y = Petal.Width, col = Species)) +
  geom_point() +
  geom_point(data = iris.medias, shape = 16, size = 5)

12.4.1.1 Líneas verticales y horizontales: geom_vline() y geom_hline()

Podemos representar líneas verticales y horizontales empleando las funciones geom_vline() y geom_hline() respectivamente.

ggplot(iris, aes(x = Petal.Length, y = Petal.Width, col = Species)) +
  geom_point() +
  geom_vline(data = iris.medias, lty = 2, aes(xintercept = Petal.Length, col = Species)) +
  geom_hline(data = iris.medias, lty = 2, aes(yintercept = Petal.Width, col = Species))

12.4.1.2 Puntos con algo de ruido en el desplazamiento: geom_jitter()

Esta función es útil para evitar el solapamiento de los valores desplazándolos ligeramente para poder observarlos. Podemos cambiar sus atributos, entre ellos, la densidad o concentración de los puntos. Y podemos hacer el jittering de dos modos, o empleando la función propia, geom_jitter() o utiizando geom_point() con el argumento position = position_jitter().

# Empleo de geom_point()
ggplot(mtcars, aes(x = cyl, y = wt)) +
  geom_point()

# Empleo de geom_jitter()
ggplot(mtcars, aes(x = cyl, y = wt)) +
  geom_jitter()

# 2 - Set width in geom_jitter()
ggplot(mtcars, aes(x = cyl, y = wt)) +
  geom_jitter(width = 0.1)

# 3 - Set position = position_jitter() in geom_point() ()
ggplot(mtcars, aes(x = cyl, y = wt)) +
  geom_point(position = position_jitter(0.1))

El overplotting también lo podemos solucionar mediante el control del tamaño de los objetos y la transparencia. Como el objeto de la visualización de datos es mostrar la información de un modo preciso y rápido, hay que controlar estos aspectos para facilitar su comprensión.

# Con overplotting
ggplot(diamonds, aes(carat, price)) +
  geom_point()

# Sin overplotting
ggplot(diamonds, aes(carat, price)) +
  geom_point(alpha = 0.1, shape = 4)

12.4.2 Diagrama de barras

Barplots y sus asociados geom

12.4.2.1 Histogramas (geom_histogram())

Se emplean para represntar una variable continua.

ggplot(iris, aes(x = Sepal.Length)) +
  geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Al ejecutar la función nos aparece siempre un mensaje indicando que se ha empleado un número de barras (bins) de 30. Los histogramas no representan directamente nuestros datos, sino que representa una función estadística sobre nuestros datos. Lo que nos representa son básicamente bins sobre cómo se concentran los datos.

Podemos controlar la anchura de las barras con binwidth.

ggplot(iris, aes(x = Sepal.Length)) +
  geom_histogram(binwidth = 0.05)

La representación gráfica de un histograma requiere de unos cálculos que ggplot2 hace automáticamente y que almacena en su interior. Si queremos representar, por ejemplo, que nos muestre la densidad en vez de los contajes, tenemos que indicarle como atributo y y llamar a esos datos internos generados por ggplot2 para representar el histograma. Esta llamada a esos datos internos se hace con ..density..

ggplot(iris, aes(x = Sepal.Length)) +
  geom_histogram(aes(y = ..density..))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Como tenemos varias especies, podemos indicarle también que nos muestre las diferencias por especies.

ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
  geom_histogram(binwidth = 0.1)

De modo predeterminado, en el caso anterior te apila las barras una encima de la otra (stack). De hecho, es lo que se ejecuta de modo predeterminado, como se ve a continuación con position = "stack".

ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
  geom_histogram(binwidth = 0.1, position = "stack")

Existen otras posiciones disponibles, como dodge, aunque la interpretación también es algo confusa por la cantidad y el orden de las barras.

ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
  geom_histogram(binwidth = 0.1, position = "dodge")

Otra opción es usar fill que normaliza cada barra para representar la proporción de todas las variables en cada barra por cada categoría.

ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
  geom_histogram(binwidth = 0.1, position = "fill")
## Warning: Removed 6 rows containing missing values (geom_bar).

# A basic histogram, add coloring defined by cyl
ggplot(mtcars, aes(mpg, fill = cyl)) +
  geom_histogram(binwidth = 1)

# Change position to identity
ggplot(mtcars, aes(mpg, fill = cyl)) +
  geom_histogram(binwidth = 1, position = "identity")

# Change geom to freqpoly (position is identity by default)
ggplot(mtcars, aes(mpg, color = cyl)) +
  geom_freqpoly(binwidth = 1, position = "identity")

12.4.2.2 Diagrama de barras (geom_bar())

Con esta función simplemente contamos el número de valores en cada categoría.

ggplot(mtcars, aes(x = cyl)) +
  geom_bar()

También podemos cambiar las posiciones a las variantes que vimos en los histogramas.

ggplot(mtcars, aes(x = cyl, fill = factor(am))) +
  geom_bar(position = "stack")

ggplot(mtcars, aes(x = cyl, fill = factor(am))) +
  geom_bar(position = "fill")

ggplot(mtcars, aes(x = cyl, fill = factor(am))) +
  geom_bar(position = "dodge")

Se puede jugar también con position = "dodge" haciendo que se solapen las barras.

# 1 - The last plot form the previous exercise
ggplot(mtcars, aes(x = cyl, fill = factor(am))) +
  geom_bar(position = "dodge")

# 2 - Define posn_d with position_dodge()
posn_d <- position_dodge(width = 0.2)

# 3 - Change the position argument to posn_d
ggplot(mtcars, aes(x = cyl, fill = factor(am))) +
  geom_bar(position = posn_d)

# 4 - Use posn_d as position and adjust alpha to 0.6
ggplot(mtcars, aes(x = cyl, fill = factor(am))) +
  geom_bar(position = posn_d, alpha = 0.6)

Podemos emplear también paletas de colores con scale_fill_brewer().

# Use str() on Vocab to check out the structure
str(Vocab)
## 'data.frame':    21638 obs. of  4 variables:
##  $ year      : int  2004 2004 2004 2004 2004 2004 2004 2004 2004 2004 ...
##  $ sex       : Factor w/ 2 levels "Female","Male": 1 1 2 1 2 2 1 2 2 1 ...
##  $ education : int  9 14 14 17 14 14 12 10 11 9 ...
##  $ vocabulary: int  3 6 9 8 1 7 6 6 5 1 ...
# Plot education on x and vocabulary on fill
# Use the default brewed color palette
ggplot(Vocab, aes(x = factor(education), fill = factor(vocabulary))) +
  geom_bar(position = "fill") +
  scale_fill_brewer()
## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Blues is 9
## Returning the palette you asked for with that many colors

library(RColorBrewer)

# Definition of a set of blue colors
blues <- brewer.pal(9, "Blues") # from the RColorBrewer package

# 1 - Make a color range using colorRampPalette() and the set of blues
blue_range <- colorRampPalette(blues)

# 2 - Use blue_range to adjust the color of the bars, use scale_fill_manual()
ggplot(Vocab, aes(x = factor(education), fill = factor(vocabulary))) +
  geom_bar(position = "fill") +
  scale_fill_manual(values = blue_range(11))

12.4.2.3 Gráfico de líneas (geom_line())

El gráfico de líneas representa una línea que va conectando puntos. Se realiza en ggplot2 con la función geom_line(). Vamos a estudiar la relación entre el tiempo y la temperatura en castores para ver cuándo están activos y cuándo no. Para ello cargamos el conjunto de datos beavers que contiene dos conjuntos, beaver1 y beaver2. Vamos a filtrar beaver2 para que el tiempo sea mayor de 900 (las 9:00 de la mañana).

data(beavers)

library(dplyr)
beaver <- beaver2 %>%
  filter(time > 900)

Ahora vamos a representar un gráfico de líneas donde se va a ver la línea uniendo las temperaturas separando por si está activo o no.

ggplot(beaver, aes(x = time, y = temp)) +
  geom_line()

ggplot(beaver, aes(x = time, y = temp, color = factor(activ))) +
  geom_line()

Vamos a emplear el conjunto de datos economics, localizado dentro de ggplot2, que muestra una serie temporal del desempleo en el Banco de la Reserva Federal de San Luis en los EE.UU.

data("economics")

# Número de desempleados por año
ggplot(economics, aes(x = date, y = unemploy)) +
  geom_line()

# Número de desempleados en relación a la población total por año
ggplot(economics, aes(x = date, y = unemploy/pop)) +
  geom_line()

Es interesante ahora añadir los periodos de recesión económica. Para ello cargamos los datos y los convertimos a formato de fecha:

recess <- data.frame(
  begin = c("1969-12-01","1973-11-01","1980-01-01","1981-07-01","1990-07-01","2001-03-01"), 
  end = c("1970-11-01","1975-03-01","1980-07-01","1982-11-01","1991-03-01","2001-11-01"),
  stringsAsFactors = F
  )

library(lubridate)
recess$begin <- ymd (recess$begin)
recess$end <- ymd (recess$end)

Ahora combinamos ambos conjuntos de datos empleando la función geom_rect() para mostrar los periodos de recesión.

ggplot(economics, aes(x = date, y = unemploy/pop)) +
  geom_rect(data = recess,
         aes(xmin = begin, xmax = end, ymin = -Inf, ymax = +Inf),
         inherit.aes = F, fill = "red", alpha = 0.2) +
  geom_line()

El conjunto de datos peces contiene las capturas globales de tiene especies de salmón entre 1950 y 2010. Vemos su estructura.

str(peces)
## 'data.frame':    61 obs. of  8 variables:
##  $ Year    : int  1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 ...
##  $ Pink    : int  100600 259000 132600 235900 123400 244400 203400 270119 200798 200085 ...
##  $ Chum    : int  139300 155900 113800 99800 148700 143700 158480 125377 132407 113114 ...
##  $ Sockeye : int  64100 51200 58200 66100 83800 72000 84800 69676 100520 62472 ...
##  $ Coho    : int  30500 40900 33600 32400 38300 45100 40000 39900 39200 32865 ...
##  $ Rainbow : int  0 100 100 100 100 100 100 100 100 100 ...
##  $ Chinook : int  23200 25500 24900 25300 24500 27700 25300 21200 20900 20335 ...
##  $ Atlantic: int  10800 9701 9800 8800 9600 7800 8100 9000 8801 8700 ...

Vamos a reordenar su estructura mediante el empleo de la función gather() para que se quede una columna de año especie y captura.

library(tidyr)
peces.ord <- gather(peces, Species, Capture, -Year)

Representamos ahora la serie temporal separando por especies.

ggplot(peces.ord, aes(x = Year, y = Capture, color = Species)) +
  geom_line()

Podemos también representar las áreas, en vez de las líneas, con la función geom_area() y con geom_ribbon(). Con esta última función se fuerza a la y a ser 0 y es útil cuando las áreas tienen un alto nivel de solapamiento.

ggplot(peces.ord, aes(x = Year, y = Capture, fill = Species)) +
  geom_area(alpha = 0.6)

ggplot(peces.ord, aes(x = Year, y = Capture, fill = Species)) +
  geom_area(alpha = 0.8, position = "fill")

ggplot(peces.ord, aes(x = Year, y = Capture, fill = Species)) +
  geom_ribbon(aes(ymax = Capture, ymin = 0), alpha = 0.3)

12.5 qplot y la envoltura

En este capítulo aprenderás sobre qplot; es una forma rápida y sucia de ggplot2. No es tan intuitivo como la función ggplot(), pero puede ser útil en casos específicos.

Comparemos ggplot2 con qplot(). Se ve que qplot() da el mismo resultado que ggplot() porque asume que queremos representar un diagrama de dispersión.

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, col = Species)) + 
  geom_point()

qplot(x = Sepal.Length, y = Sepal.Width, col = Species, data = iris)

Podemos añadir otros argumentos a qplot(), como position. Cuando añadimos alpha tenemos un problema, porque qplot() interpreta que queremos mapear el valor de alpha. Para prevenir su representación en la leyenda tenemos que inhibirlo con alpha = I(0.5).

qplot(x = Sepal.Length, y = Sepal.Width, col = Species, alpha = 0.6, data = iris)

qplot(x = Sepal.Length, y = Sepal.Width, col = Species, alpha = I(0.6), data = iris)

qplot() es más versátil que la función plot() de base ya que, aunque es más sucio que ggplot() permite integrar muchos argumentos del mismo. Puede parecer que emplear qplot() es más sencillo que ggplot(), pero esto es así para figuras sencillas. Una vez que queremos complicar y añadir capas a la figura, ggplot() permite emplear la gramática de un modo más eficaz y creativa que qplot().

# basic qplot scatter plot:
qplot(wt, mpg, data = mtcars)

# Categorical variable mapped onto size:
# cyl
qplot(wt, mpg, data = mtcars, size = factor(cyl))
## Warning: Using size for a discrete variable is not advised.

# gear
qplot(wt, mpg, data = mtcars, size = factor(gear))
## Warning: Using size for a discrete variable is not advised.

# Continuous variable mapped onto col:
# hp
qplot(wt, mpg, data = mtcars, color = hp)

# qsec
qplot(wt, mpg, data = mtcars, color = qsec)

# qplot() with x only
qplot(data = mtcars, x = factor(cyl))

# qplot() with x and y
qplot(data = mtcars, x = factor(cyl), y = factor(vs))

# qplot() with geom set to jitter manually
qplot(data = mtcars, x = factor(cyl), y = factor(vs), geom = "jitter")

Sin embargo, para hacer un gráfico de puntos “verdadero”, puede usar geom_dotplot(). La diferencia es que a diferencia de geom_point(), geom_dotplot() utiliza una estadística de binning. Binning significa cortar una variable continua (la y en este caso) en “bins” discretos. Una cosa a notar es que geom_dotplot() usa un símbolo de trazado diferente a geom_point(). Para estos símbolos, la estética del color cambia el color de su borde, y la estética del relleno cambia el color de su interior.

# cyl and am are factors, wt is numeric
class(mtcars$cyl)
## [1] "factor"
class(mtcars$am)
## [1] "numeric"
class(mtcars$wt)
## [1] "numeric"
# "Basic" dot plot, with geom_point():
ggplot(mtcars, aes(cyl, wt, col = am)) +
  geom_point(position = position_jitter(0.2, 0))

# 1 - "True" dot plot, with geom_dotplot():
ggplot(mtcars, aes(cyl, wt, fill = am)) +
  geom_dotplot(binaxis = "y", stackdir = "center")
## `stat_bindot()` using `bins = 30`. Pick better value with `binwidth`.

# 2 - qplot with geom "dotplot", binaxis = "y" and stackdir = "center"
qplot(
  cyl, wt,
  data = mtcars,
  fill = am,
  geom = "dotplot",
  binaxis = "y",
  stackdir = "center"
)
## `stat_bindot()` using `bins = 30`. Pick better value with `binwidth`.

12.6 Resumiendo

data("ChickWeight")

# ChickWeight is available in your workspace
# 1 - Check out the head of ChickWeight
head(ChickWeight)
## Grouped Data: weight ~ Time | Chick
##   weight Time Chick Diet
## 1     42    0     1    1
## 2     51    2     1    1
## 3     59    4     1    1
## 4     64    6     1    1
## 5     76    8     1    1
## 6     93   10     1    1
# 2 - Basic line plot
ggplot(ChickWeight, aes(x = Time, y = weight)) +
  geom_line(aes(group = Chick))

# 3 - Take plot 2, map Diet onto col.
ggplot(ChickWeight, aes(x = Time, y = weight, color = Diet)) +
  geom_line(aes(group = Chick))

# 4 - Take plot 3, add geom_smooth()
ggplot(ChickWeight, aes(x = Time, y = weight, color = Diet)) +
  geom_line(aes(group = Chick), alpha = 0.3) +
  geom_smooth(lwd = 2, se = F)
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

12.7 Estadísticas

La capa de estadísticas en ggplot2 presenta dos categorías de funciones: aquéllas que se ejecutan dentro de una geometría o las que se generan independientemente. Estas últimas comienzan con stat_...().

stat_bin() en geom_histogram(), geom_bar() y geom_freqpoly() stat_smooth() en geom_smooth() stat_boxplot() en geom_boxplot() stat_bindot() en geom_dotplot() stat_bin2d() en geom_bin2d() stat_binhex() en geom_hex() stat_contour() en geom_contour() stat_quantile() en geom_quantile() stat_sum() en geom_count()

12.7.1 stat_bin()

Por ejemplo, cuando se realiza un histograma se mencionó que se ejecuta de modo predeterminado con el estadístico de bins. Pero si ejecutamos entonces directamente stat_bin(), obtendremos el mismo resultado. Esta función simplemente cuenta el número de observaciones, por lo que el resultado al hacer un histograma o diagrama de barras es idéntico.

p <- ggplot(iris, aes(x = Sepal.Length))
p + geom_histogram() # histograma
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

p + stat_bin()       # estadístico con bins
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

12.7.2 Suavizado con stat_smooth()

Esta función se ejecuta en el interior de geom_smooth() o puede ejecutarse también independientemente. Si no se especifica el tipo de suavizado, de método predeterminado se emplea el loess (method = "loess"), que es un algoritmo de suavizado no paramétrico que básicamente funciona pasando como una ventana deslizante a lo largo del eje x. En cada ventana se calcula una media ponderada. Pero también hay otros métodos, como los métodos paramétricos, entre los que se encuentran lm, glm, rlm y gam. Para grupos mayores de 1000 el método predeterminado es gam.

# Diagrama de dispersión con loess
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  geom_smooth()
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

# Diagrama de dispersión con un modelo lineal y 95% intervalo de confianza
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  geom_smooth(method = "lm")

# Diagrama de dispersión con un modelo lineal y sin el 95% intervalo de confianza
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE)

Si empleamos grupos, podemos hacerlo específicandolo dentor de aesthetics.

ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) +
  geom_point() +
  stat_smooth(method = "lm", se = TRUE)

ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) +
  geom_point() +
  stat_smooth(method = "lm", se = FALSE) +
  stat_smooth(aes(group = 1), method = "lm", se = TRUE)

En la última figura anterior, se añadió una variable dummy (aes(group = 1)) para que no heredara la división por grupos y pudiera representar la regresión lineal de todos los datos en conjunto.

Vamos a continuar modificando stat_smooth(). En las figuras anteriores usamos se = FALSE en stat_smooth() para eliminar el intervalo de confianza del 95%. Aquí consideraremos otro argumento, span, usado en el suavizado LOESS, y veremos cómo personalizar varios modelos en relación a la representación de grupos y una combinación de grupos con el modelo general.

# Figura 1: utilizamos el método loess con span
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  geom_smooth(se = FALSE, span = 0.7)
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

# Figura 2: añadimos un segundo stat_smooth() para representar todos y por grupos
ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) +
  geom_point() +
  stat_smooth(method = "lm", se = FALSE) +
  stat_smooth(method = "loess", aes(group = 1),
              se = FALSE, col = "black", span = 0.7)

# Figura 3: col = "All" dentro de aes para mostrar en la leyenda la línea
ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) +
  geom_point() +
  stat_smooth(method = "lm", se = FALSE, lwd = 0.5) +
  stat_smooth(method = "loess",
              aes(group = 1, col = "All"),
              se = FALSE, span = 0.7)

# Plot 4: Cambiamos los colores de la línea
myColors <- c(brewer.pal(3, "Dark2"), "black")
ggplot(mtcars, aes(x = wt, y = mpg, col = factor(cyl))) +
  geom_point() +
  stat_smooth(method = "lm", se = FALSE, lwd = 0.5) +
  stat_smooth(method = "loess", 
              aes(group = 1, col="All"), 
              se = FALSE, span = 0.7) +
  scale_color_manual("Cylinders", values = myColors)