9  dplyr - gramática para manipulación de datos

En este capítulo se introduce el paquete dplyr de Tidyverse, el cual implementa un conjunto de funciones para solucionar las operaciones más comunes de manejo de datos.

9.1 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.

9.2 Trabajo previo

9.2.1 Lecturas

Wickham, H., Çetinkaya-Rundel, M., & Grolemund, G. (s. f.). R for Data Science (2nd ed.) Chapter 4 - Data transformation. Recuperado 5 de mayo de 2024, de https://r4ds.hadley.nz/data-transform.html

9.3 Introducción

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 procesamiento 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.

summarize(): 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.

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 (|>). En el material de este curso, se prefiere la segunda opción. Los pipes se utilizan para comunicar procesos y así 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.

# 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

9.4 Instalación y carga

El paquete dplyr puede instalarse junto con todos los demás paquete de Tidyverse o de manera individual:

# 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():

# Carga conjunta de Tidyverse
library(tidyverse)

# Carga individual
library(dplyr)

Seguidamente, se cargan algunos paquetes adicionales que se utilizan en este capítulo.

# Carga de readr, paquete para lectura de datos
library(readr)

# Carga de tidyr, paquete para creación de datos "tidy"
library(tidyr)

# Carga de knitr, paquete para integrar salidas en R en documentos dinámicos
# (ej. Quarto). En este capítulo se usa para generar tablas.
library(knitr)

9.5 Conjuntos de datos para ejemplos

En los ejemplos de este capítulo, se utilizan dos conjunto de datos:

9.5.1 Pingüinos del archipiélago Palmer

Para cargar el conjunto de datos penguins, basta con cargar el paquete palmerpenguins.

# Carga del paquete de datos palmerpenguins
library(palmerpenguins)

La función glimpse() despliega la estructura de un conjunto de datos, incluyendo los nombres de las columnas, sus tipos de datos y una muestra de estos:

# Estructura del conjunto de datos penguins
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…

La función también puede llamarse mediante un pipe:

# Estructura del conjunto de datos penguins
penguins |>
  glimpse()
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…

Un conjunto de datos puede visualizarse al escribir su nombre en la consola de R o en un programa:

# Despliegue de los datos de penguins
penguins
# A tibble: 344 × 8
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 2 more variables: sex <fct>, year <int>

penguins es un tibble, 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. Note la diferencia, por ejemplo, con la forma en la que se despliega el conjunto de datos iris (observe también la diferencia entre las salidas de class(iris) y class(penguins)). A pesar de estas diferencias en el despliegue, en general, un data frame regular y un tibble pueden tratarse indistintamente.

Para generar una salida más estilizada, puede usarse la función knitr::kable(), la cual genera tablas para documentos web. En el siguiente ejemplo, se obtienen los primeros registros de penguins con la función head() y se despliegan en una tabla mediante kable().

# Despliegue de los primeros registros de penguins en una tabla kable
penguins |>
  head(n = 10) |>
  kable(format = "html")
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
Adelie Torgersen 39.1 18.7 181 3750 male 2007
Adelie Torgersen 39.5 17.4 186 3800 female 2007
Adelie Torgersen 40.3 18.0 195 3250 female 2007
Adelie Torgersen NA NA NA NA NA 2007
Adelie Torgersen 36.7 19.3 193 3450 female 2007
Adelie Torgersen 39.3 20.6 190 3650 male 2007
Adelie Torgersen 38.9 17.8 181 3625 female 2007
Adelie Torgersen 39.2 19.6 195 4675 male 2007
Adelie Torgersen 34.1 18.1 193 3475 NA 2007
Adelie Torgersen 42.0 20.2 190 4250 NA 2007

9.5.2 Delitos cometidos en Costa Rica en 2024

Se utiliza la función readr::read_csv() 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).

# Carga de los datos de delitos cometidos en 2024
delitos <- read_csv(
  "https://raw.githubusercontent.com/gf0604-procesamientodatosgeograficos/2025-i/main/datos/oij/estadisticaspoliciales2024.csv"
)

Estructura del conjunto de datos:

