9  dplyr - gramática para manipulación 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 2023

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 2023
delitos_2023 <-
  read_csv(
    "https://raw.githubusercontent.com/gf0604-procesamientodatosgeograficos/2024-i/main/datos/oij/estadisticas-policiales/estadisticaspoliciales2023.csv"
  )

Estructura del conjunto de datos:

# Estructura de los datos de delitos cometidos en 2023
glimpse(delitos_2023)
Rows: 49,609
Columns: 12
$ Delito       <chr> "ASALTO", "ASALTO", "HURTO", "ASALTO", "ASALTO", "HURTO",…
$ SubDelito    <chr> "ARMA BLANCA", "ARMA BLANCA", "ARDID PREVIO/DISTRACCION",…
$ Fecha        <chr> "12/31/2023", "12/31/2023", "12/31/2023", "12/31/2023", "…
$ Hora         <chr> "21:00:00 - 23:59:59", "21:00:00 - 23:59:59", "21:00:00 -…
$ Victima      <chr> "PERSONA", "PERSONA", "PERSONA", "PERSONA", "PERSONA", "V…
$ SubVictima   <chr> "PEATON [PERSONA]", "PEATON [PERSONA]", "TAXISTA [PERSONA…
$ Edad         <chr> "Mayor de edad", "Mayor de edad", "Mayor de edad", "Mayor…
$ Genero       <chr> "HOMBRE", "MUJER", "HOMBRE", "HOMBRE", "HOMBRE", "MUJER",…
$ Nacionalidad <chr> "COSTA RICA", "COSTA RICA", "COSTA RICA", "NICARAGUA", "C…
$ Provincia    <chr> "SAN JOSE", "SAN JOSE", "SAN JOSE", "SAN JOSE", "SAN JOSE…
$ Canton       <chr> "SAN JOSE", "MORAVIA", "DESAMPARADOS", "SAN JOSE", "SAN J…
$ Distrito     <chr> "MERCED", "SAN VICENTE", "SAN MIGUEL", "HOSPITAL", "HOSPI…

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_2023 |>
  head(n = 10) |>
  kable(format = "html")
Delito SubDelito Fecha Hora Victima SubVictima Edad Genero Nacionalidad Provincia Canton Distrito
ASALTO ARMA BLANCA 12/31/2023 21:00:00 - 23:59:59 PERSONA PEATON [PERSONA] Mayor de edad HOMBRE COSTA RICA SAN JOSE SAN JOSE MERCED
ASALTO ARMA BLANCA 12/31/2023 21:00:00 - 23:59:59 PERSONA PEATON [PERSONA] Mayor de edad MUJER COSTA RICA SAN JOSE MORAVIA SAN VICENTE
HURTO ARDID PREVIO/DISTRACCION 12/31/2023 21:00:00 - 23:59:59 PERSONA TAXISTA [PERSONA] Mayor de edad HOMBRE COSTA RICA SAN JOSE DESAMPARADOS SAN MIGUEL
ASALTO INMOVILIZACION 12/31/2023 21:00:00 - 23:59:59 PERSONA PEATON [PERSONA] Mayor de edad HOMBRE NICARAGUA SAN JOSE SAN JOSE HOSPITAL
ASALTO ARMA DE FUEGO 12/31/2023 21:00:00 - 23:59:59 PERSONA PEATON [PERSONA] Mayor de edad HOMBRE COSTA RICA SAN JOSE SAN JOSE HOSPITAL
HURTO POR DESCUIDO 12/31/2023 21:00:00 - 23:59:59 VIVIENDA NO APLICA [VIVIENDA] Mayor de edad MUJER COSTA RICA SAN JOSE DOTA JARDIN
ASALTO ARREBATO 12/31/2023 21:00:00 - 23:59:59 PERSONA PASAJERO DE TRANSPORTE PUBLICO [PERSONA] Mayor de edad MUJER COSTA RICA SAN JOSE SAN JOSE DESCONOCIDO
ASALTO GOLPES 12/31/2023 21:00:00 - 23:59:59 PERSONA PEATON [PERSONA] Mayor de edad HOMBRE COSTA RICA PUNTARENAS CORREDORES CORREDOR
ASALTO INMOVILIZACION 12/31/2023 21:00:00 - 23:59:59 PERSONA PEATON [PERSONA] Mayor de edad HOMBRE ITALIA SAN JOSE SAN JOSE HATILLO
HURTO CON LLAVE 12/31/2023 21:00:00 - 23:59:59 VIVIENDA NO APLICA [VIVIENDA] Adulto Mayor MUJER COSTA RICA GUANACASTE NICOYA NICOYA

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 Sarapiquí
delitos_2023 |>
  filter(Delito == "HOMICIDIO" & Canton == "SARAPIQUI") |>
  select(Canton, SubDelito, Fecha, Edad, Nacionalidad) |>
  kable()
