-
Notifications
You must be signed in to change notification settings - Fork 2
/
09-ggplot-III.Rmd
374 lines (293 loc) · 16.2 KB
/
09-ggplot-III.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# Apariencia de gráficos
En [el capítulo de ggplot2](gráficos-con-ggplot2.html) vimos el funcionamiento básico de esta librería.
Los gráficos generados en ese capítulo son los típicos gráficos exploratorios: rápidos de hacer, pero sin mucho tiempo dedicado a los detalles.
No son gráficos que estén listos para presentar en público.
Modificar la apariencia de un gráfico no es sólo cuestión de cambiarle los colores o la fuente para que sea "lindo".
Implica pensar sobre cómo hacer que el mensaje del gráfico sea fácil de entender rápidamente.
En este capítulo, vamos a empezar con un gráfico de líneas que hicimos antes que muestra la evolución de la anomalía trimestral de la cantidad total de visitantes a parques nacionales de distintas regiones.
```{r message=FALSE}
library(dplyr)
library(ggplot2)
parques <- readr::read_csv("datos/parques_tidy.csv") %>%
group_by(mes = lubridate::month(indice_tiempo), region) %>%
mutate(total = total - mean(total)) %>%
ungroup()
parques %>%
ggplot(aes(indice_tiempo, total)) +
geom_line(aes(color = region))
```
Pero este gráfico no es algo que podamos publicar.
Los números del eje y en notación científica no son muy amigables, los títulos en los ejes son nombres de variables en minúscula y sin espacios y la escala de colores es medio fea.
Vamos a ir mejorando este gráfico para que sea mucho más presentable y fácil de leer.
::: {.alert .alert-info}
**Desafío**
Antes de seguir analizá el bloque de código anterior.
¿Podés entender todo lo que hace cada línea?
Tratá de describirlo en tus propias palabras.
:::
## Escalas
Cuando ggplot2 *mapea* distintos colores a los distintos valores de la columna `region`, lo que define qué color le corresponde a qué categoría de `región` es una **escala**.
Esto no se limita a los colores; detrás de todo *mapeo* generado por `aes()` hay una escala.
Si un gráfico no tiene una escala explícita, ggplot2 usa las escalas por defecto.
Para modificar una escala, hay que sumar una nueva capa con una función de escala.
Así como las funciones de geometrías empiezan con `geom_`, las funciones de escala comienzan con `scale_` (de escala en inglés).
Luego, sigue el tipo de apariencia que mapean (`color`, `fill`, `shape`, etc.) y en muchos casos un nombre o una característica de esa escala.
Es importante distinguir entre las escalas continuas y discretas.
Las escalas continuas mapean valores continuos como números, o fechas, mientras que las discretas, mapean valores categóricos.
En el caso de nuestro gráfico, donde los colores representan distintas regiones del país, se trata de una variable categórica, por lo que necesitamos escalas discretas.
### Escalas de colores
La escala de colores que usa ggplot2 por defecto no es de las mejores, de hecho las personas que tienen daltonismo muy posiblemente no logren diferenciar todas las líneas.
Una escala o paleta de colores usada (principalmente para valores continuos) es [**viridis**](https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html) que fue creada justamente para resolver este y otros problemas.
Otra gran familia de paletas de colores es [**ColorBrewer**](https://colorbrewer2.org/), la cual tiene variantes tanto para valores discretos como para valores continuos.
Vamos a probar la paleta "Dark2" de ColorBrewer, que es una paleta *qualitativa*.
Cómo estamos modificando el *color*, la función a usar será `scale_color_brewer()`:
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
geom_line(aes(color = region)) +
scale_color_brewer(type = "qual", palette = "Dark2")
```
::: {.alert .alert-info}
**Desafío**
A modo de prueba, cambia la paleta de colores actual por la de Viridis.
Para eso tenés que usar `scale_color_viridis_d()`.
La "d" del final viene de *discrete* y se usa para variables discretas o categorías, mientas que si los datos son continuos, se usa "c".
:::
Como las variaciones en los visitantes de Patagonia y el Litoral son mucho más grandes en magnitud que las del resto de las regiones, éstas últimas aparecen casi como una línea recta constante en cero.
Este es un problema que ninguna escala de colores puede solucionar.
Si queremos mostrar líneas de tiempo con escalas muy distintas, hay que usar paneles con escalas libres.
Esto se hace poniendo `scales = "free_y"` en `facet_wrap()` para especificar que cada panel tiene su propia escala del eje vertical.
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
geom_line(aes(color = region)) +
scale_color_brewer(type = "qual", palette = "Dark2") +
facet_wrap(~region, scales = "free_y")
```
Esto permite ver la variabilidad existente en todas las regiones independientemente de su escala vertical.
Al mismo tiempo ahora los paneles son son fácilmente comparable entre si porque el rango del eje y es distinto en cada uno.
Como cada región está en su panel propio, ahora el color está codificando información redundante.
Llegamos a un momento importante en el proceso de creación de un gráfico.
Además de agregar capas, escalas y colores, también es importante saber cuándo **sacar**.
En este caso, podemos sacar la leyenda de colores agregando `guide = "none"` a `scale_color_brewer()`.
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
geom_line(aes(color = region)) +
scale_color_brewer(type = "qual", palette = "Dark2", guide = "none") +
facet_wrap(~region, scales = "free_y")
```
Pero más radical es directamente sacar el mapeo del color.
A veces la mejor escala de colores es sin escala de colores.
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
geom_line() +
facet_wrap(~region, scales = "free_y")
```
Ahora que cada región tiene su propio panel, se pueden ver las tendencias a largo plazo fácilmente.
En casi todas las regiones la cantidad de visitantes estuvo en crecimiento desde 2008 y luego colapsó con la pandemia.
Buenos Aires y Cuyo parecen ser la excepción; ambas regiones estaban estables antes de la pandemia.
Una buena forma de resaltar la tendencia a largo plazo es agregando una recta de regresión lineal.
Vimos que una forma rápida de agregar una línea de tendencia es agregando una capa con la función `geom_smooth(method = "lm")`
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
geom_line() +
geom_smooth(method = "lm") +
facet_wrap(~region, scales = "free_y")
```
Pero esto tiene un problema.
El colapso de los visitantes en 2020 y 2021 están generando una línea de tendencia chata o incluso negativa.
¿Cómo hacer para calcular la línea de tendencia usando sólo los datos anteriores a 2020?
Lo que se puede hacer es generar una variable extra con los datos pre-pandemia y luego usar esos datos en el `geom_smooth()`:
```{r}
pre_pandemia <- filter(parques, lubridate::year(indice_tiempo) < 2020)
parques %>%
ggplot(aes(indice_tiempo, total)) +
geom_line() +
geom_smooth(data = pre_pandemia,
method = "lm") +
facet_wrap(~region, scales = "free_y")
```
Del código anterior surge algo muy importante: es posible generar capas en un gráfico usando una tabla *distinta* a la que usamos para graficar las capas anteriores.
Esto es útil principalmente para definir etiquetas o resaltar determinadas observaciones.
Estamos computando la regresión para los datos anteriores a 2020 porque los posteriores son anómalos.
Sería buena idea indicar en el gráfico dónde empiezan los datos anómalos con un recuadro grisado.
La tarea de "anotar" un gráfico con geometrías que no hacen referencia a los datos se hace con la función `annotate()`.
Toma el nombre de un geom y luego los parámetros estéticos **fuera** del `aes()`.
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
annotate("rect",
xmin = as.Date("2020-01-01"), xmax = as.Date("2021-04-01"),
ymin = -Inf, ymax = Inf,
alpha = 0.3) +
geom_line() +
geom_smooth(data = pre_pandemia,
method = "lm") +
facet_wrap(~region, scales = "free_y")
```
En este código, `annotate()` dibuja un rectángulo (el equivalente a `geom_rect()`) donde el límite izquierdo es el primero de enero de 2020, y el derecho es el primero de abril de 2021.
Los límites superiores e inferiores son -Inf y +Inf, lo que indican que tienen que cubrir todos los límites del gráfico.
Además, con `alpha = 0.3`, el rectángulo es semitransparente.
### Escala de ejes
Además de modificar escalas de colores, también se pueden modificar las escalas de posición, es decir, los ejes.
En este gráfico, los números del eje y son poco amigables; especialmente la notación científica para los paneles de litoral y de patagonia.
Entonces vamos a modificar el eje y agregando una capa con `scale_y_continuous()` (porque `total` es una variable continua) usando el argumento `labels` de esa función.
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
annotate("rect",
xmin = as.Date("2020-01-01"), xmax = as.Date("2021-04-01"),
ymin = -Inf, ymax = Inf,
alpha = 0.3) +
geom_line() +
geom_smooth(data = pre_pandemia,
method = "lm") +
facet_wrap(~region, scales = "free_y") +
scale_y_continuous(labels = scales::number_format())
```
Este código usa la función `nombre_format()` del paquete **scales**.
Este es un paquete que no hace falta instalar por separado porque viene con ggplot2 e implementa transformaciones de escala y funciones para dar formato.
La función `number_format()` devuelve una función (sí, es una función que devuelve una función) que se encarga de darle un formato bonito a los números, incluyendo redondeo de cifras significativas, separador de miles o de decimales, etc.
### Etiquetas y texto
Algo que hay que arreglar sí o sí en este gráfico antes publicarlo es el nombre de los ejes y los paneles.
Primero, cambiemos las etiquetas de los ejes agregando una nueva capa con la función `labs()`.
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
annotate("rect",
xmin = as.Date("2020-01-01"), xmax = as.Date("2021-04-01"),
ymin = -Inf, ymax = Inf,
alpha = 0.3) +
geom_line() +
geom_smooth(data = pre_pandemia,
method = "lm") +
facet_wrap(~region, scales = "free_y") +
scale_y_continuous(labels = scales::number_format()) +
labs(y = "Cantidad de visitantes",
x = NULL)
```
Este código le asigna el texto "Cantidad de visitantes" al eje vertical, y le quita la etiqueta al eje x, ya que se sobreentiende por convención que se trata del eje de tiempo.
Para cambiar el nombre de los paneles hay que primero generar un vector que asigne cada nombre "de computadora" (o sea, sin espacios ni mayúsculas ni tildes) al nombre "para personas".
La forma de hacer esto es creando un vector con nombres.
```{r}
etiquetas_regiones <- c("buenos-aires" = "Buenos Aires",
"cordoba" = "Córdoba",
"cuyo" = "Cuyo",
"litoral" = "Litoral",
"norte" = "Norte",
"patagonia" = "Patagonia")
etiquetas_regiones
```
Fijate que el contenido del vector son las regiones como queremos que se muestren en el gráfico, y luego cada elemento tiene como nombre el texto como está en los datos.
Ahora podemos usar ese vector como un "labeller".
En jerga de ggplot2, los "labellers" son funciones que "etiquetan" los paneles.
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
annotate("rect",
xmin = as.Date("2020-01-01"), xmax = as.Date("2021-04-01"),
ymin = -Inf, ymax = Inf,
alpha = 0.3) +
geom_line() +
geom_smooth(data = pre_pandemia,
method = "lm") +
facet_wrap(~region, scales = "free_y",
labeller = labeller(region = etiquetas_regiones)) +
scale_y_continuous(labels = scales::number_format()) +
labs(y = "Cantidad de visitantes",
x = NULL)
```
Si el gráfico va a circular por distintos lugares o fuera de su contexto inicial, conviene sumar algún texto que describa el gráfico y la fuente de datos.
Esto se puede lograr agregando título, subtítulo y caption (mejor conocido como epígrafe) dentro de la función `labs()`.
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
annotate("rect",
xmin = as.Date("2020-01-01"), xmax = as.Date("2021-04-01"),
ymin = -Inf, ymax = Inf,
alpha = 0.3) +
geom_line() +
geom_smooth(data = pre_pandemia,
method = "lm") +
facet_wrap(~region, scales = "free_y",
labeller = labeller(region = etiquetas_regiones)) +
scale_y_continuous(labels = scales::number_format()) +
labs(y = "Cantidad de visitantes",
x = NULL,
title = "Visitas a parques nacionales con respecto a la media mensual",
subtitle = "En casi todas las regiones, la cantidad de vicitantes aumentó antes de la pandemia",
caption = "Fuente: elaboración propia en base a datos del MINTUR.")
```
## Temas
Hasta ahora llegamos a un gráfico muy funcional.
Muestra los datos, resalta las características más importantes y tiene etiquetas legibles.
Pero es medio aburrido.
Peor, al tener los colores por defecto de ggplot2, no tiene ningún toque personal que lo haga resaltar.
Entonces, lo último que queda por hacer es cambiar la apariencia global del gráfico usando los *temas*.
ggplot2 tiene muchos *temas* disponibles y para todos los gustos.
Además hay otros paquetes que extienden las posibilidades, por ejemplo [{ggthemes}](https://github.com/jrnold/ggthemes).
Por defecto ggplot2 usa `theme_grey()`, probemos `theme_minimal()`.
```{r}
parques %>%
ggplot(aes(indice_tiempo, total)) +
annotate("rect",
xmin = as.Date("2020-01-01"), xmax = as.Date("2021-04-01"),
ymin = -Inf, ymax = Inf,
alpha = 0.3) +
geom_line() +
geom_smooth(data = pre_pandemia,
method = "lm") +
facet_wrap(~region, scales = "free_y",
labeller = labeller(region = etiquetas_regiones)) +
scale_y_continuous(labels = scales::number_format()) +
labs(y = "Cantidad de visitantes",
x = NULL,
title = "Visitas a parques nacionales con respecto a la media mensual",
subtitle = "En casi todas las regiones, la cantidad de vicitantes aumentó antes de la pandemia",
caption = "Fuente: elaboración propia en base a datos del MINTUR.") +
theme_minimal()
```
::: {.alert .alert-info}
Ahora es tu turno.
Elegí un [tema que te guste](https://es.r4ds.hadley.nz/images/visualization-themes.png){.alert-link} y probalo.
Además, si se te ocurre algún título mejor modificalo!
:::
::: {.alert .alert-success}
Junto con las funciones `theme_...()`, hay una función llamada `theme()` que permite cambiar la apariencia de cualquier elemento del gráfico.
Tiene casi infinitas opciones y si algún momento te desvelas intentando cambiar esa línea o ese borde, seguro que `theme()` tiene alguna opción para hacer eso.
:::
Finalmente, así quedó el código que genera el gráfico final.
```{r message=FALSE, warning=FALSE}
parques <- readr::read_csv("datos/parques_tidy.csv") %>%
group_by(mes = lubridate::month(indice_tiempo), region) %>%
mutate(total = total - mean(total)) %>%
ungroup()
pre_pandemia <- filter(parques, lubridate::year(indice_tiempo) < 2020)
etiquetas_regiones <- c("buenos-aires" = "Buenos Aires",
"cordoba" = "Córdoba",
"cuyo" = "Cuyo",
"litoral" = "Litoral",
"norte" = "Norte",
"patagonia" = "Patagonia")
parques %>%
ggplot(aes(indice_tiempo, total)) +
annotate("rect",
xmin = as.Date("2020-01-01"), xmax = as.Date("2021-04-01"),
ymin = -Inf, ymax = Inf,
alpha = 0.3) +
geom_line() +
geom_smooth(data = pre_pandemia,
method = "lm") +
facet_wrap(~region, scales = "free_y",
labeller = labeller(region = etiquetas_regiones)) +
scale_y_continuous(labels = scales::number_format()) +
labs(y = "Cantidad de visitantes",
x = NULL,
title = "Visitas a parques nacionales con respecto a la media mensual",
subtitle = "En casi todas las regiones, la cantidad de vicitantes aumentó antes de la pandemia",
caption = "Fuente: elaboración propia en base a datos del MINTUR.") +
theme_minimal()
```