# Estructura de los datos de delitos
glimpse(delitos)
Rows: 40,945
Columns: 12
$ Delito       <chr> "ASALTO", "ASALTO", "ASALTO", "HURTO", "HURTO", "HURTO", …
$ SubDelito    <chr> "ARMA BLANCA", "ARMA DE FUEGO", "ARMA DE FUEGO", "CARTERI…
$ Fecha        <date> 2024-11-30, 2024-11-30, 2024-11-30, 2024-11-30, 2024-11-…
$ Hora         <chr> "21:00:00 - 23:59:59", "21:00:00 - 23:59:59", "21:00:00 -…
$ Victima      <chr> "PERSONA", "PERSONA", "VEHÍCULO", "PERSONA", "OTROS", "PE…
$ SubVíctima   <chr> "PEATÓN [PERSONA]", "PEATÓN [PERSONA]", "MOTOCICLETA/REPA…
$ Edad         <chr> "Mayor de edad", "Mayor de edad", "Mayor de edad", "Mayor…
$ Sexo         <chr> "MUJER", "MUJER", "HOMBRE", "HOMBRE", "HOMBRE", "MUJER", …
$ Nacionalidad <chr> "COSTA RICA", "COSTA RICA", "CUBA", "COSTA RICA", "COSTA …
$ Provincia    <chr> "GUANACASTE", "HEREDIA", "CARTAGO", "SAN JOSÉ", "LIMÓN", …
$ Canton       <chr> "ABANGARES", "FLORES", "CARTAGO", "SAN JOSÉ", "SIQUIRRES"…
$ Distrito     <chr> "LAS JUNTAS", "SAN JOAQUÍN", "SAN NICOLAS", "CATEDRAL", "…

Despliegue de los datos (debido a que delitos_2023 es un data frame, pero no un tibble, se limitan manualmente las filas y columnas que se muestran):

# Despliegue de los primeros registros de delitos en una tabla kable
delitos |>
  head(n = 10) |>
  kable(format = "html")
Delito SubDelito Fecha Hora Victima SubVíctima Edad Sexo Nacionalidad Provincia Canton Distrito
ASALTO ARMA BLANCA 2024-11-30 21:00:00 - 23:59:59 PERSONA PEATÓN [PERSONA] Mayor de edad MUJER COSTA RICA GUANACASTE ABANGARES LAS JUNTAS
ASALTO ARMA DE FUEGO 2024-11-30 21:00:00 - 23:59:59 PERSONA PEATÓN [PERSONA] Mayor de edad MUJER COSTA RICA HEREDIA FLORES SAN JOAQUÍN
ASALTO ARMA DE FUEGO 2024-11-30 21:00:00 - 23:59:59 VEHÍCULO MOTOCICLETA/REPARTIDOR [VEHÍCULO] Mayor de edad HOMBRE CUBA CARTAGO CARTAGO SAN NICOLAS
HURTO CARTERISTA 2024-11-30 21:00:00 - 23:59:59 PERSONA PEATÓN [PERSONA] Mayor de edad HOMBRE COSTA RICA SAN JOSÉ SAN JOSÉ CATEDRAL
HURTO OTRO O INDETERMINADO 2024-11-30 21:00:00 - 23:59:59 OTROS NO DEFINIDO [OTROS] Mayor de edad HOMBRE COSTA RICA LIMÓN SIQUIRRES SIQUIRRES
HURTO POR DESCUIDO 2024-11-30 21:00:00 - 23:59:59 PERSONA PEATÓN [PERSONA] Mayor de edad MUJER COSTA RICA PUNTARENAS MONTEVERDE MONTEVERDE
HURTO POR DESCUIDO 2024-11-30 21:00:00 - 23:59:59 OTROS EL ESTADO [OTROS] Mayor de edad MUJER COSTA RICA HEREDIA HEREDIA HEREDIA
ASALTO ARREBATO 2024-11-30 21:00:00 - 23:59:59 PERSONA PEATÓN [PERSONA] Mayor de edad HOMBRE NICARAGUA SAN JOSÉ SAN JOSÉ HOSPITAL
ROBO DE VEHÍCULO DESCUIDO 2024-11-30 21:00:00 - 23:59:59 VEHÍCULO MOTOCICLETA [VEHÍCULO] Mayor de edad HOMBRE COSTA RICA GUANACASTE ABANGARES SIERRA
ROBO DE VEHÍCULO POR CONFIANZA 2024-11-30 21:00:00 - 23:59:59 VEHÍCULO MOTOCICLETA [VEHÍCULO] Mayor de edad HOMBRE COSTA RICA GUANACASTE CAÑAS CAÑAS

