1 Introducción

El curso base en el que este notebook está basado es R graphics with ggplot2 workshop notes. Aquí está la traducción y algunas modificaciones para su mejor adaptación a un notebook.

Los materiales y datasets están aquí. También debes instalar el paquete tidyverse y cargarlo. Este paquete incluye la librería ggplot2. También hay algún ejercicio incluido, hacerlos sin ver el código, y usar el código incluido para chequear el tuyo.

Por qué usar ggplot2:

La idea básica es construir el gráfico con bloqeus independientes, que se construyen de forma aislada. Los bloques que constituyen un gráfico son los siguientes:

2 Cargar los datos de ejemplo

Cargamos los datos de ejemplo, que son los precios de las casas en los diferentes estados de USA.

housing <- read_csv("dataSets/landdata-states.csv")
## Parsed with column specification:
## cols(
##   State = col_character(),
##   region = col_character(),
##   Date = col_double(),
##   Home.Value = col_double(),
##   Structure.Cost = col_double(),
##   Land.Value = col_double(),
##   Land.Share..Pct. = col_double(),
##   Home.Price.Index = col_double(),
##   Land.Price.Index = col_double(),
##   Year = col_double(),
##   Qrtr = col_double()
## )
head(housing)
State region Date Home.Value Structure.Cost Land.Value Land.Share..Pct. Home.Price.Index Land.Price.Index Year Qrtr
AK West 2010.25 224952 160599 64352 28.6 1.481 1.552 2010 1
AK West 2010.50 225511 160252 65259 28.9 1.484 1.576 2010 2
AK West 2009.75 225820 163791 62029 27.5 1.486 1.494 2009 3
AK West 2010.00 224994 161787 63207 28.1 1.481 1.524 2009 4
AK West 2008.00 234590 155400 79190 33.8 1.544 1.885 2007 4
AK West 2008.25 233714 157458 76256 32.6 1.538 1.817 2008 1

3 ggplot2 vs gráficos base de R

Comparando con los gráficos base de R, ggplot2:

Histograma con los gráficos base:

hist(housing$Home.Value)

Histograma con ggplot2:

ggplot(housing, aes(x = Home.Value)) +
  geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Ahora vamos a ver un gráfico con colores y varias series de datos.

Con los gráficos base:

plot(Home.Value ~ Date,
     col = factor(State),
     data = filter(housing, State %in% c("MA", "TX")))
legend("topleft",
       legend = c("MA", "TX"),
       col = c("black", "red"),
       pch = 1)

Con ggplot2:

ggplot(filter(housing, State %in% c("MA", "TX")),
       aes(x=Date,
           y=Home.Value,
           color=State))+
  geom_point()

Con ggplot2, en una sola instrucción, hacemos el filtrado, especificamos los ejes y la series de datos y el tipo de gráfico.

4 Objetos geométricos y aesteticos

4.1 Mapeo aestético

En ggplot2, aestético significa “algo que puedes ver”. Algunos ejemplos son:

  • Posición (por ejemplo, los ejes x e y)
  • Color (color “externo”)
  • Fill (color de relleno)
  • Shape (forma de puntos)
  • Linetype (tipo de linea)
  • Size (tamaño)

Cada tipo de objeto geométrico (geom) solo acepta un subconjunto de todos los aestéticos. Puedes consultar la pagina de ayuda de geom() para ver que aestéticos acepta. El mapeo aestético se hace con la función aes().

4.2 Objetos geométricos

Los objetos geométricos son las formas que puede tomar un gráfico. Algunos ejemplos son:

  • Puntos (geom_point() para scatter plots, gráficos de puntos, etc…)
  • Lineas (geom_line() para series temporales, lineas de tendencia, etc…)
  • Cajas (geom_boxplot() para gráficos de cajas)

Un gráfico debe tener al menos un geom, pero no hay limite. Puedes añadir más geom usando el signo +.

Con el siguiente código podrás ver una lista de los objetos geométricos:

help.search("geom_", package = "ggplot2")
## starting httpd help server ... done

5 Gráficos de dispersión (scatter plots)

Ahora que ya sabemos que es el mapeo aestético y los objetos geométricos, podemos hacer un grafico de dispersión (scatter plot). geom_point() requiere mapeo para la x y la y, el resto es opcional.

hp2001Q1 <- filter(housing, Date == 2001.25) 
ggplot(hp2001Q1,
       aes(y = Structure.Cost, x = Land.Value)) +
  geom_point()