Canton SubDelito Fecha Edad Nacionalidad
SARAPIQUI DISCUSION/RIÑA 12/06/2023 Desconocido COSTA RICA
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 10/28/2023 Desconocido Desconocido
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 10/16/2023 Mayor de edad COSTA RICA
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 09/30/2023 Mayor de edad COSTA RICA
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 09/28/2023 Desconocido Desconocido
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 07/28/2023 Desconocido Desconocido
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 07/07/2023 Desconocido Desconocido
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 07/05/2023 Mayor de edad COSTA RICA
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 06/25/2023 Desconocido Desconocido
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 05/31/2023 Mayor de edad COSTA RICA
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 05/22/2023 Desconocido COSTA RICA
SARAPIQUI DISCUSION/RIÑA 05/16/2023 Mayor de edad COSTA RICA
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 04/12/2023 Mayor de edad COSTA RICA
SARAPIQUI POR LA COMISION DE OTRO DELITO 03/18/2023 Mayor de edad NICARAGUA
SARAPIQUI DISCUSION/RIÑA 03/13/2023 Mayor de edad COSTA RICA
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 02/16/2023 Mayor de edad Desconocido
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 01/31/2023 Adulto Mayor COSTA RICA
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 01/31/2023 Mayor de edad COSTA RICA
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 01/07/2023 Mayor de edad COSTA RICA
# Homicidios cometidos en el cantón de Sarapiquí 
# a personas no costarricenses
delitos_2023 |>
  filter(Delito == "HOMICIDIO" &
           Canton == "SARAPIQUI" & Nacionalidad != "COSTA RICA") |>
  select(Canton, SubDelito, Fecha, Edad, Nacionalidad) |>
  kable()
Canton SubDelito Fecha Edad Nacionalidad
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 10/28/2023 Desconocido Desconocido
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 09/28/2023 Desconocido Desconocido
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 07/28/2023 Desconocido Desconocido
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 07/07/2023 Desconocido Desconocido
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 06/25/2023 Desconocido Desconocido
SARAPIQUI POR LA COMISION DE OTRO DELITO 03/18/2023 Mayor de edad NICARAGUA
SARAPIQUI AJUSTE DE CUENTAS/VENGANZA 02/16/2023 Mayor de edad Desconocido
# 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_2023 |>
  select(Fecha) |>
  mutate(Fecha_Date = as.Date(delitos_2023$Fecha, format="%m/%d/%Y")) |>
  mutate(Anio = as.integer(format(as.Date(delitos_2023$Fecha, format="%m/%d/%Y"), "%Y"))) |>  
  mutate(Dia = as.integer(format(as.Date(delitos_2023$Fecha, format="%m/%d/%Y"), "%m"))) |>
  mutate(Mes = as.integer(format(as.Date(delitos_2023$Fecha, format="%m/%d/%Y"), "%d"))) |>
  slice_head(n = 10)
# A tibble: 10 × 5
   Fecha      Fecha_Date  Anio   Dia   Mes
   <chr>      <date>     <int> <int> <int>
 1 12/31/2023 2023-12-31  2023    12    31
 2 12/31/2023 2023-12-31  2023    12    31
 3 12/31/2023 2023-12-31  2023    12    31
 4 12/31/2023 2023-12-31  2023    12    31
 5 12/31/2023 2023-12-31  2023    12    31
 6 12/31/2023 2023-12-31  2023    12    31
 7 12/31/2023 2023-12-31  2023    12    31
 8 12/31/2023 2023-12-31  2023    12    31
 9 12/31/2023 2023-12-31  2023    12    31
10 12/31/2023 2023-12-31  2023    12    31

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_2023 |>
  filter(Delito == "HOMICIDIO") |>
  group_by(Provincia, Canton) |>
  summarize(
    homicidios_2023 = n()
  ) |>
  arrange(desc(homicidios_2023)) |>
  kable()