9.6 Funciones

En esta sección, se describen y ejemplifican las principales funciones de dplyr.

9.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 species, bill_length_mm y sex
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>  
# ℹ 334 more rows

Cambio de nombres de columnas:

# 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)
# 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>  
# ℹ 334 more rows

El operador : permite seleccionar un rango de columnas continuas:

# Selección de las columnas desde 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
# ℹ 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
# ℹ 334 more rows

9.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).

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:

# Pingüinos 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_length_mm body_mass_g
  <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
1 Adelie  Torgersen           46            21.5               194        4200
2 Adelie  Torgersen           45.8          18.9               197        4150
3 Adelie  Biscoe              45.6          20.3               191        4600
# ℹ 2 more variables: sex <fct>, year <int>
# Pingüinos de las especies 'Adelie' o 'Gentoo'
penguins |>
  filter(species == 'Adelie' | species == 'Gentoo')
# A tibble: 276 × 8
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 266 more rows
# ℹ 2 more variables: sex <fct>, year <int>
# Pingüinos de especies diferentes a 'Chinstrap'
penguins |>
  filter(!(species == 'Chinstrap'))
# A tibble: 276 × 8
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 266 more rows
# ℹ 2 more variables: sex <fct>, year <int>
# Homicidios cometidos en el cantón de San Carlos
delitos |>
  filter(Delito == "HOMICIDIO" & Canton == "SAN CARLOS") |>
  select(Canton, SubDelito, Fecha, Edad, Nacionalidad) |>
  kable()
Canton SubDelito Fecha Edad Nacionalidad
SAN CARLOS AJUSTE DE CUENTAS/VENGANZA 2024-09-19 Mayor de edad COSTA RICA
SAN CARLOS FEMICIDIO 2024-08-27 Mayor de edad COSTA RICA
SAN CARLOS AJUSTE DE CUENTAS/VENGANZA 2024-08-25 Mayor de edad COSTA RICA
SAN CARLOS POR LA COMISIÓN DE OTRO DELITO 2024-05-11 Desconocido NICARAGUA
SAN CARLOS DISCUSIÓN/RIÑA 2024-05-08 Mayor de edad Desconocido
SAN CARLOS VIOLENCIA DOMÉSTICA 2024-04-28 Mayor de edad COSTA RICA
SAN CARLOS REPELIENDO ACTIVIDAD CRIMINAL 2024-04-14 Mayor de edad NICARAGUA
SAN CARLOS DISCUSIÓN/RIÑA 2024-04-10 Desconocido COSTA RICA
SAN CARLOS AJUSTE DE CUENTAS/VENGANZA 2024-03-26 Mayor de edad COSTA RICA
SAN CARLOS AJUSTE DE CUENTAS/VENGANZA 2024-02-24 Desconocido COSTA RICA
SAN CARLOS AJUSTE DE CUENTAS/VENGANZA 2024-02-24 Desconocido COSTA RICA
SAN CARLOS AJUSTE DE CUENTAS/VENGANZA 2024-02-13 Mayor de edad COSTA RICA
# Homicidios cometidos en el cantón de SAN CARLOS 
# a personas no costarricenses
delitos |>
  filter(Delito == "HOMICIDIO" &
           Canton == "SAN CARLOS" & Nacionalidad != "COSTA RICA") |>
  select(Canton, SubDelito, Fecha, Edad, Nacionalidad) |>
  kable()
