9 dplyr

dplyr es un paquete que permite llevar a cabo muchas de las funciones que pueden llevarse a cabo con la sintaxis básica de R. Ahora bien, dplyr resulta útil ya que propone una “grámatica” para manipular bases de datos. Otra de las ventajas de este paquete es la eficiencia computacional con la que puede llevar a cabo sus funciones.

9.1 Instalación de dplyr

Como se mencionaba anteriormente para ggplot2, puede obternerse dplyr instalando tidyverse de la siguiente manera:

install.packages("tidyverse")

También puede instalarse solamente dplyr:

install.packages("dplyr")

9.2 Principales verbos de dplyr

Los “verbos” del paquete son aquellas funciones que pueden entenderse de manera más natural, estas son:

  • select: retorna un conjunto de columnas.

  • arrange: permite reorganizar filas de un data frame.

  • filter: retorna, dadas ciertas condiciones lógicas, un conjunto de filas.

  • mutate: permite añadir nuevas variables o transformar variables ya existentes.

  • rename: cambia el nombre de las variables de un data frame.

  • summarise: resume la información contenida en un data frame.

  • %>%: llamado el operador “pipe” permite conectar multiples accciones.

A continuación, estudiaremos cada una de estas funciones.

9.3 select()

Inicialmente debemos cargar la librería para usar sus funciones:

## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union

Igualmente instalamos y cargamos una librería cuyos datos podremos utilizar:

install.packages("nycflights13")

Podemos familiarizarnos con los datos que se encuentran en el paquete nycflights13 usando la función View()

View(airlines)
View(flights)
View(weather)

Observemos el contenido del data frame weather:

##  [1] "origin"     "year"       "month"      "day"        "hour"      
##  [6] "temp"       "dewp"       "humid"      "wind_dir"   "wind_speed"
## [11] "wind_gust"  "precip"     "pressure"   "visib"      "time_hour"
## # A tibble: 26,115 x 15
##    origin  year month   day  hour  temp  dewp humid wind_dir wind_speed
##    <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>
##  1 EWR     2013     1     1     1  39.0  26.1  59.4      270      10.4 
##  2 EWR     2013     1     1     2  39.0  27.0  61.6      250       8.06
##  3 EWR     2013     1     1     3  39.0  28.0  64.4      240      11.5 
##  4 EWR     2013     1     1     4  39.9  28.0  62.2      250      12.7 
##  5 EWR     2013     1     1     5  39.0  28.0  64.4      260      12.7 
##  6 EWR     2013     1     1     6  37.9  28.0  67.2      240      11.5 
##  7 EWR     2013     1     1     7  39.0  28.0  64.4      240      15.0 
##  8 EWR     2013     1     1     8  39.9  28.0  62.2      250      10.4 
##  9 EWR     2013     1     1     9  39.9  28.0  62.2      260      15.0 
## 10 EWR     2013     1     1    10  41    28.0  59.6      260      13.8 
## # ... with 26,105 more rows, and 5 more variables: wind_gust <dbl>,
## #   precip <dbl>, pressure <dbl>, visib <dbl>, time_hour <dttm>

Para crear otra base de datos a partir de weather que tenga sólo las variables origin, year, month, day y temp procedemos de la siguiente manera:

## [1] "origin" "year"   "month"  "day"    "temp"
## # A tibble: 26,115 x 5
##    origin  year month   day  temp
##    <chr>  <int> <int> <int> <dbl>
##  1 EWR     2013     1     1  39.0
##  2 EWR     2013     1     1  39.0
##  3 EWR     2013     1     1  39.0
##  4 EWR     2013     1     1  39.9
##  5 EWR     2013     1     1  39.0
##  6 EWR     2013     1     1  37.9
##  7 EWR     2013     1     1  39.0
##  8 EWR     2013     1     1  39.9
##  9 EWR     2013     1     1  39.9
## 10 EWR     2013     1     1  41  
## # ... with 26,105 more rows