Provincia Canton homicidios_2023
LIMON LIMON 91
SAN JOSE SAN JOSE 82
PUNTARENAS PUNTARENAS 68
LIMON MATINA 50
ALAJUELA ALAJUELA 44
LIMON POCOCI 31
GUANACASTE NICOYA 28
SAN JOSE DESAMPARADOS 26
GUANACASTE SANTA CRUZ 24
SAN JOSE ALAJUELITA 23
CARTAGO CARTAGO 21
LIMON SIQUIRRES 21
SAN JOSE GOICOECHEA 20
ALAJUELA SAN CARLOS 19
HEREDIA SARAPIQUI 19
CARTAGO PARAISO 17
HEREDIA HEREDIA 17
PUNTARENAS PARRITA 17
GUANACASTE LIBERIA 16
PUNTARENAS CORREDORES 16
CARTAGO LA UNION 15
PUNTARENAS QUEPOS 15
ALAJUELA SAN RAMON 13
PUNTARENAS GARABITO 13
SAN JOSE PEREZ ZELEDON 13
GUANACASTE CARRILLO 11
CARTAGO TURRIALBA 10
LIMON GUACIMO 10
LIMON TALAMANCA 10
SAN JOSE ESCAZU 8
SAN JOSE ASERRI 7
SAN JOSE TIBAS 7
SAN JOSE VASQUEZ DE CORONADO 7
ALAJUELA LOS CHILES 6
GUANACASTE CANAS 6
SAN JOSE CURRIDABAT 6
SAN JOSE MONTES DE OCA 6
GUANACASTE ABANGARES 5
PUNTARENAS ESPARZA 5
PUNTARENAS GOLFITO 5
PUNTARENAS OSA 5
ALAJUELA GRECIA 4
CARTAGO EL GUARCO 4
GUANACASTE LA CRUZ 4
HEREDIA SAN RAFAEL 4
ALAJUELA ATENAS 3
ALAJUELA GUATUSO 3
ALAJUELA NARANJO 3
ALAJUELA OROTINA 3
CARTAGO OREAMUNO 3
GUANACASTE BAGACES 3
HEREDIA SANTO DOMINGO 3
PUNTARENAS MONTES DE ORO 3
SAN JOSE LEON CORTES 3
SAN JOSE MORAVIA 3
ALAJUELA POAS 2
ALAJUELA UPALA 2
CARTAGO ALVARADO 2
GUANACASTE TILARAN 2
HEREDIA SANTA BARBARA 2
PUNTARENAS COTO BRUS 2
SAN JOSE MORA 2
ALAJUELA RIO CUARTO 1
CARTAGO JIMENEZ 1
GUANACASTE NANDAYURE 1
HEREDIA BARVA 1
HEREDIA FLORES 1
HEREDIA SAN PABLO 1
PUNTARENAS BUENOS AIRES 1
SAN JOSE PURISCAL 1
SAN JOSE SANTA ANA 1
SAN JOSE TARRAZU 1
# Cantidad de registros por delito y subdelito
delitos_2023 |>
  group_by(Delito, SubDelito) |>
  summarize(
    n = n()
  ) |>
  arrange(desc(n)) |>
  kable()
Delito SubDelito n
HURTO POR DESCUIDO 8326
ROBO FORZADURA 6066
ASALTO ARMA DE FUEGO 4915
TACHA DE VEHICULO TACHA DE VEHICULO 3863
ASALTO ARMA BLANCA 2925
HURTO POR CONFIANZA 2924
HURTO CARTERISTA 2869
ROBO DE VEHICULO DESCUIDO 2356
ASALTO ARREBATO 1915
ROBO BOQUETE 1385
ROBO ESCALAMIENTO 1274
ASALTO GOLPES 1248
ROBO DE VEHICULO ASALTO 1070
ROBO DE VEHICULO COCHERAZO 750
HURTO ARDID PREVIO/DISTRACCION 728
ROBO CORTA CANDADOS 695
ROBO RUPTURA VENTANA 682
ASALTO INTIMIDACION VERBAL 666
HOMICIDIO AJUSTE DE CUENTAS/VENGANZA 633
HURTO OTRO O INDETERMINADO 633
ROBO QUITAN CELOSIAS 502
HURTO CON LLAVE 484
ROBO DE VEHICULO POR CONFIANZA 399
ASALTO CANDADO CHINO 352
ROBO OTRO O INDETERMINADO 345
ASALTO INMOVILIZACION 331
HURTO PROGRESIVOS 233
ASALTO ARMA CONTUNDENTE 170
ASALTO OTRO O INDETERMINADO 149
ROBO DE VEHICULO OTRO O INDETERMINADO 133
HOMICIDIO DISCUSION/RIÑA 118
HURTO RETIRO DE CAJERO AUTOMATICO 116
HOMICIDIO POR LA COMISION DE OTRO DELITO 83
HURTO USO DE SOMNIFERO 74
ROBO DE VEHICULO ARDID PREVIO 51
HURTO GANZUA/VARILLA 47
HOMICIDIO VIOLENCIA DOMESTICA 26
ASALTO USO DE GAS 19
HOMICIDIO OTRO O INDETERMINADO 16
HOMICIDIO REPELIENDO ACTIVIDAD CRIMINAL 12
HOMICIDIO PROFESIONAL 9
HOMICIDIO NO DETERMINADO 5
HURTO DESCONOCIDO 5
ASALTO DESCONOCIDO 3
ROBO DESCONOCIDO 3
HOMICIDIO FEMICIDIO 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_2023 |>
  distinct(Victima) |>
  kable()
Victima
PERSONA
VIVIENDA
VEHICULO
EDIFICACION
OTROS

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_2023 |>
  count(Victima)
# A tibble: 5 × 2
  Victima         n
  <chr>       <int>
1 EDIFICACION  6981
2 OTROS        1946
3 PERSONA     19448
4 VEHICULO    11476
5 VIVIENDA     9758
# Expresión equivalente con summarize
delitos_2023 |>
  group_by(Victima) |>
  summarize(n = n()) |>
  kable()
Victima n
EDIFICACION 6981
OTROS 1946
PERSONA 19448
VEHICULO 11476
VIVIENDA 9758

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