Canton SubDelito Fecha Edad Nacionalidad
SAN CARLOS POR LA COMISIÓN DE OTRO DELITO 2024-05-11 Desconocido NICARAGUA
SAN CARLOS DISCUSIÓN/RIÑA 2024-05-08 Mayor de edad Desconocido
SAN CARLOS REPELIENDO ACTIVIDAD CRIMINAL 2024-04-14 Mayor de edad NICARAGUA
# 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))
# A tibble: 175 × 8
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           46            21.5               194        4200
 2 Adelie  Dream               44.1          19.7               196        4400
 3 Adelie  Torgersen           45.8          18.9               197        4150
 4 Adelie  Biscoe              45.6          20.3               191        4600
 5 Adelie  Torgersen           44.1          18                 210        4000
 6 Gentoo  Biscoe              46.1          13.2               211        4500
 7 Gentoo  Biscoe              50            16.3               230        5700
 8 Gentoo  Biscoe              48.7          14.1               210        4450
 9 Gentoo  Biscoe              50            15.2               218        5700
10 Gentoo  Biscoe              47.6          14.5               215        5400
# ℹ 165 more rows
# ℹ 2 more variables: sex <fct>, year <int>

Condiciones relacionadas con valores NA (nulos):

# 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 tidyr::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  
# ℹ 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  
# ℹ 323 more rows

9.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_length_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_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Dream               32.1          15.5               188        3050
 2 Adelie  Dream               33.1          16.1               178        2900
 3 Adelie  Torgersen           33.5          19                 190        3600
 4 Adelie  Dream               34            17.1               185        3400
 5 Adelie  Torgersen           34.1          18.1               193        3475
 6 Adelie  Torgersen           34.4          18.4               184        3325
 7 Adelie  Biscoe              34.5          18.1               187        2900
 8 Adelie  Torgersen           34.6          17.2               189        3200
 9 Adelie  Torgersen           34.6          21.1               198        4400
10 Adelie  Biscoe              35            17.9               190        3450
# ℹ 334 more rows
# ℹ 2 more variables: sex <fct>, year <int>

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_length_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_length_mm body_mass_g
   <fct>     <fct>           <dbl>         <dbl>             <int>       <int>
 1 Gentoo    Biscoe           59.6          17                 230        6050
 2 Chinstrap Dream            58            17.8               181        3700
 3 Gentoo    Biscoe           55.9          17                 228        5600
 4 Chinstrap Dream            55.8          19.8               207        4000
 5 Gentoo    Biscoe           55.1          16                 230        5850
 6 Gentoo    Biscoe           54.3          15.7               231        5650
 7 Chinstrap Dream            54.2          20.8               201        4300
 8 Chinstrap Dream            53.5          19.9               205        4500
 9 Gentoo    Biscoe           53.4          15.8               219        5500
10 Chinstrap Dream            52.8          20                 205        4550
# ℹ 334 more rows
# ℹ 2 more variables: sex <fct>, year <int>

Nótese que los valores NA se ubican al final de cualquier ordenamiento.

9.6.4 mutate()

La función mutate() crea o modifica columnas en un data frame.

# 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)
# 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
# ℹ 334 more rows
# 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)
# 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 
# ℹ 334 more rows
# Creación de las columnas 
# Fecha_Date (tipo Date), Anio, Mes y Dia (enteros)
delitos |>
  select(Fecha) |>
  mutate(Fecha_Date = as.Date(delitos$Fecha, format="%m/%d/%Y")) |>
  mutate(Anio = as.integer(format(as.Date(delitos$Fecha, format="%m/%d/%Y"), "%Y"))) |>  
  mutate(Dia = as.integer(format(as.Date(delitos$Fecha, format="%m/%d/%Y"), "%m"))) |>
  mutate(Mes = as.integer(format(as.Date(delitos$Fecha, format="%m/%d/%Y"), "%d"))) |>
  slice_head(n = 10)
# A tibble: 10 × 5
   Fecha      Fecha_Date  Anio   Dia   Mes
   <date>     <date>     <int> <int> <int>
 1 2024-11-30 2024-11-30  2024    11    30
 2 2024-11-30 2024-11-30  2024    11    30
 3 2024-11-30 2024-11-30  2024    11    30
 4 2024-11-30 2024-11-30  2024    11    30
 5 2024-11-30 2024-11-30  2024    11    30
 6 2024-11-30 2024-11-30  2024    11    30
 7 2024-11-30 2024-11-30  2024    11    30
 8 2024-11-30 2024-11-30  2024    11    30
 9 2024-11-30 2024-11-30  2024    11    30
