Entendiendo ggplot2

Introducción

A diferencia de los gráficos con el paquete base donde creamos un gráfico a base de pasos sucesivos, ggplot2 se basa en una gramática de gráficos, añadiendo elementos a un graphical device , donde distintos componentes independientes se pueden combinar de muchas maneras diferentes.

Así, ggplot2 fracciona un gráfico en tres partes fundamentales que son: data, aesthetics y geometry, las dos últimas se denominan layers (en general son elementos geométricos y transformaciones estadísticas). Esto nos permite crear gráficos de una forma interactiva, comenzando con los datos que queremos representar y añadiéndole sucesivamente layers que completan y dan forma al gráfico.

Como dice Wickhman: reducir la distancia entre el gráfico pensado y el que estás creando en tu script, nos permite crear gráficos utilizando la misma estructura de pensamiento que empleamos para diseñar un análisis. También nos obliga a pensar un gráfico antes de crearlo, pero esto mismo ocurre cuando queremos llevar a cabo un análisis.

Nota: La gg del nombre de ggplot2 viene de Grammar of Graphics.

La función ggplot()

ggplot(data = NULL, mapping = aes(), ..., environment = parent.frame())
  • data: un data frame
  • aes, aesthetics: se emplea para indicar los ejes x e y. También para controlar colores, tamaños, formas, alturas, etc....
  • geometry: aquí indicaremos el tipo de gráfico (histograma, box plot, líneas,...densidades, puntos,...)

Para crear un gráfico con ggplot, lo primero que hay que hacer es definir el conjunto de datos, el sistema de coordenadas y entonces, aplicar una geometría determinada. Este sería el esquema más básico: datos, geometría y sistema de coordenadas ( por defecto el sistema de coordenadas es el cartesiano).


Vamos a verlo paso a paso. Trabajaremos con el dataframe mtcars, y consideraremos la variable cyl y gear como factores:

library(ggplot2)
data(mtcars)
df     <- mtcars[ , c("mpg", "cyl", "wt", "gear") ]
df$cyl <- factor( df$cyl )
df$gear <- factor( df$gear )

head(df)

i. Datos

Queremos representar el peso (wp) frente/relacionada con las millas/galon (mpg).
En una primera aproximación definimos la parte del origen de datos y los ejes en la función ggplot() con el argumento aes():

ggplot(data = mtcars, aes(x = wt, y = mpg) ) 

Observamos como en aes establecemos los ejes, pero en ningún momento hemos especificado un layer de geometría, así que no producirá más que un marco general (ejes cartesianos), sin una geometría especifica.

Este comportamiento nos da una idea de la forma interactiva de trabajar con ggplot2.

ii. Geometrías

Para añadir layers empleamos el signo + y continuamos en la siguiente línea del layer:

ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    geom_point()

La función geom_point() admite muchos argumentos, como el tamaño de los puntos (size), la forma (shape), el color (color), etc..

ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    geom_point( size=1.5, shape=18, color="red" )

Nota: Hay layers más avanzados que hacen transformaciones de los datos: stat_density(), stat_count(),..

Prueba a emplear otros layers de geometrías como geom_line():

ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    geom_....
ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    geom_line()

iii. Varias geometrías

Incluso podemos añadir más de un layer de geometría

ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    geom_line( linetype=2 ) + 
    geom_point( col="blue" )

iv. Diferentes orígenes de datos

También es posible emplear diferentes orígenes de datos para cada layer. Aunque complica el gráfico, nos ayuda a comprender la potencia de los layers de geometría de ggplot. Presentamos un ejemplo:

ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    geom_point( col="blue" ) +
    geom_line( data=head(mtcars), color="red" )

Observamos cómo la línea, geom_line() solo actúa sobre el subconjunto head(mtcars).

También podemos hacer cálculos sencillos sobre las variables

ggplot( data = mtcars, aes(x = log(wt), y = log(mpg) )) +
    geom_point( col="blue" )

Ejercicio: ¿Puedes anticiparte a lo que estas sentencias producirán?:

  1. ggplot( data = diamonds, aes(carat, price) ) + geom_point()
  2. ggplot( data = economics, aes(date, unemploy) ) + geom_line()
data(diamonds)

data(economics)
data(diamonds)
ggplot( data = diamonds, aes(carat, price) ) + geom_point()
data(economics)
ggplot( data = economics, aes(date, unemploy) ) + geom_line()

v. Sistemas de coordenadas

En ocasiones conviene parametrizar el sistema de coordenadas, por ejemplo para hacer zoom en una parte del gráfico

ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    coord_cartesian(xlim=c(2,5)) +
    geom_line()