Nótese como el primer argumento corresponde al data frame weather, del cual se seleccionan las variables que le siguen por comas.

También podemos excluir columnas específicas, agregando un guión ‘-

##  [1] "year"       "month"      "day"        "hour"       "temp"      
##  [6] "dewp"       "humid"      "wind_dir"   "wind_speed" "wind_gust" 
## [11] "precip"     "pressure"   "visib"      "time_hour"
## # A tibble: 26,115 x 14
##     year month   day  hour  temp  dewp humid wind_dir wind_speed wind_gust
##    <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>     <dbl>
##  1  2013     1     1     1  39.0  26.1  59.4      270      10.4         NA
##  2  2013     1     1     2  39.0  27.0  61.6      250       8.06        NA
##  3  2013     1     1     3  39.0  28.0  64.4      240      11.5         NA
##  4  2013     1     1     4  39.9  28.0  62.2      250      12.7         NA
##  5  2013     1     1     5  39.0  28.0  64.4      260      12.7         NA
##  6  2013     1     1     6  37.9  28.0  67.2      240      11.5         NA
##  7  2013     1     1     7  39.0  28.0  64.4      240      15.0         NA
##  8  2013     1     1     8  39.9  28.0  62.2      250      10.4         NA
##  9  2013     1     1     9  39.9  28.0  62.2      260      15.0         NA
## 10  2013     1     1    10  41    28.0  59.6      260      13.8         NA
## # ... with 26,105 more rows, and 4 more variables: precip <dbl>,
## #   pressure <dbl>, visib <dbl>, time_hour <dttm>

De la misma manera podemos usar la notación “:”, aprendida anteriormente, para configurar rangos de columnas:

## [1] "origin" "year"   "month"  "day"    "hour"   "temp"

Podríamos combinar funciones de la siguiente manera:

## [1] "dewp"       "humid"      "wind_dir"   "wind_speed" "wind_gust" 
## [6] "precip"     "pressure"   "visib"      "time_hour"

En el ejemplo anterior retiramos las columnas comprendidas entre origin y temp del data frame weather.

9.4 arrange()

La función arrange() permite reorganizar las filas de una base de datos. Por defecto, arrange() organiza las filas por orden ascendente.

Para organizar las filas del data frame weather en orden ascendente dados los valores de la variable temp el código es el siguiente:

## # A tibble: 26,115 x 15
##    origin  year month   day  hour  temp  dewp humid wind_dir wind_speed
##    <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>
##  1 EWR     2013     1    23     5  10.9 -4     50.2      270      10.4 
##  2 EWR     2013     1    23     6  10.9 -4     50.2      270      11.5 
##  3 EWR     2013     1    23     2  12.0 -7.06  41.3      270      10.4 
##  4 EWR     2013     1    23     3  12.0 -5.98  43.5      270       8.06
##  5 EWR     2013     1    23     4  12.0 -5.08  45.4      270       9.21
##  6 EWR     2013     1    23     7  12.0 -2.92  50.4      270      11.5 
##  7 EWR     2013     1    24     3  12.0  1.94  63.3       30       4.60
##  8 JFK     2013     1    23     4  12.0 -7.06  41.3      280      12.7 
##  9 JFK     2013     1    23     5  12.0 -5.08  45.4      280      15.0 
## 10 JFK     2013     1    23     6  12.0 -5.08  45.4      290      13.8 
## # ... with 26,105 more rows, and 5 more variables: wind_gust <dbl>,
## #   precip <dbl>, pressure <dbl>, visib <dbl>, time_hour <dttm>

Por otro lado, si queremos organizarlas de manera descendente podríamos hacer lo siguiente:

## # A tibble: 26,115 x 15
##    origin  year month   day  hour  temp  dewp humid wind_dir wind_speed
##    <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>
##  1 EWR     2013     7    18    15 100.   66.0  33.2      300       9.21
##  2 EWR     2013     7    19    16 100.   71.1  39.5      230      20.7 
##  3 EWR     2013     7    19    13  99.0  72.0  42.1      260      18.4 
##  4 EWR     2013     7    19    14  99.0  71.1  40.8      250      20.7 
##  5 EWR     2013     7    19    15  99.0  72.0  42.1      240      16.1 
##  6 LGA     2013     7    18    15  99.0  64.0  32.0      250      12.7 
##  7 LGA     2013     7    19    15  99.0  69.1  38.2      240      16.1 
##  8 LGA     2013     7    19    16  99.0  69.1  38.2      230      15.0 
##  9 EWR     2013     7    18    12  98.1  69.1  39.2      300       9.21
## 10 EWR     2013     7    18    13  98.1  66.9  36.4      300      11.5 
## # ... with 26,105 more rows, and 5 more variables: wind_gust <dbl>,
## #   precip <dbl>, pressure <dbl>, visib <dbl>, time_hour <dttm>

Para reorganizar las filas de un data frame podemos considerar más de una variable, por ejemplo, por day y hour:

## # A tibble: 26,115 x 15
##    origin  year month   day  hour  temp  dewp humid wind_dir wind_speed
##    <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>
##  1 EWR     2013     2     1     0  28.9  10.9  46.4      250      19.6 
##  2 EWR     2013     3     1     0  37.0  30.0  75.5      250       4.60
##  3 EWR     2013     4     1     0  46.4  45.0  96.0      270       6.90
##  4 EWR     2013     5     1     0  52.0  30.9  44.3        0       0   
##  5 EWR     2013     6     1     0  78.1  64.0  62.0      230       6.90
##  6 EWR     2013     7     1     0  75.2  71.6  88.6      140       3.45
##  7 EWR     2013     8     1     0  73.0  63.0  70.7      180       4.60
##  8 EWR     2013     9     1     0  80.1  71.1  74.1      220       5.75
##  9 EWR     2013    10     1     0  59    54.0  83.3      230       8.06
## 10 EWR     2013    11     1     0  64.0  62.1  93.3      200      11.5 
## # ... with 26,105 more rows, and 5 more variables: wind_gust <dbl>,
## #   precip <dbl>, pressure <dbl>, visib <dbl>, time_hour <dttm>

9.5 filter()

Esta función nos permite filtrar filas dada una condición:

## # A tibble: 14,181 x 15
##    origin  year month   day  hour  temp  dewp humid wind_dir wind_speed
##    <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>
##  1 EWR     2013     1    14     9  53.6  52.0 100        250       5.75
##  2 EWR     2013     1    14    10  55.9  53.1  90.0      270       5.75
##  3 EWR     2013     1    14    11  57.9  50    74.9      320      11.5 
##  4 EWR     2013     1    14    12  55.9  45.0  66.5      340      11.5 
##  5 EWR     2013     1    14    13  57.0  44.1  61.8      320      10.4 
##  6 EWR     2013     1    14    14  55.9  37.9  50.7      340      12.7 
##  7 EWR     2013     1    14    15  55.0  39.0  54.7      350      13.8 
##  8 EWR     2013     1    14    16  53.1  30.9  42.6      320      15.0 
##  9 EWR     2013     1    20    10  54.0  28.0  36.6      240      19.6 
## 10 EWR     2013     1    20    11  54.0  27.0  35.0      250      19.6 
## # ... with 14,171 more rows, and 5 more variables: wind_gust <dbl>,
## #   precip <dbl>, pressure <dbl>, visib <dbl>, time_hour <dttm>

En el ejemplo anterior incluimos las filas que cumplieran la condición especificada en el segundo argumento, es decir, vuelos realizados con temperaturas mayores a 52º.

También es posible incluir varias condiciones, donde además de la condición de temperatura, filtramos por vuelos que tengan como origen el aeropuerto EWR.:

## 
##  EWR  JFK  LGA 
## 8703 8706 8706
## # A tibble: 4,775 x 15
##    origin  year month   day  hour  temp  dewp humid wind_dir wind_speed
##    <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>
##  1 EWR     2013     1    14     9  53.6  52.0 100        250       5.75
##  2 EWR     2013     1    14    10  55.9  53.1  90.0      270       5.75
##  3 EWR     2013     1    14    11  57.9  50    74.9      320      11.5 
##  4 EWR     2013     1    14    12  55.9  45.0  66.5      340      11.5 
##  5 EWR     2013     1    14    13  57.0  44.1  61.8      320      10.4 
##  6 EWR     2013     1    14    14  55.9  37.9  50.7      340      12.7 
##  7 EWR     2013     1    14    15  55.0  39.0  54.7      350      13.8 
##  8 EWR     2013     1    14    16  53.1  30.9  42.6      320      15.0 
##  9 EWR     2013     1    20    10  54.0  28.0  36.6      240      19.6 
## 10 EWR     2013     1    20    11  54.0  27.0  35.0      250      19.6 
## # ... with 4,765 more rows, and 5 more variables: wind_gust <dbl>,
## #   precip <dbl>, pressure <dbl>, visib <dbl>, time_hour <dttm>

En la siguiente función, nos quedamos con los datos de temperatura entre 52 y 80 y donde el origen sean los aeropuertos de EWR y JFK.

## 
##  EWR  JFK 
## 3900 4076
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   52.52   60.08   66.20   66.32   73.04   78.98

Para plantear las condiciones según las cuales se filtrará el data frame hacemos uso de los operadores lógicos mencionados en el capítulo 6.

Otro ejemplo con filtros simúltaneos:

9.6 mutate()

La función mutate() permite realizar transformaciones de las variables del data frame. Tanto reconfigurar variables dadas o crear nuevas usando información ya incluida.

Continuando con el data frame weather, creemos la variable temp3 que es la divición de temp sobre 3:

## # A tibble: 3 x 2
##    temp temp3
##   <dbl> <dbl>
## 1  39.0  13.0
## 2  39.0  13.0
## 3  39.0  13.0

Ahora creamos una variable binaria a partir de temp, donde la nueva variable btemp va a tomar el valor de 1 si temp se encuentra entre 52 y 80, y 0 en otro caso. El código sería:

## # A tibble: 3 x 2
##    temp btemp
##   <dbl> <dbl>
## 1  39.0     0
## 2  39.0     0
## 3  39.0     0
## 
##     0     1 
## 14154 11960

También es posible crear variables más complejas utilizando otras funciones en mutate. Por ejemplo, creemos una variable que exprese las frecuencias absolutas acumuladas de la variable temp:

## # A tibble: 5 x 2
##    temp acum.temp
##   <dbl>     <dbl>
## 1  39.0      39.0
## 2  39.0      78.0
## 3  39.0     117. 
## 4  39.9     157. 
## 5  39.0     196

9.7 rename()

rename nos permite renombrar las variables de un data frame de una forma sencilla e intuitiva.

Observemos los nombres de las variables del data frame weather:

##  [1] "origin"     "year"       "month"      "day"        "hour"      
##  [6] "temp"       "dewp"       "humid"      "wind_dir"   "wind_speed"
## [11] "wind_gust"  "precip"     "pressure"   "visib"      "time_hour"

Podríamos utilizar la función rename() para traducir los nombres de algunas variables del data frame weather, por ejemplo:

##  [1] "origen"      "año"         "mes"         "día"         "hora"       
##  [6] "temperatura" "dewp"        "humid"       "wind_dir"    "wind_speed" 
## [11] "wind_gust"   "precip"      "pressure"    "visib"       "time_hour"

9.8 summarise()

La función summarise crea una nueva base de datos a partir otra. Es una función similar a mutate, no obstante, este crea data frames nuevos y no columnas dentro de uno.

Observemos el data frame planes que brinda información sobre los aviones y creemos uno nuevo donde incluyamos la media y varianza del número de asientos:

## [1] "tailnum"      "year"         "type"         "manufacturer" "model"       
## [6] "engines"      "seats"        "speed"        "engine"
## # A tibble: 5 x 9
##   tailnum  year type           manufacturer   model  engines seats speed engine 
##   <chr>   <int> <chr>          <chr>          <chr>    <int> <int> <int> <chr>  
## 1 N10156   2004 Fixed wing mu~ EMBRAER        EMB-1~       2    55    NA Turbo-~
## 2 N102UW   1998 Fixed wing mu~ AIRBUS INDUST~ A320-~       2   182    NA Turbo-~
## 3 N103US   1999 Fixed wing mu~ AIRBUS INDUST~ A320-~       2   182    NA Turbo-~
## 4 N104UW   1999 Fixed wing mu~ AIRBUS INDUST~ A320-~       2   182    NA Turbo-~
## 5 N10575   2002 Fixed wing mu~ EMBRAER        EMB-1~       2    55    NA Turbo-~
## # A tibble: 1 x 2
##   media_seats var_seats
##         <dbl>     <dbl>
## 1        154.     5425.

9.9 Operador pipe: %>%

Como su nombre lo dice, el operador pipe permite concatenar funciones distintas del paquete dplyr permitiendo que adopten forma de tubería. Este operador permite de forma intuitiva realizar funciones anidadas. Un ejemplo claro de este operador puede plantearse usando cualquiera de las funciones que hemos aprendido anteriormente.

Supongamos que queremos construir una base de datos a partir de weather teniendo en cuenta ciertos filtros a partir de la variable temp, con la función pipe el código sería:

##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   52.52   60.08   66.92   66.39   73.04   78.98

La idea, entonces, es que a la base de datos weather le aplicamos (%>%) el filtro, lo cual nos da como resultado la nueva base data con el filtro.

Imaginemos, ahora, que deseamos aplicar varias de las funciones del paquete dplyr a un mismo data frame ¿cómo podríamos emplear el operador %>%? Analicemos el siguiente ejemplo:

## # A tibble: 5 x 6
##   origin  year month   day  hour  temp
##   <chr>  <int> <int> <int> <int> <dbl>
## 1 EWR     2013     1    14     9  53.6
## 2 EWR     2013     1    14    10  55.9
## 3 EWR     2013     1    14    11  57.9
## 4 EWR     2013     1    14    12  55.9
## 5 EWR     2013     1    14    13  57.0
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   52.52   60.08   66.92   66.39   73.04   78.98

En el ejemplo anterior aplicamos dos condiciones al data frame weather: primero filtramos y al resultado del filtrado seleccionamos las columnas comprendidas entre origin y temp.

Ahora, filtremos, seleccionamos variables y creamos dos variables nuevas:

## # A tibble: 5 x 8
##   origin  year month   day  hour  temp temp3 btemp
##   <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>
## 1 EWR     2013     1    14     9  53.6  17.9     1
## 2 EWR     2013     1    14    10  55.9  18.6     1
## 3 EWR     2013     1    14    11  57.9  19.3     1
## 4 EWR     2013     1    14    12  55.9  18.6     1
## 5 EWR     2013     1    14    13  57.0  19.0     1

Calculemos la media y la varianza de seats por type, y para ello utilizamos la función group_by:

## 
##  Fixed wing multi engine Fixed wing single engine               Rotorcraft 
##                     3292                       25                        5
## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 3 x 4
##   type                         n media_seats var_seats
##   <chr>                    <int>       <dbl>     <dbl>
## 1 Fixed wing multi engine   3292      156.     5268.  
## 2 Fixed wing single engine    25        3.68      9.48
## 3 Rotorcraft                   5        8.6      15.3

9.10 Combinando bases de datos

Existen dos principales formas de combinar bases de datos:

  • Verticalmente: adicionar nuevas observaciones
  • Horizontalmente: adicionar nuevas variables

Verticalmente

En este caso tendremos dos bases de datos con igual variables pero con diferentes individuos, con lo cual el número de observaciones de la base de datos final tendrá la suma de las observaciones de las dos bases pegadas.