10 2024-11-30 2024-11-30  2024    11    30

La función group_by() agrupa una o más columnas. Generalmente, esto se hace con el objetivo de rea

# 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)
# 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 
# ℹ 334 more rows

9.6.5 summarize()

La función summarize() se utiliza generalmente junto con la función group_by() 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:

# Mmínimo, máximo, promedio de masa y cantidad de individuos 
# para cada especie de pingüinos
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()
  )
# 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

La función n() cuenta la cantidad de filas en un grupo.

# Cantidad de homicidios por provincia y cantón
delitos |>
  filter(Delito == "HOMICIDIO") |>
  group_by(Provincia, Canton) |>
  summarize(
    homicidios = n()
  ) |>
  arrange(desc(homicidios)) |>
  kable()
Provincia Canton homicidios
SAN JOSÉ SAN JOSÉ 87
LIMÓN LIMÓN 79
PUNTARENAS PUNTARENAS 56
ALAJUELA ALAJUELA 36
SAN JOSÉ ALAJUELITA 36
LIMÓN MATINA 33
SAN JOSÉ DESAMPARADOS 26
SAN JOSÉ GOICOECHEA 25
PUNTARENAS PARRITA 22
GUANACASTE NICOYA 21
LIMÓN POCOCÍ 20
CARTAGO TURRIALBA 19
ALAJUELA SAN RAMÓN 18
HEREDIA HEREDIA 18
PUNTARENAS QUEPOS 18
CARTAGO CARTAGO 17
CARTAGO LA UNIÓN 14
GUANACASTE LIBERIA 14
LIMÓN GUÁCIMO 14
LIMÓN SIQUIRRES 14
HEREDIA SARAPIQUÍ 13
PUNTARENAS GARABITO 13
ALAJUELA SAN CARLOS 12
GUANACASTE SANTA CRUZ 12
PUNTARENAS ESPARZA 11
CARTAGO PARAÍSO 10
ALAJUELA LOS CHILES 7
GUANACASTE CARRILLO 7
GUANACASTE LA CRUZ 7
PUNTARENAS CORREDORES 7
SAN JOSÉ PÉREZ ZELEDÓN 7
PUNTARENAS OSA 6
SAN JOSÉ ESCAZÚ 6
PUNTARENAS BUENOS AIRES 5
SAN JOSÉ SANTA ANA 5
SAN JOSÉ TIBÁS 5
ALAJUELA OROTINA 4
ALAJUELA UPALA 4
GUANACASTE BAGACES 4
GUANACASTE CAÑAS 4
HEREDIA SANTO DOMINGO 4
LIMÓN TALAMANCA 4
PUNTARENAS GOLFITO 4
SAN JOSÉ CURRIDABAT 4
SAN JOSÉ MORAVIA 4
ALAJUELA ATENAS 3
ALAJUELA SARCHÍ 3
CARTAGO EL GUARCO 3
CARTAGO OREAMUNO 3
GUANACASTE ABANGARES 3
SAN JOSÉ ASERRÍ 3
SAN JOSÉ VÁSQUEZ DE CORONADO 3
ALAJUELA GRECIA 2
ALAJUELA PALMARES 2
HEREDIA SAN RAFAEL 2
SAN JOSÉ DESCONOCIDO 2
SAN JOSÉ MONTES DE OCA 2
SAN JOSÉ MORA 2
SAN JOSÉ PURISCAL 2
ALAJUELA POÁS 1
ALAJUELA RÍO CUARTO 1
ALAJUELA SAN MATEO 1
CARTAGO ALVARADO 1
GUANACASTE TILARÁN 1
HEREDIA BELÉN 1
HEREDIA SAN PABLO 1
PUNTARENAS COTO BRUS 1
PUNTARENAS PUERTO JIMÉNEZ 1
SAN JOSÉ LEÓN CORTÉS 1
SAN JOSÉ TURRUBARES 1
# Cantidad de registros por delito y subdelito
delitos |>
  group_by(Delito, SubDelito) |>
  summarize(
    n = n()
  ) |>
  arrange(desc(n)) |>
  kable()