¿Se te ocurre cómo limitar también el eje de OY, por ejemplo entre 15 y 20?

ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    coord_cartesian(xlim=c(2,5), ....) +
    geom_line()
ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    coord_cartesian(xlim=c(2,5), ylim = c(15,20)) +
    geom_line()

Quiz

Refinando un gráfico: colour, shape y size

Podemos añadir más variables al gráfico introduciendo nuevos aesthetics en la llamada a aes()

Trabajamos con el conjunto de datos mpg;

Por ejemplo, cada punto de un color según la clase (class) a la que pertenezca con colour=class:
ggplot( data = mpg, aes(cty, hwy, colour=class) ) +
    geom_point()
Incluimos una forma según drv fijando shape= drv:
ggplot( data = mpg, aes(displ, hwy, colour=class, shape= drv) ) +
    geom_point()
más aún:
ggplot( data = mpg, aes(displ, hwy, colour=class, shape= drv, size=cyl) ) +
    geom_point()

Generalmente, diferentes tipos de atributos estéticos trabajan mejor con ciertos tipos de variables, por ejemplo color y shape son apropiados para variables categóricas, size con continuas.

Como dice Hadley Wickham cuando se utilizan elementos estéticos (aesthetics) en un gráfico, menos es normalmente más. Es difícil ver las relaciones simultáneas entre el color, la forma y el tamaño, por lo que hay que ser prudente a la hora de utilizar demasiados aesthetics simultáneamente. En lugar de tratar de hacer una gráfico muy complejo que muestre todo a la vez, hay que intentar, quizás, crear una serie de gráficos simples que cuenten una historia, llevando al lector de la ignorancia al conocimiento.

when using aesthetics in a plot, less is usually more (Hadley Wickham)

Agrupamientos: Como estamos viendo, para añadir más información a un gráfico, en general lo que se hace es añadir más variables en los aesthetics como son: colour, shape, fill, linetype, fill, linetypes y más abruptamente con facets.

En ocasiones también podemos emplear el argumento group que suele requerir variables discretas para que ggplot particione con respecto a esa variable. Así la variable de group debe tener un valor diferente para cada grupo. Como decíamos antes, diferentes argumentos trabajan mejor con diferentes geometrías y las geometrías se comportan mejor o peor, dependiendo del tipo de datos: continuos, discretos, mixtos,...

Otro ejemplo:

ggplot( data = df, aes(mpg, wt,  group=cyl, color=cyl) ) +
    geom_line()

Observamos cómo un gráfico de líneas hace una línea diferente para cada "grupo". Las hemos coloreado para mejor entendimiento del ejemplo. Pero un efecto parecido podemos obtener jugando con el tipo de línea.

ggplot( data = df, aes(mpg, wt,  linetype=cyl) ) +
    geom_line()

Facetting

El enfoque facetting nos permite crear matrices de gráficos seǵun una variable categórica. facetting creará una tabla de gráficos, un gráfico para cada subconjunto. El gráfico se divide en tantas columnas como niveles hay en la variable class con facet_grid(.~class)

ggplot( data = mpg, aes(displ, hwy) ) +
    geom_point() +
    facet_grid(.~class)
En tantas filas como class con facet_grid(class~.)
ggplot( data = mpg, aes(displ, hwy) ) +
    geom_point() +
    facet_grid(class~.)
En tantas filas como cyl y columnas como class con facet_grid(cyl~class)
ggplot( data = mpg, aes(displ, hwy) ) +
    geom_point() +
    facet_grid(cyl~class)

Customización plus

i. Líneas auxiliares

Podemos añadir líneas verticales u horizontales a un gráfico empleando el layer geom_abline() y sus derivados: geom_hline() y geom_vline() .