ggplot(hp2001Q1,
       aes(y = Structure.Cost, x = log(Land.Value))) +
  geom_point()

5.1 Recta de regresión

Podemos combinar objetos geom() para añadir al gráfico una recta de regresión, y añadir un color a los puntos en base a una variable.

hp2001Q1$pred.SC <- predict(lm(Structure.Cost ~ log(Land.Value), data = hp2001Q1)) #añadimos una variable con los valores de la recta de regresión del coste de estructura en base al valor del terreno

p1 <- ggplot(hp2001Q1, aes(x = log(Land.Value), y = Structure.Cost)) #gráfico base

p1 + geom_point(aes(color = Home.Value)) + #añade el color a los puntos en base al valor
  geom_line(aes(y = pred.SC)) #añade la recta de regresión

5.2 Suavizadores (smoothers)

El geom_smooth() añade una aproximación de los datos x e y, e incluye una cinta con el margen de confianza.

p1 +
  geom_point(aes(color = Home.Value)) +
  geom_smooth() #sustituimos la recta de regresión geom_line() anterior por la aproximación
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

5.3 Texto (etiquetas en los puntos)

Podemos añadir una etiquetas a los puntos del gráfico de dispersión con el objeto geom_text().

p1 + 
  geom_point() +
  geom_text(aes(label=State), size = 3)

Con el objeto geom_text_repel() de la libreria ggrepel, las etiquetas no se colocan encima de los puntos, sino a un lado.

p1 + 
  geom_point() + 
  geom_text_repel(aes(label=State), size = 3)

5.4 Mapeo de aestéticos vs asignación

Dentro de aes() debemos poner las variables aestéticas, mientras que valores fijos para todas las variables las podemos poner fuera.

p1 +
  geom_point(aes(size = 2),# incorrecto! 2 no es una variable
             color="red") # correcto, todos los puntos en rojo

p1 +
  geom_point(size = 2,# correcto, todos los puntos de tamaño 2
             color="red") # correcto, todos los puntos en rojo

5.5 Mapeo de variables a otros aestéticos

Se pueden mapear otros aestéticos, como x e y, para visualizar algunas cosas concretas.

p1 +
  geom_point(aes(color=Home.Value, shape = region))

6 Ejercicio 1

Lee los datos del fichero dataSets/EconomistData.csv. En estos datos vemos los valores del Human Development Index y Corruption Perception Index para varios paises.

Hay que crear un gráfico con los siguientes parámetros:

  1. Crear un gráfico de dispersión con el CPI en el eje X y el HDI en el eje Y.
  2. Colorea los puntos en azul.
  3. Mapea el color de los puntos a la variable Region.
  4. Haz los puntos más grandes poniendo un tamaño de 2.
  5. Mapera el tamaño de los puntos al HDI.Rank
dat <- read_csv("dataSets/EconomistData.csv")
## Parsed with column specification:
## cols(
##   Country = col_character(),
##   HDI.Rank = col_double(),
##   HDI = col_double(),
##   CPI = col_double(),
##   Region = col_character()
## )
head(dat)
Country HDI.Rank HDI CPI Region
Afghanistan 172 0.398 1.5 Asia Pacific
Albania 70 0.739 3.1 East EU Cemt Asia
Algeria 96 0.698 2.9 MENA
Angola 148 0.486 2.0 SSA
Argentina 45 0.797 3.0 Americas
Armenia 86 0.716 2.6 East EU Cemt Asia

Punto 1:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point()

Punto 2:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point(color="blue")

Punto 3:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point(aes(color = Region))

Punto 4:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point(aes(color = Region),size = 2)

Punto 5:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point(aes(color = Region, size = HDI.Rank))

7 Transformaciones estadísticas

7.1 Transformaciones estadísticas

Algunos gráficos, como los gráficos de dispersión, no requieren transformación: cada punto es colocado en las coordenadas x e y igual a su valor original. Otros gráficos, como los boxplot, histogramas, rectas de predicción… necesitan transformaciones estadísticas:

  • Para el boxplot, el valor y es transformado a la media y 1.5(IQR)
  • Para los suavizadores (smoothers), el valor de la y debe transformarse en valores predictivos.

Cada objeto geom tiene un estadistico por defecto, pero se pueden cambiar. Por ejemplo, el estadistico por defecto para geom_bar es stat_bin:

args(geom_histogram)
## function (mapping = NULL, data = NULL, stat = "bin", position = "stack", 
##     ..., binwidth = NULL, bins = NULL, na.rm = FALSE, show.legend = NA, 
##     inherit.aes = TRUE) 
## NULL
args(stat_bin)
## function (mapping = NULL, data = NULL, geom = "bar", position = "stack", 
##     ..., binwidth = NULL, bins = NULL, center = NULL, boundary = NULL, 
##     breaks = NULL, closed = c("right", "left"), pad = FALSE, 
##     na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) 
## NULL

7.2 Estableciendo los argumentos de las transformaciones estadísticas

Los argumentos a las funciones stat_ se deben pasar a través de la funcion geom_. Para cambiar el valor por defecto, primero debemos saber que stat usa el geom, para después cambiarlo.

Por ejemplo, para el histograma de Home.Value:

p2 <- ggplot(housing, aes(x = Home.Value))
p2 + geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

En el mensaje de aviso especifica que se puede cambiar el valor del stat_bin() con la opción binwidth.

p2 + geom_histogram(stat = "bin", binwidth=4000) #hacemos 4000 particiones de los datos, en lugar de los 30 por defecto

7.3 Cambiando la transformación estadística

Algunas veces la transformación estadística por defecto no es la que necesitas. Esto pasa a menudo con datos con algún tratamiento previo, como las agrupaciones.

housing.sum <- aggregate(housing["Home.Value"], housing["State"], FUN=mean)
rbind(head(housing.sum), tail(housing.sum))
State Home.Value
1 AK 147385.14
2 AL 92545.22
3 AR 82076.84
4 AZ 140755.59
5 CA 282808.08
6 CO 158175.99
46 VA 155391.44
47 VT 132394.60
48 WA 178522.58
49 WI 108359.45
50 WV 77161.71
51 WY 122897.25

Y ahora intentamos hacer un gráfico de barras:

#ggplot(housing.sum, aes(x=State, y=Home.Value)) + 
#  geom_bar()

¿Cual es el problema? Basicamente, que hemos agregado los datos, y después pedimos a ggplot que los vuelva a agregar (geom_bar() por defecto utiliza stat = stat_count) y obviamente esto no funciona. Podemos solucionarlo diciendole a geom_bar que use una transformación estadística diferente:

ggplot(housing.sum, aes(x = State, y = Home.Value)) + 
  geom_bar(stat = "identity")

8 Ejercicio 2

  1. Recrear el grafico de dispersión con el CPI en el eje X y el HDI en el eje Y, como en el ejercicio anterior.
  2. Superpone una linea de prediccion usando geom_smooth()
  3. Superone una linea de predicion usando geom_smooth(), pero usando un modelo lineal para la predicción. Pista: consulta ?stat_smooth.
  4. Superpone una linea de predicción usando geom_line(). Pista: cambia la transformación estadistica.

BONUS: Superpone una linea de predicción usando el metodo loess por defecto, pero que sea menos suavizada. Pista: consulta ?loess

Punto 1:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point()

Punto 2:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point() +
  geom_smooth()
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

Punto 3:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point() +
  geom_smooth(method = "lm")

Punto 4:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point() +
  geom_line(stat = "smooth",method = "loess")

BONUS

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point() +
  geom_smooth(span = 0.1) #valores span mas bajos dan una curva menos suavizada
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

9 Escalas

Con aes() especificamos que variables incluimos en el gráfico, pero no especificamos como se muestran. Cuando especificamos color = x, no escogemos el color, o con shape = x no elegimos la forma. Para esto se utilizan las escalas.

En ggplot2, las escalas incluyen:

9.1 Argumentos de escala comunes

Los siguientes argumentos son comunes a la mayoría de las escalas de ggplot2:

name: el primer argumento da el eje o el titulo de la leyenda limits: el minimo y el maximo de la escala breaks: los punto a lo largo de la escala donde las etiquetas deben aparecer labels: las etiquetas que apareceran en cada break

Escalas específicas pueden tener argumentos adicionales: por ejemplo, la función scale_color_continouos tiene los argumentos low y high para establecer los colores para la parte baja y alta de la escala respectivamente.

9.2 Ejemplos de la modificación de las escalas

Empezamos construyendo un gráfico de puntos básico:

p3 <- ggplot(housing,
             aes(x = State,
                 y = Home.Price.Index)) + 
        theme(legend.position="top",
              axis.text=element_text(size = 6))
