# Instalación conjunta de Tidyverse
install.packages("tidyverse")
# Instalación individual
install.packages("dplyr")
8 dplyr - gramática para manipulación de datos
8.1 Trabajo previo
8.1.1 Lecturas
Wickham, H., & Grolemund, G. (2017). R for Data Science: Import, Tidy, Transform, Visualize, and Model Data (1st ed.). O’Reilly Media. https://r4ds.had.co.nz/
8.2 Resumen
En este capítulo se estudia el paquete dplyr
de Tidyverse.
8.3 Características generales
El paquete dplyr de Tidyverse 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 manipulación de datos más comunes”. Los principales “verbos” (i.e. funciones) de esta gramática son:
select(): selecciona columnas con base en sus nombres.
filter(): selecciona filas con base en sus valores.
arrange(): cambia el orden de las filas.
mutate(): crea nuevas columnas, las cuales se expresan como funciones de columnas existentes.
summarise(): agrupa y resume valores.
Todas estas operaciones pueden combinarse con la función group_by(), la cual ejecuta cualquiera de las operaciones anteriores “en grupo”. Además, dplyr proporciona funciones adicionales para tareas más específicas.
Todas las funciones de dplyr trabajan de manera similar:
- El primer argumento es un data frame. Puede omitirse si la función recibe el data frame a través del operador pipe.
- Los argumentos siguientes describen que hacer con el data frame, utilizando los nombres de las columnas (sin comillas).
- El resultado es un nuevo data frame.
Las funciones de dplyr
pueden encadenarse a través del operador pipe (tubo), ya sea el del paquete magrittr (%>%) o el del paquete base de R (|>). Los procesos se enlazan con pipes para formar pipelines (tuberías).
8.4 Instalación y carga
El paquete dplyr
pueden instalarse junto con todos los demás paquete de Tidyverse o de manera individual:
Una vez instalado, dplyr
puede cargarse con la función library()
:
# Carga conjunta de Tidyverse
library(tidyverse)
# Carga individual
library(dplyr)
8.5 Conjuntos de datos de ejemplo
Para los ejemplos de este capítulo, se utiliza el paquete de datos palmerpenguins.
# Carga del paquete de datos palmerpenguins
library(palmerpenguins)
Estructura del conjunto de datos penguins
:
# Estructura del paquete de datos palmerpenguins
glimpse(penguins)
Rows: 344
Columns: 8
$ species <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adel…
$ island <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgerse…
$ bill_length_mm <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, …
$ bill_depth_mm <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, …
$ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186…
$ body_mass_g <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, …
$ sex <fct> male, female, female, NA, female, male, female, male…
$ year <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007…
Vista del conjunto de datos penguins
:
# Vista del paquete de datos palmerpenguins
View(penguins)
8.6 Funciones
8.6.1 select()
La función select()
selecciona (y opcionalmente renombra) columnas de un data frame con base en sus nombres.
# Selección de las columnas de especie, longitud del pico y sexo
|>
penguins select(species, bill_length_mm, sex)
# A tibble: 344 × 3
species bill_length_mm sex
<fct> <dbl> <fct>
1 Adelie 39.1 male
2 Adelie 39.5 female
3 Adelie 40.3 female
4 Adelie NA <NA>
5 Adelie 36.7 female
6 Adelie 39.3 male
7 Adelie 38.9 female
8 Adelie 39.2 male
9 Adelie 34.1 <NA>
10 Adelie 42 <NA>
# … with 334 more rows
Cambio de nombres de columnas:
# Selección y cambio de nombre de las columnas de especie, longitud del pico y sexo
|>
penguins select(especie = species,
longitud_pico_mm = bill_length_mm,
sexo = sex)
# A tibble: 344 × 3
especie longitud_pico_mm sexo
<fct> <dbl> <fct>
1 Adelie 39.1 male
2 Adelie 39.5 female
3 Adelie 40.3 female
4 Adelie NA <NA>
5 Adelie 36.7 female
6 Adelie 39.3 male
7 Adelie 38.9 female
8 Adelie 39.2 male
9 Adelie 34.1 <NA>
10 Adelie 42 <NA>
# … with 334 more rows
El operador :
permite seleccionar un rango de columnas continuas:
# Selección de las columnas en el rango de species a flipper_length_mm
|>
penguins select(species:flipper_length_mm)
# A tibble: 344 × 5
species island bill_length_mm bill_depth_mm flipper_length_mm
<fct> <fct> <dbl> <dbl> <int>
1 Adelie Torgersen 39.1 18.7 181
2 Adelie Torgersen 39.5 17.4 186
3 Adelie Torgersen 40.3 18 195
4 Adelie Torgersen NA NA NA
5 Adelie Torgersen 36.7 19.3 193
6 Adelie Torgersen 39.3 20.6 190
7 Adelie Torgersen 38.9 17.8 181
8 Adelie Torgersen 39.2 19.6 195
9 Adelie Torgersen 34.1 18.1 193
10 Adelie Torgersen 42 20.2 190
# … with 334 more rows
Selección de todas las columnas que cumplen una condición:
# Selección de las columnas numéricas
|>
penguins select(where(is.numeric))
# A tibble: 344 × 5
bill_length_mm bill_depth_mm flipper_length_mm body_mass_g year
<dbl> <dbl> <int> <int> <int>
1 39.1 18.7 181 3750 2007
2 39.5 17.4 186 3800 2007
3 40.3 18 195 3250 2007
4 NA NA NA NA 2007
5 36.7 19.3 193 3450 2007
6 39.3 20.6 190 3650 2007
7 38.9 17.8 181 3625 2007
8 39.2 19.6 195 4675 2007
9 34.1 18.1 193 3475 2007
10 42 20.2 190 4250 2007
# … with 334 more rows
8.6.2 filter()
La función filter()
retorna un subconjunto de un data frame con todas las filas que satisfacen una condición (i.e. expresión lógica).
Ejemplos de uso de expresiones y operadores lógicos:
# Filas de la especie 'Adelie' con longitud del pico mayor o igual a 45 mm
|>
penguins filter(species == 'Adelie' & bill_length_mm >= 45)
# A tibble: 3 × 8
species island bill_length_mm bill_depth_mm flipper_l…¹ body_…² sex year
<fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
1 Adelie Torgersen 46 21.5 194 4200 male 2007
2 Adelie Torgersen 45.8 18.9 197 4150 male 2008
3 Adelie Biscoe 45.6 20.3 191 4600 male 2009
# … with abbreviated variable names ¹flipper_length_mm, ²body_mass_g
# Filas de la especie 'Adelie' o 'Gentoo'
|>
penguins filter(species == 'Adelie' | species == 'Gentoo')
# A tibble: 276 × 8
species island bill_length_mm bill_depth_mm flipper_…¹ body_…² sex year
<fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
1 Adelie Torgersen 39.1 18.7 181 3750 male 2007
2 Adelie Torgersen 39.5 17.4 186 3800 fema… 2007
3 Adelie Torgersen 40.3 18 195 3250 fema… 2007
4 Adelie Torgersen NA NA NA NA <NA> 2007
5 Adelie Torgersen 36.7 19.3 193 3450 fema… 2007
6 Adelie Torgersen 39.3 20.6 190 3650 male 2007
7 Adelie Torgersen 38.9 17.8 181 3625 fema… 2007
8 Adelie Torgersen 39.2 19.6 195 4675 male 2007
9 Adelie Torgersen 34.1 18.1 193 3475 <NA> 2007
10 Adelie Torgersen 42 20.2 190 4250 <NA> 2007
# … with 266 more rows, and abbreviated variable names ¹flipper_length_mm,
# ²body_mass_g
# Filas de especies diferentes a 'Chinstrap'
|>
penguins filter(!(species == 'Chinstrap'))
# A tibble: 276 × 8
species island bill_length_mm bill_depth_mm flipper_…¹ body_…² sex year
<fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
1 Adelie Torgersen 39.1 18.7 181 3750 male 2007
2 Adelie Torgersen 39.5 17.4 186 3800 fema… 2007
3 Adelie Torgersen 40.3 18 195 3250 fema… 2007
4 Adelie Torgersen NA NA NA NA <NA> 2007
5 Adelie Torgersen 36.7 19.3 193 3450 fema… 2007
6 Adelie Torgersen 39.3 20.6 190 3650 male 2007
7 Adelie Torgersen 38.9 17.8 181 3625 fema… 2007
8 Adelie Torgersen 39.2 19.6 195 4675 male 2007
9 Adelie Torgersen 34.1 18.1 193 3475 <NA> 2007
10 Adelie Torgersen 42 20.2 190 4250 <NA> 2007
# … with 266 more rows, and abbreviated variable names ¹flipper_length_mm,
# ²body_mass_g
# Filas 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))
# A tibble: 175 × 8
species island bill_length_mm bill_depth_mm flipper_…¹ body_…² sex year
<fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
1 Adelie Torgersen 46 21.5 194 4200 male 2007
2 Adelie Dream 44.1 19.7 196 4400 male 2007
3 Adelie Torgersen 45.8 18.9 197 4150 male 2008
4 Adelie Biscoe 45.6 20.3 191 4600 male 2009
5 Adelie Torgersen 44.1 18 210 4000 male 2009
6 Gentoo Biscoe 46.1 13.2 211 4500 fema… 2007
7 Gentoo Biscoe 50 16.3 230 5700 male 2007
8 Gentoo Biscoe 48.7 14.1 210 4450 fema… 2007
9 Gentoo Biscoe 50 15.2 218 5700 male 2007
10 Gentoo Biscoe 47.6 14.5 215 5400 male 2007
# … with 165 more rows, and abbreviated variable names ¹flipper_length_mm,
# ²body_mass_g
Condiciones relacionadas con valores NA
:
# Filas con valor NA en la columna sex
|>
penguins select(species, island, sex) %>%
filter(is.na(sex))
# A tibble: 11 × 3
species island sex
<fct> <fct> <fct>
1 Adelie Torgersen <NA>
2 Adelie Torgersen <NA>
3 Adelie Torgersen <NA>
4 Adelie Torgersen <NA>
5 Adelie Torgersen <NA>
6 Adelie Dream <NA>
7 Gentoo Biscoe <NA>
8 Gentoo Biscoe <NA>
9 Gentoo Biscoe <NA>
10 Gentoo Biscoe <NA>
11 Gentoo Biscoe <NA>
La función drop_na()
remueve las filas con valores NA
en una o varias columnas.
# 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)
# A tibble: 333 × 6
species bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex
<fct> <dbl> <dbl> <int> <int> <fct>
1 Adelie 39.1 18.7 181 3750 male
2 Adelie 39.5 17.4 186 3800 female
3 Adelie 40.3 18 195 3250 female
4 Adelie 36.7 19.3 193 3450 female
5 Adelie 39.3 20.6 190 3650 male
6 Adelie 38.9 17.8 181 3625 female
7 Adelie 39.2 19.6 195 4675 male
8 Adelie 41.1 17.6 182 3200 female
9 Adelie 38.6 21.2 191 3800 male
10 Adelie 34.6 21.1 198 4400 male
# … with 323 more rows
# 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()
# A tibble: 333 × 6
species bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex
<fct> <dbl> <dbl> <int> <int> <fct>
1 Adelie 39.1 18.7 181 3750 male
2 Adelie 39.5 17.4 186 3800 female
3 Adelie 40.3 18 195 3250 female
4 Adelie 36.7 19.3 193 3450 female
5 Adelie 39.3 20.6 190 3650 male
6 Adelie 38.9 17.8 181 3625 female
7 Adelie 39.2 19.6 195 4675 male
8 Adelie 41.1 17.6 182 3200 female
9 Adelie 38.6 21.2 191 3800 male
10 Adelie 34.6 21.1 198 4400 male
# … with 323 more rows
8.6.3 arrange()
La función arrange()
cambia el orden de las filas de un data frame de acuerdo con los valores de las columnas seleccionadas.
# Ordenamiento ascendente por las columnas 'bill_lenght_mm' y 'bill_depth_mm'
|>
penguins arrange(bill_length_mm, bill_depth_mm)
# A tibble: 344 × 8
species island bill_length_mm bill_depth_mm flipper_…¹ body_…² sex year
<fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
1 Adelie Dream 32.1 15.5 188 3050 fema… 2009
2 Adelie Dream 33.1 16.1 178 2900 fema… 2008
3 Adelie Torgersen 33.5 19 190 3600 fema… 2008
4 Adelie Dream 34 17.1 185 3400 fema… 2008
5 Adelie Torgersen 34.1 18.1 193 3475 <NA> 2007
6 Adelie Torgersen 34.4 18.4 184 3325 fema… 2007
7 Adelie Biscoe 34.5 18.1 187 2900 fema… 2008
8 Adelie Torgersen 34.6 17.2 189 3200 fema… 2008
9 Adelie Torgersen 34.6 21.1 198 4400 male 2007
10 Adelie Biscoe 35 17.9 190 3450 fema… 2008
# … with 334 more rows, and abbreviated variable names ¹flipper_length_mm,
# ²body_mass_g
Por defecto, las columnas se ordenan de manera acendente. Si se desea un orden descendente, puede utilizarse la función desc()
.
# Ordenamiento descendente por las columnas 'bill_lenght_mm' y 'bill_depth_mm'
|>
penguins arrange(desc(bill_length_mm), desc(bill_depth_mm))
# A tibble: 344 × 8
species island bill_length_mm bill_depth_mm flipper_l…¹ body_…² sex year
<fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
1 Gentoo Biscoe 59.6 17 230 6050 male 2007
2 Chinstrap Dream 58 17.8 181 3700 fema… 2007
3 Gentoo Biscoe 55.9 17 228 5600 male 2009
4 Chinstrap Dream 55.8 19.8 207 4000 male 2009
5 Gentoo Biscoe 55.1 16 230 5850 male 2009
6 Gentoo Biscoe 54.3 15.7 231 5650 male 2008
7 Chinstrap Dream 54.2 20.8 201 4300 male 2008
8 Chinstrap Dream 53.5 19.9 205 4500 male 2008
9 Gentoo Biscoe 53.4 15.8 219 5500 male 2009
10 Chinstrap Dream 52.8 20 205 4550 male 2008
# … with 334 more rows, and abbreviated variable names ¹flipper_length_mm,
# ²body_mass_g
Nótese que los valores NA
se ubican al final de cualquier ordenamiento.
La función across()
aplica una función en múltiples columnas.
# Ordenamiento ascendente por las columnas que empiezan con 'bill'
|>
penguins arrange(across(starts_with('bill')))
# A tibble: 344 × 8
species island bill_length_mm bill_depth_mm flipper_…¹ body_…² sex year
<fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
1 Adelie Dream 32.1 15.5 188 3050 fema… 2009
2 Adelie Dream 33.1 16.1 178 2900 fema… 2008
3 Adelie Torgersen 33.5 19 190 3600 fema… 2008
4 Adelie Dream 34 17.1 185 3400 fema… 2008
5 Adelie Torgersen 34.1 18.1 193 3475 <NA> 2007
6 Adelie Torgersen 34.4 18.4 184 3325 fema… 2007
7 Adelie Biscoe 34.5 18.1 187 2900 fema… 2008
8 Adelie Torgersen 34.6 17.2 189 3200 fema… 2008
9 Adelie Torgersen 34.6 21.1 198 4400 male 2007
10 Adelie Biscoe 35 17.9 190 3450 fema… 2008
# … with 334 more rows, and abbreviated variable names ¹flipper_length_mm,
# ²body_mass_g
# Ordenamiento ascendente por las columnas que contienen la hilera 'lenght'
|>
penguins arrange(across(contains('length')))
# A tibble: 344 × 8
species island bill_length_mm bill_depth_mm flipper_…¹ body_…² sex year
<fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
1 Adelie Dream 32.1 15.5 188 3050 fema… 2009
2 Adelie Dream 33.1 16.1 178 2900 fema… 2008
3 Adelie Torgersen 33.5 19 190 3600 fema… 2008
4 Adelie Dream 34 17.1 185 3400 fema… 2008
5 Adelie Torgersen 34.1 18.1 193 3475 <NA> 2007
6 Adelie Torgersen 34.4 18.4 184 3325 fema… 2007
7 Adelie Biscoe 34.5 18.1 187 2900 fema… 2008
8 Adelie Torgersen 34.6 17.2 189 3200 fema… 2008
9 Adelie Torgersen 34.6 21.1 198 4400 male 2007
10 Adelie Biscoe 35 17.9 190 3450 fema… 2008
# … with 334 more rows, and abbreviated variable names ¹flipper_length_mm,
# ²body_mass_g
8.6.4 mutate()
La función mutate()
crea o modifica columnas en un data frame.
# Creación de la columna 'body_mass_kg' con el valor de 'body_mass_g' expresado en kg
|>
penguins select(species, body_mass_g) |>
mutate(body_mass_kg = body_mass_g/1000)
# A tibble: 344 × 3
species body_mass_g body_mass_kg
<fct> <int> <dbl>
1 Adelie 3750 3.75
2 Adelie 3800 3.8
3 Adelie 3250 3.25
4 Adelie NA NA
5 Adelie 3450 3.45
6 Adelie 3650 3.65
7 Adelie 3625 3.62
8 Adelie 4675 4.68
9 Adelie 3475 3.48
10 Adelie 4250 4.25
# … with 334 more rows
# Creación de la columnas 'body_mass_g_mean' (promedio de masa)
# y 'body_mass_g_normalized' (proporción 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)
# A tibble: 344 × 4
species body_mass_g body_mass_g_mean body_mass_g_normalized
<fct> <int> <dbl> <dbl>
1 Adelie 3750 4202. 0.892
2 Adelie 3800 4202. 0.904
3 Adelie 3250 4202. 0.773
4 Adelie NA 4202. NA
5 Adelie 3450 4202. 0.821
6 Adelie 3650 4202. 0.869
7 Adelie 3625 4202. 0.863
8 Adelie 4675 4202. 1.11
9 Adelie 3475 4202. 0.827
10 Adelie 4250 4202. 1.01
# … with 334 more rows
La función group_by()
agrupa una o más columnas.
# Creación de la columnas 'body_mass_g_mean_species' (promedio de masa de la especie)
# y 'body_mass_g_species_normalized' (proporción con respecto al promedio de masa 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)
# A tibble: 344 × 4
# Groups: species [3]
species body_mass_g body_mass_g_mean_species body_mass_g_species_normalized
<fct> <int> <dbl> <dbl>
1 Adelie 3750 3701. 1.01
2 Adelie 3800 3701. 1.03
3 Adelie 3250 3701. 0.878
4 Adelie NA 3701. NA
5 Adelie 3450 3701. 0.932
6 Adelie 3650 3701. 0.986
7 Adelie 3625 3701. 0.980
8 Adelie 4675 3701. 1.26
9 Adelie 3475 3701. 0.939
10 Adelie 4250 3701. 1.15
# … with 334 more rows
8.6.5 summarise()
La función summarise()
crea un nuevo data frame con una (o más filas), correspondientes a combinaciones de las columnas usadas en una agrupación. Esta función frecuentemente se usa en combinación con group_by()
. Si no hay agrupación, se retorna una sola fila que sumariza todas las observaciones de la entrada.
Sumarización sin agrupamiento:
# Creación de un data frame con las columnas sumarizadas 'body_mass_g_mean' (promedio de masa)
# y 'n' (cantidad de individuos)
|>
penguins summarise(body_mass_g_mean = mean(body_mass_g, na.rm = TRUE),
n = n())
# A tibble: 1 × 2
body_mass_g_mean n
<dbl> <int>
1 4202. 344
La función n() cuenta la cantidad de filas en un grupo.
Sumarización con agrupamiento:
# Creación de un data frame con las columnas sumarizadas de mínimo, máximo y promedio de masa,
# y cantidad de individuos para cada especie
|>
penguins group_by(species) |>
summarise(
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()
)
# A tibble: 3 × 5
species body_mass_g_min body_mass_g_max body_mass_g_mean n
<fct> <int> <int> <dbl> <int>
1 Adelie 2850 4775 3701. 152
2 Chinstrap 2700 4800 3733. 68
3 Gentoo 3950 6300 5076. 124
8.6.6 Otras
8.6.6.1 count()
Una forma alternativa (a summarise()
) de realizar un conteo es con la función count():
# Creación de un data frame con el conteo de individuos por especie
|>
penguins count(species)
# A tibble: 3 × 2
species n
<fct> <int>
1 Adelie 152
2 Chinstrap 68
3 Gentoo 124
8.7 Ejercicios
Utilice las funciones de dplyr
para responder a las siguientes preguntas sobre el conjunto de datos penguins
:
- ¿Cuántos individuos de cada sexo hay en cada especie?
- ¿Cuál es el mínimo, máximo y promedio de masa corporal (peso) por especie y sexo?
- ¿Cuántos individuos se observaron durante cada año?
- ¿Cuántos individuos de cada especie se observaron durante cada año?
- ¿Cuántos individuos de cada especie y cada sexo se observaron durante cada año?
- ¿Cuál es el promedio de masa corporal (peso) por año?
- ¿Cuál es el promedio de masa corporal (peso) por año para cada especie?
8.8 Recursos de interés
RStudio. (2017). Data transformation with dplyr::Cheat Sheet. https://github.com/rstudio/cheatsheets/blob/45c1e642468695830fd8b724587ccfe8901e2185/data-transformation.pdf