Ejercicio: Añade una línea que pase por el punto (0,10) y tenga pendiente m=3, otra vertical que pase por el punto (3,0) y una horizontal que pase por el punto (25,0)

ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    geom_line() +
    geom_abline (aes(intercept=...
ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    geom_line() +
    geom_abline (aes(intercept=10, slope=3), col="red", linetype=2)+
    geom_vline(aes(xintercept=3), col="blue", linetype=3)+
    geom_hline(aes(yintercept=24),col="purple", linetype=4) 

ii. Segmentos

ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    geom_line() +
     geom_segment(aes(x = 2, y = 15, xend = 3, yend = 15))

Podemos rápidamente convertir un segmento en una flecha:

ggplot( data = mtcars, aes(x = wt, y = mpg) ) +
    geom_line() +
     geom_segment(aes(x = 2, y = 15, xend = 3, yend = 15),
      arrow = arrow(length = unit(0.5, "cm")))

iii. Etiquetado de ejes

En ocasiones debemos de etiquetar el gráfico y los ejes. Para eso disponemos de funciones específicas que sobreescriben lo parametrizado por defecto.

  • p + ggtitle("Titulo principal"): añade un título principal
  • p + xlab("Eje 0X"): Cambia solo la etiqueta del eje de las Xs
  • p + ylab("Eje 0Y"): Cambia solo la etiqueta del eje de las Ys
  • p + labs(title = "Titulo", x = "Eje 0X", y = "Eje 0Y"): Todo a la misma vez (esta es la forma más usada)

Se puede obligar un salto de línea empleando "\n"

pp <- ggplot( data = df, aes(x = cyl, y = mpg, group = cyl, fill=cyl) ) +
  geom_boxplot()
pp

pp <- pp +
  labs(title = "Birria de gráfico", x = "Eje 0X: cyl", y = "Eje 0Y: mpg", subtitle = "Un subtitulo..")
pp

Podemos también cambiar el tema de los ejes, incluido el tipo de letra con face, que puede tomar los valores: "plain", "italic", "bold" y "bold.italic".

pp <- pp +
  theme(  plot.title = element_text(color="red", size=12, face="bold.italic"),
          axis.title.x = element_text(color="blue", size=12, face="bold"),
          axis.title.y = element_text(color="#993333", size=12, face="italic")
          )
pp

Nota: element_blank() oculta la etiqueta cuando empleamos theme()

¿Cómo harías para que omitiera la etiqueta del eje OY y sí apareciera la de OX? Emplea la ayuda F1 si es preciso

pp <- pp +
  .....
pp
pp <- pp +
  xlab("Eje 0X: wt") +
  ylab(NULL)
pp

iv. Leyendas

Podemos modificar elementos de la leyenda que ggplot ofrece por defecto. Por ejemplo el título, puesto que crea la leyenda según la variable que pongamos en fill.

pp + labs(fill = "Nº de \nvelocidades")
pp

También se puede cambiar la posición: "left","top", "right", "bottom", "none". Incluso podemos ser más precisos empleando un vector con componentes que toman valores entre 0 y 1: c(0,0) corresponde a la "parte inferior izquierda" y c(1,1) corresponde a la "parte superior derecha".

pp + theme(legend.position="top")
pp + theme(legend.position = c(0.13, 0.2))

Para no incluir leyenda se puede utilizar la sentencia theme(legend.position = "none")

pp + theme(legend.position = "none")

Límites de los ejes

Hay varias formas de hacerlo, algunas implican el recortado de datos y otras no. Es más recomendable no recortar los datos.

  1. p + coord_cartesian(xlim = c(5, 20), ylim = (0, 50)): Así hacemos un zoom en el gráfico, sin recortar los datos.
  2. Con recorte de los datos (elimina los puntos de datos no vistos)
  • p + xlim(5, 20) + ylim(0, 50)
  • p + scale_x_continuous(limits = c(5,20)) + scale_y_continuous(limits = c(0, 50))
  1. Expandir los límites del gráfico con datos: expand_limits() crea una delgada envoltura alrededor de geom_blank() que facilita añadir datos al gráfico.
  • p + expand_limits(x = 0, y = 0): ajustar el corte de los ejes x e y a (0,0)
  • p + expand_limits(x = c(5, 50), y = c(0, 150))

Probamos estas funciones con el siguiente gráfico de puntos:

data(cars)
q <- ggplot(cars, aes(x = speed, y = dist, col=rep(1:5,10) )) + geom_point()
q

q <- ggplot(cars, aes(x = speed, y = dist, col=rep(1:5,10) )) + geom_point()
q + coord_cartesian(xlim =c(5, 20), ylim = c(0, 50)) +xlab("coord_cartesian")
q + xlim(5, 20) + ylim(0, 50) + xlab("xlim")
q + scale_x_continuous(limits = c(5,20)) + scale_y_continuous(limits = c(0, 50)) + xlab("scale_x_continuous")
q + expand_limits(x = 0, y = 0) + xlab("expand_limits(x=0. u=0...")
q + expand_limits(x = c(5, 50), y = c(0, 150))+ expand_limits("expand_limits(x = c(5, 50)")

Miscelánea

Polígonos

library("maps")
spain = map_data('world', region = 'Spain')
ggplot(spain, aes(x = long, y = lat, group = group)) +
    geom_polygon(fill = 'white', colour = 'black')

puntos <- data.frame( x=c(2,5,7), y=c(3,5,3) )
ggplot(puntos, aes(x = x, y = y)) +
    geom_polygon(colour="red", alpha=0.5)

Colores

Por la importancia que tiene el color en un gráfico vamos a ahondar un poco más en este aesthetics ya que, con muy poco más, los resultados puden ser muchísimo mejores.

ToothGrowth$dose <- factor(ToothGrowth$dose)
bp <- ggplot(ToothGrowth, aes(x=dose, y=len)) +
    geom_boxplot()
bp

Podemos usar los argumentos estándar de la geometría concreta que estemos empleando y asignarle colores:

bp + geom_boxplot(fill = 'steelblue', color = "red")

O colorear según alguna variable del conjunto de datos para añadir más información

bp + geom_boxplot(aes(fill = dose)) 

La luminosidad y la intensidad de color de los colores por defecto puede ser modificado usando las funciones scale_hue():

bp + geom_boxplot(aes(fill = dose)) +
    scale_fill_hue(l=40, c=35)

Aunque también podemos asignar colores manualmente:

bp + geom_boxplot(aes(fill = dose)) +
    scale_fill_manual(values=c("#999999", "#E69F00", "#56B4E9"))

Paletas de color

Una opción muy interesante es aplicar paletas que combinan colores y que están estudiadas para que queden bien:

library(RColorBrewer)

bp + geom_boxplot(aes(fill = dose)) +
    scale_fill_brewer(palette="Dark2")

En el caso de gráficos de líneas o puntos en lugar de scale_fill_brewer() empleamos scale_color_brewer()

Nos pude ser de utilidad el portal COLORBREWER 2.0 para seleccionar paletas según los criterios que deseemos, por ejemplo "6 colores divergentes" o "6 colores en secuenciales",...

Estas son unas paletas aptas para daltónicos:

# contiene el verde
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")
# con color negro
cbbPalette <- c("#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")

scale_fill_manual(values=cbPalette) o así scale_colour_manual(values=cbPalette)

Una opción muy socorrida es emplear escalas de grises:

bp + geom_boxplot(aes(fill = dose)) +
    scale_fill_grey(start=0.8, end=0.2) 

Igualmente para gráficos de líneas o puntos emplear en lugar de scale_fill_grey() scale_color_grey()

También podemos especificar un gradiente entre dos colores:

  • scale_color_gradient(), scale_fill_gradient() para gradientes secuenciales entre dos colores
  • scale_color_gradient2(), scale_fill_gradient2() para gradientes divergente
  • scale_color_gradientn(), scale_fill_gradientn() para gradientes secuenciales entre n colores
class(mtcars$qsec)
sp2 <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point(aes(color = qsec))
sp2
sp2 + scale_color_gradient(low="blue", high="red")

# divergente con un punto medio, la media por ejemplo
mid <- mean(mtcars$qsec)
sp2 + scale_color_gradient2(midpoint = mid, low = "blue", mid = "white", high = "red", space = "Lab" )

Gradientes entre varios colores:

sp2 <- ggplot(mtcars, aes(x = wt, y = mpg)) +
    geom_point(aes(color = qsec))
sp2 + scale_color_gradientn(colours = rainbow(5))

Tipos de lineas y formas

Los valores que los argumentos linetype y shape pueden tomar son estos:

Autoevaluación

Ahora puedes practicar lo que has aprendido descargando el siguiente fichero Rmd, abriéndolo con Rstudio y resolviendo las preguntas de autoevaluación.

Autoevaluación

Para descargar el Rmd de autoevaluación haz click derecho en el enlace anterior y "Guardar enlace como..." y selecciona dónde quieres guardarlo.

Si al abrirlo con Rstudio observas caracteres extraños, selecciona File/Reopen with encoding/UTF-8 y ya se debe ver bien.

Dónde seguir

Hay muchos sitios donde encontrar buen material sobre ggplot, nosotros recomendamos el libro de Hadley Wickham por que es fácil de leer, muy didáctico, asequible y se aprende mucho. Un libro intermedio es el de Alboukadel Kassambara que es un manual de acceso rápido a muchos tipos de gráficos. Para ir tirando en momentos de apuros es muy útil la cheat sheet de Rstudio y el portal de STHDA.

Continuar con tabulaR

Una vez terminado este documento puede:



Última actualización: 20201116-1211 DOI