p4 <- p3 + geom_point(aes(color = Date),
                       alpha = 0.5,
                       size = 1.5,
                       position = position_jitter(width = 0.25, height = 0))
p4

Ahora modificamos los breaks de la escala de colores en la variable Date:

p4 + scale_x_discrete(name="State Abbreviation") +
  scale_color_continuous(name="",
                         breaks = c(1976, 1994, 2013),
                         labels = c("'76", "'94", "'13")) #cambiamos los breaks para la escala de colores de la variable Date

Ahora cambiamos la escala de colores para que los valores bajos sean azules y los valores altos sean rojos:

p4 +
  scale_x_discrete(name="State Abbreviation") +
  scale_color_continuous(name="",
                         breaks = c(1976, 1994, 2013),
                         labels = c("'76", "'94", "'13"),
                         low = "blue", high = "red")

En la librería scales hay funciones que pueden modificar más las escalas que los argumentos básicos:

p4 +
  scale_color_continuous(name="",
                         breaks = c(1976, 1994, 2013),
                         labels = c("76", "94", "13"),
                         low = muted("blue"), high = muted("red")) #la funcion muted está en la librería scales, y apaga los colores.

9.3 Usar diferentes escalas de colores

ggplot2 tiene una amplia variedad de escalas de color. Usando la funcion scale_color_gradient2 permite interpolar entre 3 colores diferentes.

p4 +
  scale_color_gradient2(name="",
                        breaks = c(1976, 1994, 2013),
                        labels = c("'76", "'94", "'13"),
                        low = muted("blue"),
                        high = muted("red"),
                        mid = "gray60", #color intermedio
                        midpoint = 1994) #establece el punto medio

Matriz parcial de escalas disponibles:

Escala Tipo Ejemplo
scale_color_ identity scale_color_discrete
scale_fill_ manual scale_fill_continuous
scale_size_ continuous scale_size_manual
- discrete scale_size_discrete
scale_shape_ discrete scale_shape_discrete
scale_linetype_ identity scale_linetype_identity
- manual scale_linetype_manual
scale_x_ continuous scale_x_continuous
scale_y_ discrete scale_y_discrete
- reverse scale_y_reverse
- log scale_y_log
- date scale_y_date
- datetime scale_y_datetime

En RStudio puedes escribir scale_ y con la tecla TAB puedes obtener la lista completa de escalas disponible.

10 Ejercicio 3

  1. Crea un gráfico de dispersión con CPI en el eje X y HDI en el eje Y. El color de los puntos debe indicar la región.
  2. Modifica los ejes x, y y la escala de color para tener nombres que se entiendan mejhor (e.g., escribe “Human development Index” en lugar de “HDI”). 3- Modifica el color de la escala para usar valores especificos que elijas. Pista: consulta ?scale_color_manual.

Punto 1:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point(aes(color = Region))

Punto 2:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point(aes(color = Region),size=2) + 
  scale_x_continuous(name = "Corruption Perception Index*") + 
  scale_y_continuous(name = "Human Development Index") + 
  scale_color_discrete(name = "World Region", h = c(0, 270))

Punto 3:

ggplot(dat,aes(x = CPI, y = HDI)) +
  geom_point(aes(color = Region),size=2) + 
  scale_x_continuous(name = "Corruption Perception Index*") + 
  scale_y_continuous(name = "Human Development Index") + 
  scale_color_manual(name = "World Region", values = c("Americas" = "red","Asia Pacific" = "blue","East EU Cemt Asia" = "yellow","EU W. Europe" = "green", "MENA" = "orange","SSA" = "purple"))

11 Aspecto (faceting)

Con esta funcionalidad podemos crear graficos diferentes para subsets de datos. Esto facilita la comparación de los datos, no solo de diferentes geom en un solo gráfico.

ggplot2 ofrece dos funciones para crear gráficos multiples:

11.1 Tendencias de precios por estado

Grafico básico con la evolución de precios por estado:

p5 <- ggplot(housing, aes(x = Date, y = Home.Value))
p5 + geom_line(aes(color = State))  

Con el gráfico anterior hay dos problemas: demasiados estados para distinguir el color de uno del otro; y que hay demasiadas lineas y se ocultan unas a otras.

Con la modificación del aspecto (faceting):

(p5 <- p5 + geom_line() +
   facet_wrap(~State, ncol = 10))

También está facet_grid() para dos dimensiones. Tiene cierta dificultad de visualización porque las variables categoricas tienen muchos niveles. Con un filtrado previo se facilitaría esta visualización.

