Taller IFIBYNE (M3.A)¶
Lo que sige es el .rmd de la guía.
---
title: "[ATR] Taller IFIBYNE (M3.A)" author: "Nicolás Méndez" date: "2019/8/13"
output:
pdf_document:
toc: true
toc_depth: 3
number_sections: true
urlcolor: blue
Esta guía fue hecha en Rstudio con Rmarkdown.
Tip del módulo: Ctrl + Shift + C (atajo para comentar y descomentar una o varias líneas).
# Opciones globales knitr::opts_chunk\$set(echo = TRUE) knitr::opts_chunk\$set(out.width='100%', dpi=300) knitr::opts_chunk\$set(fig.align='center', dev = "png", results = "hold") options(max.print=100) library("gridExtra")
Gráficos usando ggplot2¶
La idea del módulo 3 es que salgan haciendo gráficos con ggplot2, un paquete del mundillo tidy.
En esta primera parte les propongo aprender sobre la forma general de escribir gráficos en ggplot.
En la parte B y C hay ejemplos y ejercicios para los tipos de gráficos más comunes.
Preparar entorno¶
- Cargar la librería de ggplot. 2. Explorar el data frame "mtcars", un dataset de ejemplo, incluído en el entorno de R por defecto.
library(tidyverse)
# Incluye ggplot2
# Usaremos datos de ejemplo tablas precargadas: head(mtcars)
mtcars
es una tabla de eficiencias de motores en Millas Por Galon.
Usaremos dos de sus variables: cyl
(cantidad de cilindros) y mpg
(eficiencia).
Para ver otros, ejecutar: library(help = "datasets")
.
Flashback¶
-
¿Qué columnas tiene el data frame
mtcars
?Podés usar:
colnames()
names()
View()
-
¿Qué tipo de datos contiene cada columna?
str(mtcars)
lapply(mtcars, class)
glimpse(mtcars)
-
Resumir la variable
mpg
agrupando porcyl
usando la media:En base R es:
aggregate(mpg ~ cyl, mtcars, FUN = mean)
O en tidycosas:
mtcars %>% group_by(cyl) %>% summarise(mpg = mean(mpg))
Anatomía de ggplot¶
Conceptualmente, hay tres partes que deben estar para poder graficar:
- La geometría del gráfico. 2. Los datos que queremos graficar. 3. Mateadatos sobre cómo queremos representar los datos en ese gráfico.
Ahora vamos a ver cómo entregar toda esta información a ggplot
.
La Base¶
La función ggplot()
es la base, todos los gráficos hechos con esta librería empiezan llamando a esa función.
# ggplot() es la base del gráfico ggplot()
Ahora especificamos el primer ítem de la lista anterior: la geometría del gráfico.
# Las capas de geometría y otras opciones se añaden con "+"
# Para armar un gráfico de dispersión, añadiríamos geom_point() ggplot() + geom_point()
# Un gráfico de dos capas se vería así
ggplot() +
geom_point() + # Una capa de dispersión
geom_contour() # Una capa de curvas de contorno
Esta es la estructura fundamental de cualquier gráfico hecho con ggplot.
Entonces, si quisiéramos graficar puntos y una curva de regresión por encima, deberíamos escribir algo así:
# Primero la base ggplot() +
geom_point() + # Luego la capa de dispersión
geom_smooth() # Y por encima la capa de la curva
¿Qué otras geometrías hay?¶
-
Escribir
geom_
en la consola de Rstudio y explorar las opciones que aparecen para autocompletar.La referencia para cada geometría y función de ggplot2 está acá. Además hay varios sitios para buscar tipos de gráficos en R, como esta colección.
-
En esta guía vamos a usar principalmente
geom_point
ygeom_line
.
El Contenido¶
ggplot()
y las geometrías como geom_point()
, toman dos argumentos necesarios:
- data: una tabla de datos limpios y bien estructurados (datos "largos", de
gather()
). - mapping: la relación entre datos y geometría (llamados aesthetics, o
aes
).
Estas dos partes son las que nos faltaban, y ya casi podemos armar un gráfico simple.
data
¶
Los datos se pasan al argumento data
de ggplot:
```{r, eval = FALSE} ggplot(data = mtcars) + # Base del gráfico
geom_point() # Primera y única capa
Pero como todavía no le dijimos como usar esos datos, ggplot devuelve un error, pidiendo "aesthetics".
#### `aes()`
La función `aes()` se usa para pasar la relación entre los datos y la geometría al argumento `mapping` de ggplot.
Esta función nos ayuda a decirle a ggplot cúales de las variables en nuestra tabla queremos usar en los ejes y formas o colores de los elementos del gráfico.
En la geometría de dispersón, `geom_point`, debemos pasar una variable al parámetro `x` y otra al parámetro `y` dentro de `aes()`.
```{r, out.width = '40%', fig.width=3, fig.height=3}
# Un gráfico de dispersión
ggplot(data = mtcars, mapping = aes(x = cyl, y = mpg)) +
geom_point()
Deconstruir el código¶
Identificar las partes en el ejemplo anterior
data = mtcars
- Los datos están en el data frame
mtcars
.
mapping = aes(x = cyl, y = mpg)
- La columna
cyl
va en el eje horizontal (x) - La columna
mpg
en el eje vertical (y)
geom_point()
- Hacer un gráfico de dispersión.
- Como no dimos argumentos para esta geometría, hereda por defecto la
data
y elmapping
deggplot()
.
Bonus: otra forma de escribir lo mismo¶
mapping
y data
pueden especificarse en ggplot
y/o en una geometría como geom_point()
.
En este caso ayuda leer ?ggplot. Según la documentación, la idea es que cada geom tenga la flexibilidad de usar data y/o mapping diferentes a los que pusimos en ggplot()
al principio, o en otros geoms.
Lo siguente es equivalente al ejemplo anterior.
```{r, eval = FALSE} # Data y mapping se pueden pasar a cada geom por separado ggplot() +
geom_point(data = mtcars, mapping = aes(x = cyl, y = mpg))
Y la forma más prolija de escribir este gráfico es la siguiente:
```{r, eval = FALSE} # Si los parámetros están en orden, no es necesario nombrarlos ggplot(mtcars, aes(cyl, mpg)) +
geom_point()
Los "adornos"¶
Hay varias opciones que afectan diferentes elementos de la visualización.
-
Todo lo relevante al título del gráfico o de los ejes, la leyenda, etc.
(ejemplos acá). 2. Los límites, escalas y transformaciones de los ejes (log, sqrt, etc.) se pueden cambiar después de definir la geometría
(ver ejemplos).
-
Las opciones que afectan el aspecto (colores, tamaños, estilos de línea, puntos o texto) se pueden explorar ejecutando
browseVignettes("ggplot2")
.En muchos casos, estos elemntos del gráfico pueden asociarse a una variable en
aes()
.Si incluyéramos
aes(color = mpg)
en el llamado ageom_point()
, los puntos de esa geometría se colorearían según el valor dempg
.La escala de colores que se usaría se puede cambiar. Por ejemplo, añadiendo
scale_color_viridis_c()
en el siguiente ejemplo.Prueben ejecutarlo para ver qué sale :)
```{r, eval = FALSE} # Escala de colores por defecto ggplot(mtcars, aes(cyl, mpg)) +
geom_point(aes(color = mpg)) # mapeamos el valor de "mpg" al color del punto
Escala de colores tipo "viridis" ggplot(mtcars, aes(cyl, mpg)) +¶
geom_point(aes(color = mpg)) +
scale_color_viridis_c()
### Ejercicio 1
En este ejemplo hay varias líneas comentadas a ignorar (por ahora).
#### Consignas
* Ejecutar el código como está y observar el resultado. * Identificar las 3 partes principales de cada plot: **data**, **aes** y **geom**. * ¿Funcionó bien `geom_line()` en el gráfico del medio?
¿Y en el gráfico a la derecha? ¿Qué cambió?
```{r capas1, eval=FALSE}
# Izquierda: Un gráfico de dispersión
ggplot(data = mtcars, mapping = aes(cyl, mpg)) + # Base del gráfico
geom_point() # Primera y única capa
# Centro: Un gráfico de 2 capas
ggplot(data = mtcars, mapping = aes(cyl, mpg)) +
#geom_bar(aes(group = cyl), stat = "summary", alpha = .5) +
geom_point() +
geom_line() +
xlab("# Cilindros") # Así se modifica el axis label
# Derecha: el mismo gráfico, con otra estadística para
geom_line() ggplot(mtcars, aes(cyl, mpg)) +
#geom_boxplot(aes(group = cyl), fill = "#00000000") +
geom_point() +
#stat_summary(geom = "line", fun.y = "mean") +
geom_line(stat = "summary", fun.y = "mean")
Resultado esperado¶
p0 <- ggplot(data = mtcars, mapping = aes(cyl, mpg)) +
geom_point()
p1 <- ggplot(data = mtcars, mapping = aes(cyl, mpg)) +
geom_point() +
geom_line() +
xlab("# Cilindros")
p2 <- ggplot(mtcars, aes(cyl, mpg)) +
geom_point() +
geom_line(stat = "summary", fun.y = "mean")
# <https://stackoverflow.com/questions/1249548/side-by-side-plots-with-ggplot2> grid.arrange(p0, p1, p2, ncol = 3)
Ejercicio 2¶
Jugar un poco con el ejemplo anterior.
Consignas¶
-
Comentar o descomentar las capas de a una y ver qué pasa.
Quitar o agregar el
#
, manualmente o bien con el atajo: Ctrl + Shift + C. -
Cambiar el orden de las capas.
¿Cual es la capa superior? ¿La primera o la última en el código?
-
Modificar el color de los elementos en el gráfico:
-
Colorear las líneas con rojo escribiendo
color = "red"
dentro degeom_line()
. -
Agregar un aes a
geom_point()
para colorear los puntos según la cantidad de cilindros:geom_point(aes(color = cyl))
- Agregar un aes a
geom_point()
para dar forma a los puntos según la cantidad de cilindros:
geom_point(aes(shape = cyl))
¿Con qué error nos encontramos?
- Agregar un aes a
Resultado Si hicieron el último punto, se encontraron con esto:¶
"Error: A continuous variable can not be mapped to shape"
-
¿Que tipo de escala es
shape
? -
¿Que tipo de variable era
cyl
?
El error aparece cuando pedimos que una variable contínua sea traducida a una escala únicamente discreta.
Arreglemos eso discretizando la cantidad de cilindros poniendo a cyl
dentro de factor()
. Voilà!
ggplot(mtcars, aes(factor(cyl), mpg)) + # Ahora 5 y 7 no se incluyen en el eje x.
geom_point(aes(shape = factor(cyl))) # Arreglamos el error y apareció una leyenda.
ggplot(mtcars, aes(factor(cyl), mpg)) + # Ahora 5 y 7 no se incluyen en el eje x.
geom_point(aes(shape = factor(cyl))) + # Arreglamos el error y apareció una leyenda.
theme_minimal()
Buenas prácticas¶
Sabemos que la cantidad de cilindros en un motor no es una variable contínua, así que deberíamos haberla tratado de forma discreta desde el principio.
Para eso era necesario hacer la conversión al inicio del script: mtcars <- mutate(mtcars, cyl = factor(cyl))
De esta forma, cyl
es un factor en el data frame, y no es necesario aclararlo en ggplot()
.
mtcars2 <- mutate(mtcars, cyl = factor(cyl))
ggplot(mtcars2, aes(cyl, mpg)) + # Ahora 5 y 7 no se incluyen en el eje x.
geom_point(aes(shape = factor(cyl))) # Ya no se produce el error.
Además, apareció una leyenda para las escalas no representadas en los ejes del gráfico.
Bonus: discretizar cyl también afecta la escala de color¶
El efecto de convertir cyl
a factor en aes(color = cyl)
es que la escala de color también cambia.
Pasa de una escala de color contínua (que por defecto va de negro a azul) a una escala de color discreta (y linda) que permite diferenciar claramente las categorías.
p6 <- ggplot(mtcars, aes(cyl, mpg)) +
geom_point(aes(color = cyl), show.legend = F) +
ggtitle("Sin factor(cyl)")
p7 <- ggplot(mtcars, aes(cyl, mpg)) +
geom_point(aes(color = factor(cyl)), show.legend = F) +
ggtitle("factor(cyl)\nsolo en aes(color)")
p8 <- ggplot(mtcars2, aes(cyl, mpg)) +
geom_point(aes(color = cyl), show.legend = F) +
ggtitle("factor(cyl)\n en dataframe")
library(gridExtra) grid.arrange(p6, p7, p8, ncol = 3)
La escala de color puede ser discreta o contínua, y en ese caso ggplot se adapta al tipo de datos la variable.
R y confusión: ¿el código que viste en otro lado es diferente pero hace lo mismo?¶
Si. En R suele haber varias formas de hacer exactamente lo mismo.
Algunos "criterios" para elegir como escribir el ggplot (o cualquier cosa):
- Claridad (que puedas entender más adelante)
- Elegancia (escribir poco)
- Eficiencia (que ande, y en lo posible rápido)
- Consistencia (hacerlo siempre de la misma manera)
# Poner los elementos compartidos en ggplot() es más corto que escribirlos siempre por separado
# Más elegante ggplot(mtcars, aes(cyl, mpg)) +
geom_point() +
geom_line(stat = "summary", fun.y = "mean")
# No elegante ggplot() +
geom_point(data = mtcars, aes(cyl, mpg)) +
geom_line(data = mtcars, aes(cyl, mpg), stat = "summary", fun.y = "mean")
# No consistente ggplot(mtcars, aes(cyl, mpg)) +
geom_point() +
stat_summary(fun.y = "mean", geom = "line")
p3 <- ggplot(data = mtcars, mapping = aes(cyl, mpg)) +
geom_point() +
geom_line(stat = "summary", fun.y = "mean")
p4 <- ggplot() +
geom_point(data = mtcars, aes(cyl, mpg)) +
geom_line(data = mtcars, aes(cyl, mpg), stat = "summary", fun.y = "mean")
p5 <- ggplot(mtcars, aes(cyl, mpg)) +
geom_point() +
stat_summary(geom = "line", fun.y = "mean")
grid.arrange(p3, p4, p5, ncol = 3)
Bonus: aclaraciones sobre stat_summary
¶
En el último gráfico, stat_summary
reemplaza a geom_point
.
Detrás de escena, cada geom
tiene por defecto una transformación estadística (o stat
) que se aplica sobre los datos.
En el caso de geom_line
es "identity"
, que no tiene efecto. Por lo tanto todos los puntos aparecían conectados.
Al especificar geom_line(stat = "summary", fun.y = "mean")
, cambiamos la transformación para obtener la media por cada grupo.
También es posible hacer el camino inverso, llamando a la transforamción estadística stat_summary
primero, y especificar la geometría dentro: stat_summary(fun.y = "mean", geom = "line")
.