-
Notifications
You must be signed in to change notification settings - Fork 0
/
10-dplyr.qmd
527 lines (392 loc) · 17.5 KB
/
10-dplyr.qmd
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
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
---
lang: es
---
# dplyr - gramática para manipulación de datos
## Resumen
El paquete dplyr proporciona un conjunto de funciones que ayudan a solucionar las tareas de transformación de datos más comunes, entre las que están la selección de columnas, el ordenamiento de filas, el filtrado de filas, la creación o modificación de columnas y los cálculos en grupos de filas. Estas transformaciones son usualmente requeridas antes de la visualización y el modelado de los datos.
## Trabajo previo
### Lecturas
Wickham, H., Çetinkaya-Rundel, M., & Grolemund, G. (s. f.). *R for Data Science (2nd ed.) Chapter 4 - Data transformation*. Recuperado 28 de abril de 2023, de <https://r4ds.hadley.nz/data-transform.html>
## Introducción
El paquete [dplyr](https://dplyr.tidyverse.org/) de [Tidyverse](https://www.tidyverse.org/) es descrito como una gramática para la manipulación de datos, la cual proporciona un conjunto consistente de verbos que ayuda a solucionar los retos de procesamiento de datos más comunes. Los principales "verbos" (i.e. funciones) de esta gramática son:
- [select()](https://dplyr.tidyverse.org/reference/select.html): selecciona columnas con base en sus nombres.
- [filter()](https://dplyr.tidyverse.org/reference/filter.html): selecciona filas con base en sus valores.
- [arrange()](https://dplyr.tidyverse.org/reference/arrange.html): cambia el orden de las filas.
- [mutate()](https://dplyr.tidyverse.org/reference/mutate.html): crea nuevas columnas, las cuales se expresan como funciones de columnas existentes.
- [summarize()](https://dplyr.tidyverse.org/reference/summarise.html): agrupa y resume valores.
Todas estas operaciones pueden combinarse con la función [group_by()](https://dplyr.tidyverse.org/reference/group_by.html), la cual ejecuta cualquiera de las operaciones anteriores "en grupo". Además, dplyr proporciona [funciones adicionales](https://dplyr.tidyverse.org/reference/index.html) para tareas más específicas.
Las funciones de dplyr pueden encadenarse a través del operador *pipe* (tubo), ya sea el del paquete magrittr [(%\>%)](https://magrittr.tidyverse.org/reference/pipe.html) o el del paquete base de R [(\|\>)](https://michaelbarrowman.co.uk/post/the-new-base-pipe/). Los procesos se enlazan con *pipes* para formar *pipelines* (tuberías).
Todas las funciones de dplyr trabajan de manera similar:
1. El primer argumento siempre es un data frame. Puede omitirse si la función recibe el data frame a través del operador *pipe*.
2. Los argumentos siguientes describen que hacer con el data frame, utilizando los nombres de las columnas (sin comillas).\
3. El resultado siempre es un nuevo data frame.
Ya que cada función de dplyr se especializa en una sola tarea, usualmente es necesario encadenar funciones mediante *pipes* para lograr un objetivo de procesamiento de datos. Por ejemplo, el siguiente bloque de código usa tres verbos, o funciones, de dplyr para obtener la masa promedio de cada especie de pingüinos que habita en la isla Biscoe.
```{r}
#| label: ejemplo-pipes
#| eval: false
# Cálculo de la masa promedio para cada especie de pingüinos
# que habita en la isla Biscoe
penguins |>
filter(island == "Biscoe") |>
group_by(species) |>
summarize(
body_mass_g_mean = mean(body_mass_g, na.rm = TRUE)
)
```
```
A tibble: 2 × 2
species body_mass_g_mean
<fct> <dbl>
Adelie 3710.659
Gentoo 5076.016
```
## Instalación y carga
El paquete dplyr puede instalarse junto con todos los demás paquete de Tidyverse o de manera individual:
```{r}
#| label: instalacion-dplyr
#| eval: false
# Instalación conjunta de Tidyverse
install.packages("tidyverse")
# Instalación individual
install.packages("dplyr")
```
Una vez instalado, dplyr puede cargarse con la función `library()`:
```{r}
#| label: carga-dplyr
#| message: false
# Carga conjunta de Tidyverse
library(tidyverse)
# Carga individual
library(dplyr)
```
Seguidamente, se cargan algunos paquetes adicionales que se utilizan en este capítulo.
```{r}
#| label: carga-otros
#| message: false
# Carga de readr, paquete para lectura de datos
library(readr)
# Carga de tidyr, paquete para creación de datos "tidy"
library(tidyr)
```
## Conjuntos de datos para ejemplos
En los ejemplos de este capítulo, se utilizan dos conjunto de datos:
- `penguins`, contenido en el paquete [palmerpenguins](https://allisonhorst.github.io/palmerpenguins/).
- [Estadísticas de delitos cometidos en 2022](https://sitiooij.poder-judicial.go.cr/phocadownload/Estadisticas/estadsticaspoliciales2022.xlsx), disponibles en el [sitio de datos abiertos del Organismo de Investigación Judicial (OIJ)](https://sitiooij.poder-judicial.go.cr/index.php/ayuda/servicios-policiales/servicios-a-organizaciones/indice-de-transparencia-del-sector-publico-costarricense/datos-abiertos).
### penguins
Para cargar el conjunto de datos `penguins`, basta con cargar el paquete `palmerpenguins`.
```{r}
#| label: carga-penguins
#| message: false
# Carga del paquete de datos palmerpenguins
library(palmerpenguins)
```
La función [glimpse()](https://dplyr.tidyverse.org/reference/glimpse.html) despliega la estructura de un conjunto de datos, incluyendo los nombres de las columnas, sus tipos de datos y una muestra de estos:
```{r}
#| label: estructura-penguins
# Estructura del conjunto de datos penguins
glimpse(penguins)
```
Un conjunto de datos puede visualizarse al escribir su nombre en la consola de R o en un programa:
```{r vista-penguins, eval=FALSE}
#| label: vista-penguins
#| eval: true
# Despliegue de los datos de penguins
penguins
```
`penguins` es un [tibble](https://tibble.tidyverse.org/), un tipo especial de data frame que se utiliza en Tidyverse. La diferencia más importante entre un tibble y un data frame es la manera en la que se imprimen: los tibbles están diseñados para conjuntos de datos grandes, por lo que solo muestran los primeros registros y las columnas que caben en la pantalla. Un data frame regular muestra todas sus columnas y muchos más registros, lo que dificulta su visualización. En general, un data frame regular y un tibble pueden tratarse indistintamente.
### Estadísticas de delitos cometidos en 2022
Se utiliza la función [readr::read_csv()](https://readr.tidyverse.org/reference/read_delim.html) para leer un archivo CSV almacenado en el repositorio GitHub de este curso, con los datos de las estadísticas policiales proporcionados por el OIJ en formato Excel. `readr::read_csv()` es más eficiente que `read.csv()` (del paquete base de R) y tiene otras ventajas como detección automática de tipos de datos y mejor integración con otros paquetes de Tidyverse (ej. dplyr, tidyr, ggplot2).
```{r}
#| label: carga-estadisticaspoliciales2022
#| message: false
# Carga de los datos de delitos cometidos en 2022
delitos_2022 <-
read.csv(
"https://raw.githubusercontent.com/gf0604-procesamientodatosgeograficos/2023-i/main/datos/oij/estadisticas-policiales/estadisticaspoliciales2022.csv"
)
```
Estructura del conjunto de datos:
```{r}
#| label: estructura-estadisticaspoliciales2022
# Estructura de los datos de delitos cometidos en 2022
glimpse(delitos_2022)
```
Despliegue de los datos (debido a que `delitos_2022` es un data frame, pero no un tibble, se limitan las filas y columnas que se muestran):
```{r vista-penguins, eval=FALSE}
#| label: vista-estadisticaspoliciales2022
#| eval: true
# Despliegue de una muestra de los datos de delitos cometidos en 2022
delitos_2022[1:10, c("Delito", "Victima", "Provincia")]
```
En RStudio, un conjunto de datos completo puede verse más cómodamente con la función `View()`.
## Funciones
En esta sección, se describen y ejemplifican las principales funciones de dplyr.
### select()
La función [select()](https://dplyr.tidyverse.org/reference/select.html) selecciona (y opcionalmente renombra) columnas de un data frame con base en sus nombres.
```{r}
#| label: select-ejemplo-01
# Selección de las columnas species, bill_length_mm y sex
penguins |>
select(species, bill_length_mm, sex)
```
Cambio de nombres de columnas:
```{r}
#| label: select-ejemplo-02
# Selección y cambio de nombre de las columnas
# species, bill_length_mm y sex
penguins |>
select(especie = species,
longitud_pico_mm = bill_length_mm,
sexo = sex)
```
El operador `:` permite seleccionar un rango de columnas continuas:
```{r}
#| label: select-ejemplo-03
# Selección de las columnas desde species a flipper_length_mm
penguins |>
select(species:flipper_length_mm)
```
Selección de todas las columnas que cumplen una condición:
```{r}
#| label: select-ejemplo-04
# Selección de las columnas numéricas
penguins |>
select(where(is.numeric))
```
### filter()
La función [filter()](https://dplyr.tidyverse.org/reference/filter.html) retorna un subconjunto de un data frame con todas las filas que satisfacen una condición (i.e. expresión lógica).
Puede utilizar los operadores relacionales:
- `==` (igual que) **Note la diferencia con el operador de asignación: `=`**
- `!=` (diferente de)
- `>` (estrictamente mayor que), `>=` (mayor o igual que)
- `<` (estrictamente menor que), `<=` (menor o igual que)
Y los operadores lógicos:
- `&` (*AND* o Y lógico)
- `|` (*OR* u O lógico)
- `!` (*NOT* o NO lógico)
Ejemplos de uso de expresiones y operadores lógicos:
```{r}
#| label: filter-ejemplo-01
# Pingüinos de la especie 'Adelie'
# con longitud del pico mayor o igual a 45 mm
penguins |>
filter(species == 'Adelie' & bill_length_mm >= 45)
```
```{r}
#| label: filter-ejemplo-02
# Pingüinos de las especies 'Adelie' o 'Gentoo'
penguins |>
filter(species == 'Adelie' | species == 'Gentoo')
```
```{r}
#| label: filter-ejemplo-04
# Pingüinos de especies diferentes a 'Chinstrap'
penguins |>
filter(!(species == 'Chinstrap'))
```
```{r}
#| label: filter-ejemplo-05
# Homicidios cometidos en el cantón de Esparza
delitos_2022 |>
filter(SubDelito == "HOMICIDIO" & Canton == "ESPARZA") |>
select(SubDelito, Canton, Fecha, Victima, Edad, Nacionalidad)
```
```{r}
#| label: filter-ejemplo-05b
# Homicidios cometidos en el cantón de Esparza
# a personas no costarricenses
delitos_2022 |>
filter(SubDelito == "HOMICIDIO" &
Canton == "ESPARZA" & Nacionalidad != "COSTA RICA") |>
select(SubDelito, Canton, Fecha, Victima, Edad, Nacionalidad)
```
```{r}
#| label: filter-ejemplo-06
# Pingüinos con longitud del pico mayor o igual al promedio
# El argumento lógico na.rm de mean()
# indica si los valores NA ("not available")
# deben ser removidos antes del cálculo
penguins |>
filter(bill_length_mm >= mean(bill_length_mm, na.rm = TRUE))
```
Condiciones relacionadas con valores `NA` (nulos):
```{r}
#| label: filter-ejemplo-07
# Filas con valor NA en la columna sex
penguins |>
select(species, island, sex) |>
filter(is.na(sex))
```
La función [tidyr::drop_na()](https://tidyr.tidyverse.org/reference/drop_na.html) remueve las filas con valores `NA` en una o varias columnas.
```{r}
#| label: filter-ejemplo-08
# Filas con valor diferente a NA en la columna sex
penguins |>
select(species,
bill_length_mm,
bill_depth_mm,
flipper_length_mm,
body_mass_g,
sex) |>
drop_na(sex)
```
```{r}
#| label: filter-ejemplo-09
# Filas con valor diferente a NA en cualquier columna
penguins |>
select(species,
bill_length_mm,
bill_depth_mm,
flipper_length_mm,
body_mass_g,
sex) |>
drop_na()
```
### arrange()
La función [arrange()](https://dplyr.tidyverse.org/reference/arrange.html) cambia el orden de las filas de un data frame de acuerdo con los valores de las columnas seleccionadas.
```{r}
#| label: arrange-ejemplo-01
# Ordenamiento ascendente por las columnas
# bill_length_mm y bill_depth_mm
penguins |>
arrange(bill_length_mm, bill_depth_mm)
```
Por defecto, las columnas se ordenan de manera acendente. Si se desea un orden descendente, puede utilizarse la función [desc()](https://dplyr.tidyverse.org/reference/desc.html).
```{r}
#| label: arrange-ejemplo-02
# Ordenamiento descendente por las columnas
# bill_length_mm y bill_depth_mm
penguins |>
arrange(desc(bill_length_mm), desc(bill_depth_mm))
```
Nótese que los valores `NA` se ubican al final de cualquier ordenamiento.
### mutate()
La función [mutate()](https://dplyr.tidyverse.org/reference/mutate.html) crea o modifica columnas en un data frame.
```{r}
#| label: mutate-ejemplo-01
# Creación de la columna body_mass_kg,
# correspondiente al valor de body_mass_g, pero expresado en kg
penguins |>
select(species, body_mass_g) |>
mutate(body_mass_kg = body_mass_g/1000)
```
```{r}
#| label: mutate-ejemplo-02
# Creación de las columnas body_mass_g_mean (promedio de masa) y
# body_mass_g_normalized (masa normalizada con respecto al promedio)
penguins |>
select(species, body_mass_g) |>
mutate(body_mass_g_mean = mean(body_mass_g, na.rm = TRUE)) |>
mutate(body_mass_g_normalized = body_mass_g / body_mass_g_mean)
```
```{r}
#| label: mutate-ejemplo-03
# Creación de las columnas
# Fecha_Date (tipo Date), Anio, Mes y Dia (enteros)
delitos_2022 |>
select(Fecha) |>
mutate(Fecha_Date = as.Date(delitos_2022$Fecha, format="%m/%d/%Y")) |>
mutate(Anio = as.integer(format(as.Date(delitos_2022$Fecha, format="%m/%d/%Y"), "%Y"))) |>
mutate(Dia = as.integer(format(as.Date(delitos_2022$Fecha, format="%m/%d/%Y"), "%m"))) |>
mutate(Mes = as.integer(format(as.Date(delitos_2022$Fecha, format="%m/%d/%Y"), "%d"))) |>
slice_head(n = 10)
```
La función [group_by()](https://dplyr.tidyverse.org/reference/group_by.html) agrupa una o más columnas. Generalmente, esto se hace con el objetivo de rea
```{r}
#| label: mutate-ejemplo-04
# Creación de la columnas
# body_mass_g_mean_species (promedio de masa de la especie) y
# body_mass_g_species_normalized (masa normalizada con respecto al promedio de la especie)
penguins |>
select(species, body_mass_g) |>
group_by(species) |>
mutate(body_mass_g_mean_species = mean(body_mass_g, na.rm = TRUE)) |>
mutate(body_mass_g_species_normalized = body_mass_g / body_mass_g_mean_species)
```
### summarize()
La función [summarize()](https://dplyr.tidyverse.org/reference/summarise.html) se utiliza generalmente junto con la función [group_by()](https://dplyr.tidyverse.org/reference/group_by.html) para realizar cálculos en grupos de filas de un data frame. `group_by()` agrupa las filas y `summarize()` realiza los cálculos (ej. sumas, promedios) en las columnas, para cada grupo. El resultado es un nuevo data frame con una fila por grupo. Si no hay agrupación, se retorna una sola fila correspondiente a los cálculos para todo el data frame.
Ejemplos de cálculos en grupos:
```{r}
#| label: summarise-ejemplo-02
# Creación de un data frame con las columnas calculadas de
# mínimo, máximo y promedio de masa,
# y cantidad de individuos para cada especie
penguins |>
group_by(species) |>
summarize(
body_mass_g_min = min(body_mass_g, na.rm = TRUE),
body_mass_g_max = max(body_mass_g, na.rm = TRUE),
body_mass_g_mean = mean(body_mass_g, na.rm = TRUE),
n = n()
)
```
La función [n()](https://dplyr.tidyverse.org/reference/context.html) cuenta la cantidad de filas en un grupo.
```{r}
#| label: summarise-ejemplo-03
#| message: false
# Creación de un data frame con la cantidad de homicidios
# por provincia, cantón
delitos_2022 |>
filter(SubDelito == "HOMICIDIO") |>
group_by(Provincia, Canton) |>
summarize(
homicidios_2022 = n()
) |>
arrange(desc(homicidios_2022)) |>
print(n = Inf)
```
La función [print()](https://rdrr.io/r/base/print.html) se utiliza para especificar la cantidad de filas que se imprimirán (`Inf` quiere decir que se imprimirán todas).
```{r}
#| label: summarise-ejemplo-04
#| message: false
# Creación de un data frame con la cantidad de registros
# por delito y subdelito
delitos_2022 |>
group_by(Delito, SubDelito) |>
summarize(
n = n()
) |>
arrange(desc(n)) |>
print(n = Inf)
```
Ejemplo de cálculos sin agrupamiento:
```{r}
#| label: summarise-ejemplo-01
# Creación de un data frame con las columnas
# body_mass_g_mean (promedio de masa) y n (cantidad de registros)
penguins |>
summarise(body_mass_g_mean = mean(body_mass_g, na.rm = TRUE),
n = n())
```
### Otras
#### distinct()
La función [distinct()](https://dplyr.tidyverse.org/reference/distinct.html) retorna las combinaciones únicas de filas en un data frame.
```{r}
#| label: otras-ejemplo-01
# Valores distintos de la columna Victima
delitos_2022 |>
distinct(Victima)
```
#### count()
Una forma alternativa a `summarize()` para realizar un conteo es con la función [count()](https://dplyr.tidyverse.org/reference/count.html):
```{r}
#| label: otras-ejemplo-02
# Conteo de delitos por tipo de Victima
delitos_2022 |>
count(Victima)
# Expresión equivalente con summarize
delitos_2022 |>
group_by(Victima) |>
summarize(n = n())
```
## Ejercicios
Utilice las funciones de dplyr para responder a las siguientes preguntas sobre el conjunto de datos `penguins`:
1. ¿Cuántos individuos de cada sexo hay en cada especie?
2. ¿Cuál es el mínimo, máximo y promedio de masa corporal (peso) por especie y sexo?
3. ¿Cuántos individuos se observaron durante cada año?
4. ¿Cuántos individuos de cada especie se observaron durante cada año?
5. ¿Cuántos individuos de cada especie y cada sexo se observaron durante cada año?
6. ¿Cuál es el promedio de masa corporal (peso) por año?
7. ¿Cuál es el promedio de masa corporal (peso) por año para cada especie?
## Recursos de interés
RStudio. (2017). *Data transformation with dplyr::Cheat Sheet*. <https://github.com/rstudio/cheatsheets/blob/45c1e642468695830fd8b724587ccfe8901e2185/data-transformation.pdf>