(p6 <- p5 + geom_line() +
   facet_grid(cols = vars(State), rows = vars(region)))

12 Temas

El sistema ggplot2 theme maneja los elementos del gráfico que no están ligados a los datos como:

Los temas incluidos son:

Ejemplos:

p5 + theme_linedraw()

p5 + theme_light()

p5 + theme_minimal()

12.1 Sobreescribiendo los temas por defecto

Se pueden modificar los temas por defecto usando la función theme():

p5 + theme_minimal() +
  theme(text = element_text(color = "turquoise"))

Todas las opciones están documentadas en ?theme.

12.2 Creando y salvando temas nuevos

Puedes crear nuevos temas, como se puede ver en el siguiente ejemplo:

theme_new <- theme_bw() +
  theme(plot.background = element_rect(size = 1, color = "blue", fill = "black"),
        text=element_text(size = 12, family = "Serif", color = "ivory"),
        axis.text.y = element_text(colour = "purple"),
        axis.text.x = element_text(colour = "red"),
        panel.background = element_rect(fill = "pink"),
        strip.background = element_rect(fill = muted("orange")))

p5 + theme_new

14 Poniendo todo junto

14.1 Recrear el gráfico de The Economist

Como ultimo ejercicio, hay que reproducir el gráfico de The Economist que hay al inicio de este enlace. Con este codigo lo conseguimos:

df <- read_csv("dataSets/EconomistData.csv")
## Parsed with column specification:
## cols(
##   Country = col_character(),
##   HDI.Rank = col_double(),
##   HDI = col_double(),
##   CPI = col_double(),
##   Region = col_character()
## )
head(df)
Country HDI.Rank HDI CPI Region
Afghanistan 172 0.398 1.5 Asia Pacific
Albania 70 0.739 3.1 East EU Cemt Asia
Algeria 96 0.698 2.9 MENA
Angola 148 0.486 2.0 SSA
Argentina 45 0.797 3.0 Americas
Armenia 86 0.716 2.6 East EU Cemt Asia
#renombramos las regiones según aparecen en el gráfico original
df$Region <- factor(dat$Region,
                     levels = c("EU W. Europe",
                                "Americas",
                                "Asia Pacific",
                                "East EU Cemt Asia",
                                "MENA",
                                "SSA"),
                     labels = c("OECD",
                                "Americas",
                                "Asia &\nOceania",
                                "Central &\nEastern Europe",
                                "Middle East &\nnorth Africa",
                                "Sub-Saharan\nAfrica"))
#Para evitar saturación, solo elegimos mostrar el nombre de algunos paises
pointsToLabel <- c("Russia", "Venezuela", "Iraq", "Myanmar", "Sudan",
                   "Afghanistan", "Congo", "Greece", "Argentina", "Brazil",
                   "India", "Italy", "China", "South Africa", "Spain",
                   "Botswana", "Cape Verde", "Bhutan", "Rwanda", "France",
                   "United States", "Germany", "Britain", "Barbados", "Norway", "Japan",
                   "New Zealand", "Singapore")
ggplot(df,aes(x = CPI, y = HDI)) +
  geom_point(aes(color = Region),size=2.5, shape = 1, stroke = 1.25) + 
  geom_line(stat = "smooth",method = "loess", span = 1, colour = "red") +
  scale_x_continuous(name = "Corruption Perception Index, 2011 (10=least corrupt)", limits = c(1,10), breaks = c(1:10)) + 
  theme(legend.position="top", 
        legend.text.align = 0, 
        legend.justification = c("left", "top")) +
  labs(title = "Corruption and human development",
       caption = "Sources: Transparency International; UN Human Development Report") +
  geom_text_repel(aes(label=Country), size = 3, data = filter(df, Country %in% pointsToLabel)) +
  scale_y_continuous(name = "Human Development Index, 2011 (1=best)", limits = c(0.2,1.0), breaks = seq(0.2,1.0, by = 0.1)) + 
  scale_color_manual(name = " ", 
                     values = c("Americas" = "blue",
                                "Asia &\nOceania" = "cyan2",
                                "Central &\nEastern Europe" = "green",
                                "OECD" = "blue4", 
                                "Middle East &\nnorth Africa" = "orange",
                                "Sub-Saharan\nAfrica" = "brown"),
                     guide = guide_legend(nrow = 1, order = 1))