Delito SubDelito n
HURTO POR DESCUIDO 7434
ROBO FORZADURA 4421
ASALTO ARMA DE FUEGO 4077
TACHA DE VEHÍCULO TACHA DE VEHÍCULO 3316
ROBO DE VEHÍCULO DESCUIDO 2566
HURTO CARTERISTA 2496
ASALTO ARMA BLANCA 2391
HURTO POR CONFIANZA 2151
ASALTO ARREBATO 1547
ROBO BOQUETE 1061
ROBO ESCALAMIENTO 1015
ASALTO GOLPES 989
ROBO DE VEHÍCULO ASALTO 988
ROBO DE VEHÍCULO COCHERAZO 657
HOMICIDIO AJUSTE DE CUENTAS/VENGANZA 580
ASALTO INTIMIDACIÓN VERBAL 551
ROBO CORTA CANDADOS 541
HURTO ARDID PREVIO/DISTRACCIÓN 527
ROBO RUPTURA VENTANA 515
HURTO CON LLAVE 378
ROBO QUITAN CELOSÍAS 345
ROBO DE VEHÍCULO POR CONFIANZA 324
ASALTO CANDADO CHINO 315
HURTO OTRO O INDETERMINADO 289
ASALTO INMOVILIZACIÓN 282
ROBO OTRO O INDETERMINADO 207
HURTO PROGRESIVOS 152
ASALTO ARMA CONTUNDENTE 133
HURTO RETIRO DE CAJERO AUTOMÁTICO 120
ASALTO OTRO O INDETERMINADO 109
HOMICIDIO DISCUSIÓN/RIÑA 104
HURTO USO DE SOMNÍFERO 69
ROBO DE VEHÍCULO OTRO O INDETERMINADO 58
HOMICIDIO POR LA COMISIÓN DE OTRO DELITO 51
ROBO DE VEHÍCULO ARDID PREVIO 49
HURTO GANZÚA/VARILLA 42
ASALTO USO DE GAS 18
HOMICIDIO VIOLENCIA DOMÉSTICA 18
HOMICIDIO FEMICIDIO 14
HOMICIDIO REPELIENDO ACTIVIDAD CRIMINAL 13
HOMICIDIO OTRO O INDETERMINADO 9
HOMICIDIO NO DETERMINADO 8
ASALTO DESCONOCIDO 5
HOMICIDIO PROFESIONAL 5
HURTO DESCONOCIDO 2
ROBO DE VEHÍCULO DESCONOCIDO 2
ROBO DESCONOCIDO 1

Ejemplo de cálculos sin agrupamiento:

# Promedio de masa y n cantidad de registros de pingüinos
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

9.6.6 Otras

9.6.6.1 distinct()

La función distinct() retorna las combinaciones únicas de filas en un data frame.

# Valores distintos de la columna Victima

delitos |>
  distinct(Victima) |>
  kable()
Victima
PERSONA
VEHÍCULO
OTROS
VIVIENDA
EDIFICACIÓN

9.6.6.2 count()

Una forma alternativa a summarize() para realizar un conteo es con la función count():

# Conteo de delitos por tipo de Victima
delitos |>
  count(Victima)
# A tibble: 5 × 2
  Victima         n
  <chr>       <int>
1 EDIFICACIÓN  5263
2 OTROS        1759
3 PERSONA     16501
4 VEHÍCULO    10073
5 VIVIENDA     7349
# Expresión equivalente con summarize
delitos |>
  group_by(Victima) |>
  summarize(n = n()) |>
  kable()
Victima n
EDIFICACIÓN 5263
OTROS 1759
PERSONA 16501
VEHÍCULO 10073
VIVIENDA 7349

9.7 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?

9.8 Recursos de interés

RStudio. (2017). Data transformation with dplyr::Cheat Sheet. https://github.com/rstudio/cheatsheets/blob/45c1e642468695830fd8b724587ccfe8901e2185/data-transformation.pdf