Tomando de nuevo la base de datos weather la dividimos por la mitad en términos de las observaciones y luego utilizamos la función bind_rows del paquete dplyr para volver a juntar las bases de datos.

## [1] 26115
## [1] 13057.5
## [1] 13057
## [1] 13058
## [1] 26115

Horizontalmente

Aquí la idea es que se tienen dos bases de datos que puede o no tener los mismos individuos y en cada una de ellas hay diferentes variables de esos individuos y la pega de las bases se hace a través de un identificador de los individuos que se encuentra en las dos bases. Se utiliza, entonces, la función left_join del paquete dplyr, lo cual genera el siguiente tipo de combinación de los datos:4

Se observa que se hace la combinación con el identificador común entre las dos bases de datos y la base final deja las observaciones que coinciden en las dos bases, más las observaciones que se encuentran en la primera base de datos.

Como ejemplo, se tienen dos bases de datos de tres paises. En la base GDP2020 se tiene información sobre el GDP y tipo de moneda, mientras que en DollarValue2020 se cuenta con información sobre la tasa de cambio del dólar. La idea, entonces, es juntar las dos bases de datos utilizando como llave o identificador los países.

##   Country Currency GDPTrillions
## 1      UK    Pound         2.10
## 2     USA   Dollar        20.58
## 3  France     Euro         2.78
##   Country InDollars
## 1  France     1.104
## 2      UK     1.256
## 3     USA     1.000

Para pegar las dos bases de datos se utiliza el siguiente código:

##   Country Currency GDPTrillions InDollars
## 1      UK    Pound         2.10     1.256
## 2     USA   Dollar        20.58     1.000
## 3  France     Euro         2.78     1.104

En la función left_join el primer y segundo elementos son las bases de datos a pegar, mientras que el tercero es la variable llave o identificador que se usa para la pega. Sin embargo, esta última opción no es necesario ponerla, ya que la función por sí misma identificará la variable llave para combinar las bases.

Supongamos que tenemos esta otra base de datos:

##   Currency InDollars
## 1     Euro   1.10400
## 2    Pound   1.25600
## 3      Yen   0.00926
## 4   Dollar   1.00000

Y hacemos la pega:

## Joining, by = "Currency"
##   Country Currency GDPTrillions InDollars
## 1      UK    Pound         2.10     1.256
## 2     USA   Dollar        20.58     1.000
## 3  France     Euro         2.78     1.104

Se tiene que la pega se hace con Currency y solo pega las observaciones en las cuales encontró similitud entre las dos bases de datos y deja por fuera la observación 3 de la segunda base que no encontró coincidencia con la primera base de datos.

Es posible decirle que deje las observaciones que no se encuentran en la segunda base de datos y elimine las observaciones que no coinciden la primera. Para ello utilizamos la función right_join, que genera el siguiente tipo de combinación entre bases de datos:

Se observa que, la base final tiene las observaciones que coinciden entre ambas bases, incluye la observación que no coincide de la segunda base y elimina la observación que no coincide de la primera base de datos.

En nuestro ejemplo, el código para la pega sería:

## Joining, by = "Currency"
##   Country Currency GDPTrillions InDollars
## 1      UK    Pound         2.10   1.25600
## 2     USA   Dollar        20.58   1.00000
## 3  France     Euro         2.78   1.10400
## 4    <NA>      Yen           NA   0.00926

Existen otras funciones para hacer combinaciones entre bases de datos, en los siguientes link pueden ver las demás opciones: https://dplyr.tidyverse.org/reference/mutate-joins.html, https://www.garrickadenbuie.com/project/tidyexplain/.

9.11 Información adicional

El paquete dplyr cuenta con muchas funciones más allá de las que se presentaron brevemente en este capítulo. Por otro lado, las funciones aquí presentadas pueden seguir instrucciones aún más específicas. Por esto, resulta bastante interesante consultar la documentación al respecto en el siguiente link: https://cran.r-project.org/web/packages/dplyr/dplyr.pdf