Capítulo 2 Técnicas de Manipulación de Datos

2.1 Ordenación y estructuración de los datos (Data Tidying)

Como en toda área profesional, existen algunos principios básicos que deben respetar quienes trabajan en ciencia de datos. A esto se le denomina Data Tidying. Es el primer paso del flujo de trabajo e involucra la limpieza y organización de los datos con una estructura estándar, lo cual facilita su posterior manipulación, modelado y visualización.

Principios generales

  • Cada variable está en una columna.
  • Cada observación diferente de esa variable está en una fila.
  • Debe haber una tabla distinta para cada “clase” de variable, p.e. secuencias, datos ambientales, etc
  • Si tienes múltiples tablas, debe existir una columna en cada tabla que permita enlazarlas (identificador).
  • Se debe generar archivo texto complementario con descripción de la fuente de la base de datos, diseño experimental, variables, unidades, etc.

2.2 Manipulación de datos con data.table

El programa data.table es un paquete R que proporciona una versión mejorada para el manejo de data.frames. Los data.frames son una estructura de datos estándar de manejo de datos en R. Este programa simplifica la programación y reduce tremendamente el tiempo de cálculo. Mantiene juntas operaciones de manipulación de datos, como subconjuntos, grupos, actualizaciones, uniones, etc. Las principales características incluyen:

  • Una sintaxis concisa y consistente, independientemente del conjunto de operaciones que le gustaría realizar para lograr su objetivo final.
  • La realización de los análisis de forma fluida.
  • La optimización automática de las operaciones internamente y de manera muy eficaz, al conocer con precisión los datos necesarios para cada operación, lo que lleva a un código muy rápido y eficiente en memoria.

A continuación se presenta un esquema de la estructura básica de operación de data.table:

2.2.1 Creación de un conjunto de datos con la función data.table

library(data.table)
datatable <- data.table(x =c("a", "b", "c", "d", "e"), 
                         y = c(1, 2, 3, 4, 5))  
str(datatable)
#> Classes 'data.table' and 'data.frame':   5 obs. of  2 variables:
#>  $ x: chr  "a" "b" "c" "d" ...
#>  $ y: num  1 2 3 4 5
#>  - attr(*, ".internal.selfref")=<externalptr>

Se pueden autocompletar datos

DT = data.table(x=rep(c("b","a","c"),each=3), y=c(1,3,6), v=1:9) #repite 3 veces cada valor en x
DT
#>    x y v
#> 1: b 1 1
#> 2: b 3 2
#> 3: b 6 3
#> 4: a 1 4
#> 5: a 3 5
#> 6: a 6 6
#> 7: c 1 7
#> 8: c 3 8
#> 9: c 6 9

2.2.2 Operaciones básicas para manejo de filas

DT[2]               # visualiza la segunda fila
#>    x y v
#> 1: b 3 2
DT[3:2]             # visualiza de la tercera a la segunda fila
#>    x y v
#> 1: b 6 3
#> 2: b 3 2
DT[c(2, 3)]         # visualiza la fila2 y la fila3
#>    x y v
#> 1: b 3 2
#> 2: b 6 3
DT[order(x)]        # ordena de acuerdo a la columna x 
#>    x y v
#> 1: a 1 4
#> 2: a 3 5
#> 3: a 6 6
#> 4: b 1 1
#> 5: b 3 2
#> 6: b 6 3
#> 7: c 1 7
#> 8: c 3 8
#> 9: c 6 9
DT[y>2]             # llama todas las filas que tengan valores de y > 2
#>    x y v
#> 1: b 3 2
#> 2: b 6 3
#> 3: a 3 5
#> 4: a 6 6
#> 5: c 3 8
#> 6: c 6 9
DT[y>2 & v>5]       # llama filas  de y > 2 y(&) valores de v > 5
#>    x y v
#> 1: a 6 6
#> 2: c 3 8
#> 3: c 6 9
DT[!2:4]            # llama todas las filas excepto de la 2 a la 4
#>    x y v
#> 1: b 1 1
#> 2: a 3 5
#> 3: a 6 6
#> 4: c 1 7
#> 5: c 3 8
#> 6: c 6 9
DT[-(2:4)]          # llama todas las filas excepto de la 2 a la 4
#>    x y v
#> 1: b 1 1
#> 2: a 3 5
#> 3: a 6 6
#> 4: c 1 7
#> 5: c 3 8
#> 6: c 6 9

2.2.3 Selección de subconjuntos de filas

DT["a", on="x"]         # busca las filas con “a” dentro de columna x
#>    x y v
#> 1: a 1 4
#> 2: a 3 5
#> 3: a 6 6
DT["a", on=.(x)]        # igual pero sin paréntesis en columnas
#>    x y v
#> 1: a 1 4
#> 2: a 3 5
#> 3: a 6 6
DT[2:3, sum(v)]         # calcula suma de valores en v, filas 2 y 3
#> [1] 5
DT[2:3, .(sum(v))]      # igual pero  nombrar columna como V1
#>    V1
#> 1:  5
DT[2:3, .(sv=sum(v))]   # igual pero nombra columna como “sv”  
#>    sv
#> 1:  5
colnames(DT)            # visualiza los nombres de las columnas
#> [1] "x" "y" "v"
dim(DT)                 # visualiza las dimensiones de DT
#> [1] 9 3

2.2.4 Creación de nuevas variables.

Se pueden crear nuevas bases de datos con nuevas variables a partir de la data.table existente. Para la creación de nuevas variables se pueden realizar por ejemplo operaciones aritméticas entre variables existentes.

nueva_DT <- DT[, .(y, z=y*v)]
nueva_DT
#>    y  z
#> 1: 1  1
#> 2: 3  6
#> 3: 6 18
#> 4: 1  4
#> 5: 3 15
#> 6: 6 36
#> 7: 1  7
#> 8: 3 24
#> 9: 6 54

2.2.5 Operaciones básicas para el manejo de columnas

DT[, v]                   # visualiza los elementos de la columna v como vector
#> [1] 1 2 3 4 5 6 7 8 9
DT[["v"]]                 # visualiza los elementos de la columna v como vector
#> [1] 1 2 3 4 5 6 7 8 9
DT[, list(v)]             # visualiza los elementos de la columna v en formato data.table
#>    v
#> 1: 1
#> 2: 2
#> 3: 3
#> 4: 4
#> 5: 5
#> 6: 6
#> 7: 7
#> 8: 8
#> 9: 9
DT[, .(v)]                # visualiza columna v. El parámetro .() es un alias para list()
#>    v
#> 1: 1
#> 2: 2
#> 3: 3
#> 4: 4
#> 5: 5
#> 6: 6
#> 7: 7
#> 8: 8
#> 9: 9
DT[, 2, with=FALSE]       # visualiza segunda columna
#>    y
#> 1: 1
#> 2: 3
#> 3: 6
#> 4: 1
#> 5: 3
#> 6: 6
#> 7: 1
#> 8: 3
#> 9: 6
DT[, sum(v)]              # calcula la suma de la columna v, resultado como vector 
#> [1] 45
DT[, .(sum(v))]           # calcula sumatoria, crea un  data.table con V1 como nueva columna 
#>    V1
#> 1: 45
DT[, .(v, v*2)]           # visualiza una tabla con la columna v y otra con los valores de v*2
#>    v V2
#> 1: 1  2
#> 2: 2  4
#> 3: 3  6
#> 4: 4  8
#> 5: 5 10
#> 6: 6 12
#> 7: 7 14
#> 8: 8 16
#> 9: 9 18

2.2.6 Selección de subconjuntos de columnas

DT[c(1,3), y:v]             # visualiza datos de filas 1 y 3 de las columnas de y a v.
#>    y v
#> 1: 1 1
#> 2: 6 3
DT[, sum(v), by=x]        # suma valores de v agrupados por x, preserva orden original
#>    x V1
#> 1: b  6
#> 2: a 15
#> 3: c 24
DT[, sum(v), keyby=x]     # suma valores de v agrupados por x, ordena ascendente
#>    x V1
#> 1: a 15
#> 2: b  6
#> 3: c 24
DT["b", sum(v*y), on="x"] # para valores de "b" en x calcula la suma de v*y
#> [1] 25
DT[x!="a", sum(v), by=x]  # para cada subgrupo dentro de “x” excepto “a” calcula suma de v
#>    x V1
#> 1: b  6
#> 2: c 24

2.2.7 Conversión a formato data.table

Para el siguiente ejemplo se puede convertir la popular base de datos sobre la variación morfológica de la flor Iris, pasando de un data.frame a un data.table con la función data.table().

data(iris)
class(iris)
#> [1] "data.frame"
DT <- as.data.table(iris)
class(DT)
#> [1] "data.table" "data.frame"

2.2.8 Realización de cálculos en columnas y agrupación

data(iris)
str(iris)
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
DT[, mean(Sepal.Length), by=Species]        # calcula la media de variable Sepal.Length agrupada por especie
#>       Species    V1
#> 1:     setosa 5.006
#> 2: versicolor 5.936
#> 3:  virginica 6.588
DT[, list(.N, mean(Sepal.Length), sd(Sepal.Length)), by=Species] # se pueden listar varias métricas a calcular manteniendo agrupación
#>       Species  N    V2        V3
#> 1:     setosa 50 5.006 0.3524897
#> 2: versicolor 50 5.936 0.5161711
#> 3:  virginica 50 6.588 0.6358796
DT[, .(mean=(mean(Sepal.Length)), sd=(sd(Sepal.Length))), by=Species] # se puede asignar encabezado para variable calculada
#>       Species  mean        sd
#> 1:     setosa 5.006 0.3524897
#> 2: versicolor 5.936 0.5161711
#> 3:  virginica 6.588 0.6358796

Con estes programa se pueden realizar cálculos más complejos sobre las variables en la base de datos. Se puede, por ejemplo, crear una nueva variable (Sepal.Area)producto de multiplicar Sepal.Length por Sepal.Width. El valor obtenido se puede redondear al valor más cercano a los 10 cm2. Finalmente se puede realizar un conteo con el parámetro .N de la cantidad de individuos en cada rango de area.

DT[, .N, by = 10 * round(Sepal.Length*Sepal.Width / 10)]
#>    round   N
#> 1:    20 117
#> 2:    10  29
#> 3:    30   4

2.2.9 Fusión de tablas

Una de las funciones más importantes en el manejo de bases de datos se relaciona con la capacidad de fusionar diferentes bases de datos a partir de identificadores o elementos comunes en ellas

DT = data.table(x=rep(c("b","a","c"),each=3), y=c(1,3,6), v=1:9)
NT = data.table(x=c("c","b"), v=8:7, k=c(4,2))
NT
#>    x v k
#> 1: c 8 4
#> 2: b 7 2
DT[NT, on="x"]                      # elementos de x en NT presentes en DT, autocompleta
#>    x y v i.v k
#> 1: c 1 7   8 4
#> 2: c 3 8   8 4
#> 3: c 6 9   8 4
#> 4: b 1 1   7 2
#> 5: b 3 2   7 2
#> 6: b 6 3   7 2
NT[DT, on="x"]                      # elementos de x en DT presentes en NT, autocompleta incluye NA 
#>    x  v  k y i.v
#> 1: b  7  2 1   1
#> 2: b  7  2 3   2
#> 3: b  7  2 6   3
#> 4: a NA NA 1   4
#> 5: a NA NA 3   5
#> 6: a NA NA 6   6
#> 7: c  8  4 1   7
#> 8: c  8  4 3   8
#> 9: c  8  4 6   9
DT[!NT, on="x"]                     # elementos ausentes en NT que están en DT
#>    x y v
#> 1: a 1 4
#> 2: a 3 5
#> 3: a 6 6
DT[NT, on="x", mult="first"]        # primera fila de cada grupo luego de fusión
#>    x y v i.v k
#> 1: c 1 7   8 4
#> 2: b 1 1   7 2
DT[NT, sum(v), by=.EACHI, on="x"]   # fusiona y calcula suma de v para cada grupo de x
#>    x V1
#> 1: c 24
#> 2: b  6

2.2.10 Algunos atributos especiales de data.table

DT <- data.table( a = c(1L, 2L), b = LETTERS[1:10], c = c(1:10))
DT  
#>     a b  c
#>  1: 1 A  1
#>  2: 2 B  2
#>  3: 1 C  3
#>  4: 2 D  4
#>  5: 1 E  5
#>  6: 2 F  6
#>  7: 1 G  7
#>  8: 2 H  8
#>  9: 1 I  9
#> 10: 2 J 10
DT[.N ]                       # visualiza última fila
#>    a b  c
#> 1: 2 J 10
DT[.N - 1]                  # visualiza penultima fila
#>    a b c
#> 1: 1 I 9
DT[, .N]                    # realiza conteo del número filas en DT
#> [1] 10
DT[, .N, by=a]              # cuenta los elementos en cada grupo de "a"
#>    a N
#> 1: 1 5
#> 2: 2 5
DT[, .SD[1], by=a]          # visualiza primera fila para cada grupo en “a”
#>    a b c
#> 1: 1 A 1
#> 2: 2 B 2
DT[, .I[1], by=a]         # visualiza número de fila donde empieza cada grupo de “a”
#>    a V1
#> 1: 1  1
#> 2: 2  2

También se pueden generar nuevas columnas con el parámetro grp numerando por ejemplo los grupos de “a” de 1…n

DT[, grp := .GRP, by=a]
DT
#>     a b  c grp
#>  1: 1 A  1   1
#>  2: 2 B  2   2
#>  3: 1 C  3   1
#>  4: 2 D  4   2
#>  5: 1 E  5   1
#>  6: 2 F  6   2
#>  7: 1 G  7   1
#>  8: 2 H  8   2
#>  9: 1 I  9   1
#> 10: 2 J 10   2

También es posible remover la nueva columna creada

DT[, z:=NULL] # remueve columna creada
#> Warning in `[.data.table`(DT, , `:=`(z, NULL)): Column 'z'
#> does not exist to remove
DT
#>     a b  c grp
#>  1: 1 A  1   1
#>  2: 2 B  2   2
#>  3: 1 C  3   1
#>  4: 2 D  4   2
#>  5: 1 E  5   1
#>  6: 2 F  6   2
#>  7: 1 G  7   1
#>  8: 2 H  8   2
#>  9: 1 I  9   1
#> 10: 2 J 10   2

2.3 Manipulación de datos con dplyr

El paquete dplyr es quizás una de las herramientas más útiles en la actualidad para manipular, limpiar y resumir datos. Hace que la exploración y la manipulación de datos sean fáciles y rápidas. El paquete dplyr es uno de los paquetes más poderosos y populares de R. Fue escrito por Hadley Wickham, quien ha escrito otros paquetes del Tidyverse como ggplot2 y tidyr.Proporciona un gramática consistente, basada en verbos, ayudan a resolver los desafíos de manipulación de datos más comunes. A continuación, algunos ejemplos:

2.3.1 Algunas funciones básicas

Para la práctica con dplyr se utilizará un conjunto de datos del paquete palmerpenguins sobre mediciones morfológicas en tres especies de pinguinos, tomadas en la Estación Palmer, Antarctica.

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:data.table':
#> 
#>     between, first, last
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(palmerpenguins)
data(penguins)
str(penguins)
#> tibble [344 x 8] (S3: tbl_df/tbl/data.frame)
#>  $ species          : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
#>  $ island           : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
#>  $ bill_length_mm   : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
#>  $ bill_depth_mm    : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
#>  $ flipper_length_mm: int [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
#>  $ body_mass_g      : int [1:344] 3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
#>  $ sex              : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
#>  $ year             : int [1:344] 2007 2007 2007 2007 2007 2007 2007 2007 2007 2007 ...
glimpse(penguins)
#> Rows: 344
#> Columns: 8
#> $ species           <fct> Adelie, Adelie, Adelie, Adelie, ~
#> $ island            <fct> Torgersen, Torgersen, Torgersen,~
#> $ bill_length_mm    <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3~
#> $ bill_depth_mm     <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6~
#> $ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181~
#> $ body_mass_g       <int> 3750, 3800, 3250, NA, 3450, 3650~
#> $ sex               <fct> male, female, female, NA, female~
#> $ year              <int> 2007, 2007, 2007, 2007, 2007, 20~

sample_n(penguins,3)       # selección aleatoria de un número de datos
#> # A tibble: 3 x 8
#>   species island bill_length_mm bill_depth_mm
#>   <fct>   <fct>           <dbl>         <dbl>
#> 1 Adelie  Dream            32.1          15.5
#> 2 Gentoo  Biscoe           51.1          16.3
#> 3 Gentoo  Biscoe           50.5          15.9
#> # ... with 4 more variables: flipper_length_mm <int>,
#> #   body_mass_g <int>, sex <fct>, year <int>
sample_frac(penguins,0.01) # selección aleatoria de un porcentaje de datos
#> # A tibble: 3 x 8
#>   species island bill_length_mm bill_depth_mm
#>   <fct>   <fct>           <dbl>         <dbl>
#> 1 Gentoo  Biscoe           50            15.9
#> 2 Gentoo  Biscoe           42.6          13.7
#> 3 Adelie  Dream            37.2          18.1
#> # ... with 4 more variables: flipper_length_mm <int>,
#> #   body_mass_g <int>, sex <fct>, year <int>

2.3.2 Uso del operador binario %>% del paquete magrittr

Las capacidades de dplyr son potenciadas por algunas librerías como magrittr, que introduce al ambiente de R el operador pipe o también llamado operador de tuberías %>% (ctrl+shift+m), el cual una se leería en voz alta como un “y luego” que permite enlazar las diferentes acciones a realizar. Permite realizar operaciones complejas en bases de datos siguiendo una lógica simple y fluida. A continuación una comparación:

La manera tradicional en R sería así:

summary(subset(penguins,bill_length_mm >=40))
#>       species          island    bill_length_mm 
#>  Adelie   : 51   Biscoe   :139   Min.   :40.10  
#>  Chinstrap: 68   Dream    : 85   1st Qu.:43.33  
#>  Gentoo   :123   Torgersen: 18   Median :46.50  
#>                                  Mean   :46.66  
#>                                  3rd Qu.:49.77  
#>                                  Max.   :59.60  
#>  bill_depth_mm   flipper_length_mm  body_mass_g  
#>  Min.   :13.10   Min.   :176.0     Min.   :2700  
#>  1st Qu.:15.00   1st Qu.:195.0     1st Qu.:3812  
#>  Median :16.60   Median :208.0     Median :4400  
#>  Mean   :16.75   Mean   :206.1     Mean   :4468  
#>  3rd Qu.:18.57   3rd Qu.:216.0     3rd Qu.:5038  
#>  Max.   :21.50   Max.   :231.0     Max.   :6300  
#>      sex           year     
#>  female: 99   Min.   :2007  
#>  male  :138   1st Qu.:2007  
#>  NA's  :  5   Median :2008  
#>               Mean   :2008  
#>               3rd Qu.:2009  
#>               Max.   :2009

En dplyr con los %>% sería de esta manera:

penguins %>% 
  subset(bill_length_mm >=40) %>% 
  summary()
#>       species          island    bill_length_mm 
#>  Adelie   : 51   Biscoe   :139   Min.   :40.10  
#>  Chinstrap: 68   Dream    : 85   1st Qu.:43.33  
#>  Gentoo   :123   Torgersen: 18   Median :46.50  
#>                                  Mean   :46.66  
#>                                  3rd Qu.:49.77  
#>                                  Max.   :59.60  
#>  bill_depth_mm   flipper_length_mm  body_mass_g  
#>  Min.   :13.10   Min.   :176.0     Min.   :2700  
#>  1st Qu.:15.00   1st Qu.:195.0     1st Qu.:3812  
#>  Median :16.60   Median :208.0     Median :4400  
#>  Mean   :16.75   Mean   :206.1     Mean   :4468  
#>  3rd Qu.:18.57   3rd Qu.:216.0     3rd Qu.:5038  
#>  Max.   :21.50   Max.   :231.0     Max.   :6300  
#>      sex           year     
#>  female: 99   Min.   :2007  
#>  male  :138   1st Qu.:2007  
#>  NA's  :  5   Median :2008  
#>               Mean   :2008  
#>               3rd Qu.:2009  
#>               Max.   :2009

2.3.3 Uso de la función select()

Se utiliza la función select() para seleccionar columnas. La información está bien ordenada las columnas son equivalentes de las variables.

penguins %>% select(species,bill_length_mm,body_mass_g) #selecciona por nombre de columna
#> # A tibble: 344 x 3
#>    species bill_length_mm body_mass_g
#>    <fct>            <dbl>       <int>
#>  1 Adelie            39.1        3750
#>  2 Adelie            39.5        3800
#>  3 Adelie            40.3        3250
#>  4 Adelie            NA            NA
#>  5 Adelie            36.7        3450
#>  6 Adelie            39.3        3650
#>  7 Adelie            38.9        3625
#>  8 Adelie            39.2        4675
#>  9 Adelie            34.1        3475
#> 10 Adelie            42          4250
#> # ... with 334 more rows
penguins %>% select(c(1,3,6)) # selecciona por número de columna
#> # A tibble: 344 x 3
#>    species bill_length_mm body_mass_g
#>    <fct>            <dbl>       <int>
#>  1 Adelie            39.1        3750
#>  2 Adelie            39.5        3800
#>  3 Adelie            40.3        3250
#>  4 Adelie            NA            NA
#>  5 Adelie            36.7        3450
#>  6 Adelie            39.3        3650
#>  7 Adelie            38.9        3625
#>  8 Adelie            39.2        4675
#>  9 Adelie            34.1        3475
#> 10 Adelie            42          4250
#> # ... with 334 more rows
penguins %>% select(-c(island,sex,year)) # remueve por nombre de columna
#> # A tibble: 344 x 5
#>    species bill_length_mm bill_depth_mm flipper_length_mm
#>    <fct>            <dbl>         <dbl>             <int>
#>  1 Adelie            39.1          18.7               181
#>  2 Adelie            39.5          17.4               186
#>  3 Adelie            40.3          18                 195
#>  4 Adelie            NA            NA                  NA
#>  5 Adelie            36.7          19.3               193
#>  6 Adelie            39.3          20.6               190
#>  7 Adelie            38.9          17.8               181
#>  8 Adelie            39.2          19.6               195
#>  9 Adelie            34.1          18.1               193
#> 10 Adelie            42            20.2               190
#> # ... with 334 more rows, and 1 more variable:
#> #   body_mass_g <int>
penguins %>% select(-c(2,7:8)) # remueve por número de columna
#> # A tibble: 344 x 5
#>    species bill_length_mm bill_depth_mm flipper_length_mm
#>    <fct>            <dbl>         <dbl>             <int>
#>  1 Adelie            39.1          18.7               181
#>  2 Adelie            39.5          17.4               186
#>  3 Adelie            40.3          18                 195
#>  4 Adelie            NA            NA                  NA
#>  5 Adelie            36.7          19.3               193
#>  6 Adelie            39.3          20.6               190
#>  7 Adelie            38.9          17.8               181
#>  8 Adelie            39.2          19.6               195
#>  9 Adelie            34.1          18.1               193
#> 10 Adelie            42            20.2               190
#> # ... with 334 more rows, and 1 more variable:
#> #   body_mass_g <int>

Otras funciones adicionales para seleccionar columnas según características de los nombres de las variables

penguins %>% select(contains("bill")) # selecciona columnas que contienen "bill"
#> # A tibble: 344 x 2
#>    bill_length_mm bill_depth_mm
#>             <dbl>         <dbl>
#>  1           39.1          18.7
#>  2           39.5          17.4
#>  3           40.3          18  
#>  4           NA            NA  
#>  5           36.7          19.3
#>  6           39.3          20.6
#>  7           38.9          17.8
#>  8           39.2          19.6
#>  9           34.1          18.1
#> 10           42            20.2
#> # ... with 334 more rows
penguins %>% select(ends_with("mm"))  # selecciona columnas que terminan con "bill"
#> # A tibble: 344 x 3
#>    bill_length_mm bill_depth_mm flipper_length_mm
#>             <dbl>         <dbl>             <int>
#>  1           39.1          18.7               181
#>  2           39.5          17.4               186
#>  3           40.3          18                 195
#>  4           NA            NA                  NA
#>  5           36.7          19.3               193
#>  6           39.3          20.6               190
#>  7           38.9          17.8               181
#>  8           39.2          19.6               195
#>  9           34.1          18.1               193
#> 10           42            20.2               190
#> # ... with 334 more rows
penguins %>% select(species, starts_with("flipper")) # se puede seleccionar dentro de alguna variable
#> # A tibble: 344 x 2
#>    species flipper_length_mm
#>    <fct>               <int>
#>  1 Adelie                181
#>  2 Adelie                186
#>  3 Adelie                195
#>  4 Adelie                 NA
#>  5 Adelie                193
#>  6 Adelie                190
#>  7 Adelie                181
#>  8 Adelie                195
#>  9 Adelie                193
#> 10 Adelie                190
#> # ... with 334 more rows

Se pueden seleccionar y a la vez asignar un nuevo orden a las columnas

penguins %>% select(year,species,body_mass_g,species)
#> # A tibble: 344 x 3
#>     year species body_mass_g
#>    <int> <fct>         <int>
#>  1  2007 Adelie         3750
#>  2  2007 Adelie         3800
#>  3  2007 Adelie         3250
#>  4  2007 Adelie           NA
#>  5  2007 Adelie         3450
#>  6  2007 Adelie         3650
#>  7  2007 Adelie         3625
#>  8  2007 Adelie         4675
#>  9  2007 Adelie         3475
#> 10  2007 Adelie         4250
#> # ... with 334 more rows

Se puede usar la función wherepara distinguir por atributos de las columnas

penguins %>% select(where(is.factor), body_mass_g)
#> # A tibble: 344 x 4
#>    species island    sex    body_mass_g
#>    <fct>   <fct>     <fct>        <int>
#>  1 Adelie  Torgersen male          3750
#>  2 Adelie  Torgersen female        3800
#>  3 Adelie  Torgersen female        3250
#>  4 Adelie  Torgersen <NA>            NA
#>  5 Adelie  Torgersen female        3450
#>  6 Adelie  Torgersen male          3650
#>  7 Adelie  Torgersen female        3625
#>  8 Adelie  Torgersen male          4675
#>  9 Adelie  Torgersen <NA>          3475
#> 10 Adelie  Torgersen <NA>          4250
#> # ... with 334 more rows

2.3.4 Otras funciones para el manejo de columnas

La función rename() se utiliza para cambiar de nombre a las columnas

penguins %>% rename(isla = island)
#> # A tibble: 344 x 8
#>    species isla      bill_length_mm bill_depth_mm
#>    <fct>   <fct>              <dbl>         <dbl>
#>  1 Adelie  Torgersen           39.1          18.7
#>  2 Adelie  Torgersen           39.5          17.4
#>  3 Adelie  Torgersen           40.3          18  
#>  4 Adelie  Torgersen           NA            NA  
#>  5 Adelie  Torgersen           36.7          19.3
#>  6 Adelie  Torgersen           39.3          20.6
#>  7 Adelie  Torgersen           38.9          17.8
#>  8 Adelie  Torgersen           39.2          19.6
#>  9 Adelie  Torgersen           34.1          18.1
#> 10 Adelie  Torgersen           42            20.2
#> # ... with 334 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

Se puede cambiar la localización de las columnas con la función relocate() y los parámetros .before y .after

penguins %>%  relocate(year, .after = species)
#> # A tibble: 344 x 8
#>    species  year island    bill_length_mm bill_depth_mm
#>    <fct>   <int> <fct>              <dbl>         <dbl>
#>  1 Adelie   2007 Torgersen           39.1          18.7
#>  2 Adelie   2007 Torgersen           39.5          17.4
#>  3 Adelie   2007 Torgersen           40.3          18  
#>  4 Adelie   2007 Torgersen           NA            NA  
#>  5 Adelie   2007 Torgersen           36.7          19.3
#>  6 Adelie   2007 Torgersen           39.3          20.6
#>  7 Adelie   2007 Torgersen           38.9          17.8
#>  8 Adelie   2007 Torgersen           39.2          19.6
#>  9 Adelie   2007 Torgersen           34.1          18.1
#> 10 Adelie   2007 Torgersen           42            20.2
#> # ... with 334 more rows, and 3 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>

La función distinct()se utiliza para ver los factores dentro de una columna

penguins %>% distinct(year)
#> # A tibble: 3 x 1
#>    year
#>   <int>
#> 1  2007
#> 2  2008
#> 3  2009

Mientras que la función n_distinct()se utiliza para contar el número de factores diferentes dentro de una columna. Favor notar que en el siguiente ejemplo se utiliza la función pull()para escoger la columna a analizar.

penguins %>% pull(year) %>% n_distinct()
#> [1] 3

2.3.5 Uso de la función filter()

La función filter()se utiliza para seleccionar filas que cumplen con una determinada condición o criterio lógico. Notar que esta función permite el uso de dos tipos de sintaxis, con y sin el operador binario %>%.

library(palmerpenguins)
filter(penguins,species== "Adelie")
#> # A tibble: 152 x 8
#>    species island    bill_length_mm bill_depth_mm
#>    <fct>   <fct>              <dbl>         <dbl>
#>  1 Adelie  Torgersen           39.1          18.7
#>  2 Adelie  Torgersen           39.5          17.4
#>  3 Adelie  Torgersen           40.3          18  
#>  4 Adelie  Torgersen           NA            NA  
#>  5 Adelie  Torgersen           36.7          19.3
#>  6 Adelie  Torgersen           39.3          20.6
#>  7 Adelie  Torgersen           38.9          17.8
#>  8 Adelie  Torgersen           39.2          19.6
#>  9 Adelie  Torgersen           34.1          18.1
#> 10 Adelie  Torgersen           42            20.2
#> # ... with 142 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>
penguins %>% filter(bill_length_mm >= 50)
#> # A tibble: 57 x 8
#>    species island bill_length_mm bill_depth_mm
#>    <fct>   <fct>           <dbl>         <dbl>
#>  1 Gentoo  Biscoe           50            16.3
#>  2 Gentoo  Biscoe           50            15.2
#>  3 Gentoo  Biscoe           50.2          14.3
#>  4 Gentoo  Biscoe           50            15.3
#>  5 Gentoo  Biscoe           59.6          17  
#>  6 Gentoo  Biscoe           50.5          15.9
#>  7 Gentoo  Biscoe           50.5          15.9
#>  8 Gentoo  Biscoe           50.1          15  
#>  9 Gentoo  Biscoe           50.4          15.3
#> 10 Gentoo  Biscoe           54.3          15.7
#> # ... with 47 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

Un aspecto importante de la función filter() es que permite el uso de declaraciones lógicas y booleanas. Por ejemplo, se puede usar el operador & (del inglés AND) y el operador | (del inglés OR).

penguins %>% filter(bill_length_mm >= 50 & island == "Dream")
#> # A tibble: 31 x 8
#>    species   island bill_length_mm bill_depth_mm
#>    <fct>     <fct>           <dbl>         <dbl>
#>  1 Chinstrap Dream            50            19.5
#>  2 Chinstrap Dream            51.3          19.2
#>  3 Chinstrap Dream            52.7          19.8
#>  4 Chinstrap Dream            51.3          18.2
#>  5 Chinstrap Dream            51.3          19.9
#>  6 Chinstrap Dream            51.7          20.3
#>  7 Chinstrap Dream            52            18.1
#>  8 Chinstrap Dream            50.5          19.6
#>  9 Chinstrap Dream            50.3          20  
#> 10 Chinstrap Dream            58            17.8
#> # ... with 21 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

El operador binario %in% permite el filtrado con múltiples criterios, es decir, se pueden seleccionar diferentes items dentro de una columna o variable.

penguins %>% filter(island %in% c("Torgersen","Dream"))
#> # A tibble: 176 x 8
#>    species island    bill_length_mm bill_depth_mm
#>    <fct>   <fct>              <dbl>         <dbl>
#>  1 Adelie  Torgersen           39.1          18.7
#>  2 Adelie  Torgersen           39.5          17.4
#>  3 Adelie  Torgersen           40.3          18  
#>  4 Adelie  Torgersen           NA            NA  
#>  5 Adelie  Torgersen           36.7          19.3
#>  6 Adelie  Torgersen           39.3          20.6
#>  7 Adelie  Torgersen           38.9          17.8
#>  8 Adelie  Torgersen           39.2          19.6
#>  9 Adelie  Torgersen           34.1          18.1
#> 10 Adelie  Torgersen           42            20.2
#> # ... with 166 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

Notar que se escribe el nombre de la columna a analizar, el operador y luego el conjunto de items seleccionados. También se puede incluir otros operadores lógicos.

penguins %>% filter(island %in% c("Torgensen","Dream") & body_mass_g >= 3900)
#> # A tibble: 43 x 8
#>    species island bill_length_mm bill_depth_mm
#>    <fct>   <fct>           <dbl>         <dbl>
#>  1 Adelie  Dream            37.2          18.1
#>  2 Adelie  Dream            40.9          18.9
#>  3 Adelie  Dream            39.2          21.1
#>  4 Adelie  Dream            38.8          20  
#>  5 Adelie  Dream            39.8          19.1
#>  6 Adelie  Dream            40.8          18.4
#>  7 Adelie  Dream            44.1          19.7
#>  8 Adelie  Dream            39.6          18.8
#>  9 Adelie  Dream            42.3          21.2
#> 10 Adelie  Dream            38.3          19.2
#> # ... with 33 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

El parámetro near() permite escoger dentro de un rango de valores numéricos cercanos, estableciendo un valor de tolerancia.

penguins %>% filter(near(x = body_mass_g, y = 3500, tol = 500))
#> # A tibble: 154 x 8
#>    species island    bill_length_mm bill_depth_mm
#>    <fct>   <fct>              <dbl>         <dbl>
#>  1 Adelie  Torgersen           39.1          18.7
#>  2 Adelie  Torgersen           39.5          17.4
#>  3 Adelie  Torgersen           40.3          18  
#>  4 Adelie  Torgersen           36.7          19.3
#>  5 Adelie  Torgersen           39.3          20.6
#>  6 Adelie  Torgersen           38.9          17.8
#>  7 Adelie  Torgersen           34.1          18.1
#>  8 Adelie  Torgersen           37.8          17.1
#>  9 Adelie  Torgersen           37.8          17.3
#> 10 Adelie  Torgersen           41.1          17.6
#> # ... with 144 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

El parámetro between() permite escoger un rango de valores numéricos dentro de un límite específico.

penguins %>% filter(between(bill_length_mm, left = 43, right = 46))
#> # A tibble: 52 x 8
#>    species island    bill_length_mm bill_depth_mm
#>    <fct>   <fct>              <dbl>         <dbl>
#>  1 Adelie  Torgersen           46            21.5
#>  2 Adelie  Dream               44.1          19.7
#>  3 Adelie  Torgersen           45.8          18.9
#>  4 Adelie  Dream               43.2          18.5
#>  5 Adelie  Biscoe              43.2          19  
#>  6 Adelie  Biscoe              45.6          20.3
#>  7 Adelie  Torgersen           44.1          18  
#>  8 Adelie  Torgersen           43.1          19.2
#>  9 Gentoo  Biscoe              45.4          14.6
#> 10 Gentoo  Biscoe              43.3          13.4
#> # ... with 42 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

Ejercicio 1. Dentro de la base de datos penguins, filtrar las de la especie “Chinstrap”, que tengan una masa corporal menor a 3100 g, y un longitud de pico mayor o igual a 40. Además, seleccionar las variables que contengan “mm” y las que sean factores. ¿De qué isla provienen y qué sexo tienen?


2.3.6 Manejo de NAs

Los NAs son datos faltantes en celdas específicas. Se utiliza ese acrónimo para denominar datos Not Available en inglés. Hay distintas maneras de cómo eliminarlos o incluso reemplazarlos. La manera más simple, pero más grocera, es usar la función drop_na()que remueve toda la fila donde se encuentre al menos un NA.

library(tidyr)
penguins %>% drop_na()
#> # A tibble: 333 x 8
#>    species island    bill_length_mm bill_depth_mm
#>    <fct>   <fct>              <dbl>         <dbl>
#>  1 Adelie  Torgersen           39.1          18.7
#>  2 Adelie  Torgersen           39.5          17.4
#>  3 Adelie  Torgersen           40.3          18  
#>  4 Adelie  Torgersen           36.7          19.3
#>  5 Adelie  Torgersen           39.3          20.6
#>  6 Adelie  Torgersen           38.9          17.8
#>  7 Adelie  Torgersen           39.2          19.6
#>  8 Adelie  Torgersen           41.1          17.6
#>  9 Adelie  Torgersen           38.6          21.2
#> 10 Adelie  Torgersen           34.6          21.1
#> # ... with 323 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

Otra manera es remover los valores faltantes dentro de una columna

penguins %>% drop_na(bill_depth_mm)
#> # A tibble: 342 x 8
#>    species island    bill_length_mm bill_depth_mm
#>    <fct>   <fct>              <dbl>         <dbl>
#>  1 Adelie  Torgersen           39.1          18.7
#>  2 Adelie  Torgersen           39.5          17.4
#>  3 Adelie  Torgersen           40.3          18  
#>  4 Adelie  Torgersen           36.7          19.3
#>  5 Adelie  Torgersen           39.3          20.6
#>  6 Adelie  Torgersen           38.9          17.8
#>  7 Adelie  Torgersen           39.2          19.6
#>  8 Adelie  Torgersen           34.1          18.1
#>  9 Adelie  Torgersen           42            20.2
#> 10 Adelie  Torgersen           37.8          17.1
#> # ... with 332 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

Se pueden remplazar los NAs con la media de los valores de la columna

penguins %>% 
  replace_na(
    list(bill_length_mm=mean(penguins$bill_length_mm,na.rm=TRUE)))
#> # A tibble: 344 x 8
#>    species island    bill_length_mm bill_depth_mm
#>    <fct>   <fct>              <dbl>         <dbl>
#>  1 Adelie  Torgersen           39.1          18.7
#>  2 Adelie  Torgersen           39.5          17.4
#>  3 Adelie  Torgersen           40.3          18  
#>  4 Adelie  Torgersen           43.9          NA  
#>  5 Adelie  Torgersen           36.7          19.3
#>  6 Adelie  Torgersen           39.3          20.6
#>  7 Adelie  Torgersen           38.9          17.8
#>  8 Adelie  Torgersen           39.2          19.6
#>  9 Adelie  Torgersen           34.1          18.1
#> 10 Adelie  Torgersen           42            20.2
#> # ... with 334 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

2.3.7 Uso de la función arrange()

Esta función permite ordenar de manera ascendente o descendente los elementos de una columna, sea alfabéticamente o numéricamente.

penguins %>% arrange(body_mass_g)
#> # A tibble: 344 x 8
#>    species   island    bill_length_mm bill_depth_mm
#>    <fct>     <fct>              <dbl>         <dbl>
#>  1 Chinstrap Dream               46.9          16.6
#>  2 Adelie    Biscoe              36.5          16.6
#>  3 Adelie    Biscoe              36.4          17.1
#>  4 Adelie    Biscoe              34.5          18.1
#>  5 Adelie    Dream               33.1          16.1
#>  6 Adelie    Torgersen           38.6          17  
#>  7 Chinstrap Dream               43.2          16.6
#>  8 Adelie    Biscoe              37.9          18.6
#>  9 Adelie    Dream               37.5          18.9
#> 10 Adelie    Dream               37            16.9
#> # ... with 334 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>
penguins %>% arrange(desc(body_mass_g))
#> # A tibble: 344 x 8
#>    species island bill_length_mm bill_depth_mm
#>    <fct>   <fct>           <dbl>         <dbl>
#>  1 Gentoo  Biscoe           49.2          15.2
#>  2 Gentoo  Biscoe           59.6          17  
#>  3 Gentoo  Biscoe           51.1          16.3
#>  4 Gentoo  Biscoe           48.8          16.2
#>  5 Gentoo  Biscoe           45.2          16.4
#>  6 Gentoo  Biscoe           49.8          15.9
#>  7 Gentoo  Biscoe           48.4          14.6
#>  8 Gentoo  Biscoe           49.3          15.7
#>  9 Gentoo  Biscoe           55.1          16  
#> 10 Gentoo  Biscoe           49.5          16.2
#> # ... with 334 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

Para escoger los individuos que presentaron los cinco valores más altos de una variables se podría hacer los siguiente. El parámetro wt se utiliza para definir la columna.

penguins %>%  
  arrange(desc(bill_length_mm)) %>% 
  top_n(n=5, wt=bill_length_mm)
#> # A tibble: 5 x 8
#>   species   island bill_length_mm bill_depth_mm
#>   <fct>     <fct>           <dbl>         <dbl>
#> 1 Gentoo    Biscoe           59.6          17  
#> 2 Chinstrap Dream            58            17.8
#> 3 Gentoo    Biscoe           55.9          17  
#> 4 Chinstrap Dream            55.8          19.8
#> 5 Gentoo    Biscoe           55.1          16  
#> # ... with 4 more variables: flipper_length_mm <int>,
#> #   body_mass_g <int>, sex <fct>, year <int>

También se pueden realizar operaciones más complejas que involucran el uso de varios verbos de dplyr encadenados por el operador pipe %>%. Se puede, por ejemplo, seleccionar las columnas que contenga el texto “mm”, que sean numéricas y que tenga una media menor a 50 (removiendo los NAs), para luego ordenarlos de manera descendente según los valores de la variable bill_length_mm

penguins %>% 
  select(contains("mm")) %>% 
  select(where(is.numeric))%>% 
  select(where(~mean(., na.rm=TRUE) < 50))%>% 
  arrange(desc(bill_depth_mm))
#> # A tibble: 344 x 2
#>    bill_length_mm bill_depth_mm
#>             <dbl>         <dbl>
#>  1           46            21.5
#>  2           38.6          21.2
#>  3           42.3          21.2
#>  4           34.6          21.1
#>  5           39.2          21.1
#>  6           41.3          21.1
#>  7           54.2          20.8
#>  8           42.5          20.7
#>  9           39.6          20.7
#> 10           52            20.7
#> # ... with 334 more rows

2.3.8 Uso de la función summarise()

Se utiliza esta función para resumir los datos mediante operaciones de cálculo básicas

penguins %>% 
  summarise(
    avg_bill_length = mean(bill_length_mm, na.rm=TRUE), 
    avg_body_mass = mean(body_mass_g,na.rm=TRUE))
#> # A tibble: 1 x 2
#>   avg_bill_length avg_body_mass
#>             <dbl>         <dbl>
#> 1            43.9         4202.

Se puede utilizar la función across() dentro de summarise()para aplicar funciones a múltiples columnas. Notar que se usa el parámetro .cols para seleccionar columnas y el parámetro .fns para asignar la función a realizar.

penguins %>% 
  summarise(
    across(.cols = bill_length_mm:body_mass_g, 
           .fns = ~mean(., na.rm = TRUE)))
#> # A tibble: 1 x 4
#>   bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
#>            <dbl>         <dbl>             <dbl>       <dbl>
#> 1           43.9          17.2              201.       4202.

En la gramática de dplyr el uso del punto antes del parámetro permite diferenciarlo respecto a lo que podría ser una variable con el mismo nombre. Además, es importante señalar que la sintaxis “~mean(.)” se utiliza para especificar el cálculo de la media es para cada una de las variables seleccionadas. Por supuesto, también se pueden incluir diferentes funciones a la vez.

penguins %>% 
  drop_na() %>% 
  summarise(
    across(.cols = bill_length_mm:body_mass_g, 
           .fns = c(mean,sum)))
#> # A tibble: 1 x 8
#>   bill_length_mm_1 bill_length_mm_2 bill_depth_mm_1
#>              <dbl>            <dbl>           <dbl>
#> 1             44.0           14650.            17.2
#> # ... with 5 more variables: bill_depth_mm_2 <dbl>,
#> #   flipper_length_mm_1 <dbl>, flipper_length_mm_2 <int>,
#> #   body_mass_g_1 <dbl>, body_mass_g_2 <int>

Incluso, se le puede asignar un nuevo nombre a las columnas generadas

penguins %>% 
  drop_na() %>% 
  summarise(
    across(.cols = c(where(is.numeric),-year), 
           .fns = list(avg=mean,sd=sd),
           .names = "{.col}.{.fn}"))
#> # A tibble: 1 x 8
#>   bill_length_mm.avg bill_length_mm.sd bill_depth_mm.avg
#>                <dbl>             <dbl>             <dbl>
#> 1               44.0              5.47              17.2
#> # ... with 5 more variables: bill_depth_mm.sd <dbl>,
#> #   flipper_length_mm.avg <dbl>,
#> #   flipper_length_mm.sd <dbl>, body_mass_g.avg <dbl>,
#> #   body_mass_g.sd <dbl>

También se pueden realizar operaciones más complejas con funciones personalizadas

penguins %>% 
  summarise(
    across(
      .cols = c(where(is.numeric),-year),
      .fns = function(x) mean(x,na.rm=TRUE)-sd(x,na.rm=TRUE)))
#> # A tibble: 1 x 4
#>   bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
#>            <dbl>         <dbl>             <dbl>       <dbl>
#> 1           38.5          15.2              187.       3400.

2.3.9 Uso de la función group_by()

Una de las funciones más útilizadas en dplyr. Permite agrupar diferentes variables categóricas, a partir de las cuales se pueden realizar diferentes operaciones de cálculo

penguins %>% 
  group_by(species) %>% 
  summarise(avg_mass = mean(body_mass_g, na.rm=TRUE)) %>% 
  arrange(desc(avg_mass))
#> # A tibble: 3 x 2
#>   species   avg_mass
#>   <fct>        <dbl>
#> 1 Gentoo       5076.
#> 2 Chinstrap    3733.
#> 3 Adelie       3701.

Se pueden realizar agrupaciones y cálculos para diferentes variables

penguins %>% 
  group_by(species) %>% 
  summarise(
    across(
      .cols = c(where(is.numeric),-year),
      .fns = ~mean(., na.rm = TRUE)))
#> # A tibble: 3 x 5
#>   species   bill_length_mm bill_depth_mm flipper_length_mm
#>   <fct>              <dbl>         <dbl>             <dbl>
#> 1 Adelie              38.8          18.3              190.
#> 2 Chinstrap           48.8          18.4              196.
#> 3 Gentoo              47.5          15.0              217.
#> # ... with 1 more variable: body_mass_g <dbl>

Se pueden realizar agrupaciones con dos o más variables categóricas

penguins %>% 
  group_by(species,island) %>% 
  summarise(avg_flipper = mean(flipper_length_mm,na.rm=TRUE)) %>% 
  arrange(desc(avg_flipper))
#> # A tibble: 5 x 3
#> # Groups:   species [3]
#>   species   island    avg_flipper
#>   <fct>     <fct>           <dbl>
#> 1 Gentoo    Biscoe           217.
#> 2 Chinstrap Dream            196.
#> 3 Adelie    Torgersen        191.
#> 4 Adelie    Dream            190.
#> 5 Adelie    Biscoe           189.

Con el parámetro n() se pueden contar los elementos dentro de las variables agrupadas

penguins %>% 
  group_by(species,island) %>% 
  summarise(n())
#> # A tibble: 5 x 3
#> # Groups:   species [3]
#>   species   island    `n()`
#>   <fct>     <fct>     <int>
#> 1 Adelie    Biscoe       44
#> 2 Adelie    Dream        56
#> 3 Adelie    Torgersen    52
#> 4 Chinstrap Dream        68
#> 5 Gentoo    Biscoe      124

Otra forma rápida de realizar conteos es con la función count() donde se suprime el uso las funciones group_by()y summarise(). La desventaja es que solamente aplica para una sola variable.

penguins %>% count(species, sort = TRUE)
#> # A tibble: 3 x 2
#>   species       n
#>   <fct>     <int>
#> 1 Adelie      152
#> 2 Gentoo      124
#> 3 Chinstrap    68

También se puede hacer construcción de modelos lineales automatizada. En la lógica gramatical de Tidyverse se utilizan verbos para ejecutar las diferentes acciones. Esto incluye las funciones auxiliares del propio dplyr, de R-base o de otros paquetes. Incluso, si no existe una función apropiada, se puede utilizar la función do().

penguins %>% 
  group_by(species) %>% 
  do(mod = lm(body_mass_g ~ bill_length_mm, data = .)) %>% 
       summarise(rsq = summary(mod)$r.squared)
#> # A tibble: 3 x 1
#>     rsq
#>   <dbl>
#> 1 0.301
#> 2 0.264
#> 3 0.448

Ejercicio 2. De la base de datos iris, calcule la media y la desviación estándar por especie, pero solamente de las variables que contengan la palabra “Length”. Asegúrese que los nombres de las columnas especifiquen el tipo de valor calculado.


2.3.10 Uso de la función mutate()

Permite crear nuevas variables y realizar operaciones matemáticas entre ellas.

penguins %>% mutate(mass_kg = body_mass_g / 1000, .before=bill_length_mm)
#> # A tibble: 344 x 9
#>    species island    mass_kg bill_length_mm bill_depth_mm
#>    <fct>   <fct>       <dbl>          <dbl>         <dbl>
#>  1 Adelie  Torgersen    3.75           39.1          18.7
#>  2 Adelie  Torgersen    3.8            39.5          17.4
#>  3 Adelie  Torgersen    3.25           40.3          18  
#>  4 Adelie  Torgersen   NA              NA            NA  
#>  5 Adelie  Torgersen    3.45           36.7          19.3
#>  6 Adelie  Torgersen    3.65           39.3          20.6
#>  7 Adelie  Torgersen    3.62           38.9          17.8
#>  8 Adelie  Torgersen    4.68           39.2          19.6
#>  9 Adelie  Torgersen    3.48           34.1          18.1
#> 10 Adelie  Torgersen    4.25           42            20.2
#> # ... with 334 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

Se utiliza la función transmute()cuando se quiere generar y mostrar solamente la columna resultante de una operación matemática entre variables

penguins %>% 
  transmute(bill_ratio = bill_length_mm/bill_depth_mm)
#> # A tibble: 344 x 1
#>    bill_ratio
#>         <dbl>
#>  1       2.09
#>  2       2.27
#>  3       2.24
#>  4      NA   
#>  5       1.90
#>  6       1.91
#>  7       2.19
#>  8       2   
#>  9       1.88
#> 10       2.08
#> # ... with 334 more rows

Con el parámetro if_else() se pueden definir categorías arbitrarias de clasificación dentro de variables numéricas. Esta función literalmente se leería como “si cumple con la siguiente característica lo clasifica como tal cosa, sino, como tal otra cosa”.

library(tidyr)
penguins %>% 
  drop_na() %>% 
  mutate(size = if_else(body_mass_g>4000,"BIG","SMALL")) %>% 
  count(size)
#> # A tibble: 2 x 2
#>   size      n
#>   <chr> <int>
#> 1 BIG     167
#> 2 SMALL   166

Para concatenar dos variables, asignarles nuevo nombre y posición, se puede hacer los siguiente

sp_si =
  penguins %>% 
  mutate(
    sp_sitio=paste(
      species, 
      island, 
      sep="_"),
    .before =bill_length_mm)
sp_si
#> # A tibble: 344 x 9
#>    species island    sp_sitio   bill_length_mm bill_depth_mm
#>    <fct>   <fct>     <chr>               <dbl>         <dbl>
#>  1 Adelie  Torgersen Adelie_To~           39.1          18.7
#>  2 Adelie  Torgersen Adelie_To~           39.5          17.4
#>  3 Adelie  Torgersen Adelie_To~           40.3          18  
#>  4 Adelie  Torgersen Adelie_To~           NA            NA  
#>  5 Adelie  Torgersen Adelie_To~           36.7          19.3
#>  6 Adelie  Torgersen Adelie_To~           39.3          20.6
#>  7 Adelie  Torgersen Adelie_To~           38.9          17.8
#>  8 Adelie  Torgersen Adelie_To~           39.2          19.6
#>  9 Adelie  Torgersen Adelie_To~           34.1          18.1
#> 10 Adelie  Torgersen Adelie_To~           42            20.2
#> # ... with 334 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

La función separate()se puede utilizar para separar elementos dentro de una variable categórica

sp_si %>% 
  separate(col = sp_sitio,
           into= c("especie", "isla"),
           sep="_")
#> # A tibble: 344 x 10
#>    species island especie isla  bill_length_mm bill_depth_mm
#>    <fct>   <fct>  <chr>   <chr>          <dbl>         <dbl>
#>  1 Adelie  Torge~ Adelie  Torg~           39.1          18.7
#>  2 Adelie  Torge~ Adelie  Torg~           39.5          17.4
#>  3 Adelie  Torge~ Adelie  Torg~           40.3          18  
#>  4 Adelie  Torge~ Adelie  Torg~           NA            NA  
#>  5 Adelie  Torge~ Adelie  Torg~           36.7          19.3
#>  6 Adelie  Torge~ Adelie  Torg~           39.3          20.6
#>  7 Adelie  Torge~ Adelie  Torg~           38.9          17.8
#>  8 Adelie  Torge~ Adelie  Torg~           39.2          19.6
#>  9 Adelie  Torge~ Adelie  Torg~           34.1          18.1
#> 10 Adelie  Torge~ Adelie  Torg~           42            20.2
#> # ... with 334 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

2.3.11 Fusión de bases de datos

Dentro del paquete dplyr existen numerosas funciones para unir bases de datos. Una de las funciones utilizadas para unir multiples bases de datos por filas para obtener una sola ordenándolas es con bind_rows(). Para el ejemplo dividiremos la base de datos penguins en dos imaginando que serían dos bases de datos distintas.

mitad1 <- penguins[1:172, ]
mitad2 <- penguins[173:344, ]
bind_rows(mitad1, mitad2)
#> # A tibble: 344 x 8
#>    species island    bill_length_mm bill_depth_mm
#>    <fct>   <fct>              <dbl>         <dbl>
#>  1 Adelie  Torgersen           39.1          18.7
#>  2 Adelie  Torgersen           39.5          17.4
#>  3 Adelie  Torgersen           40.3          18  
#>  4 Adelie  Torgersen           NA            NA  
#>  5 Adelie  Torgersen           36.7          19.3
#>  6 Adelie  Torgersen           39.3          20.6
#>  7 Adelie  Torgersen           38.9          17.8
#>  8 Adelie  Torgersen           39.2          19.6
#>  9 Adelie  Torgersen           34.1          18.1
#> 10 Adelie  Torgersen           42            20.2
#> # ... with 334 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

También se puede usar una lista enumerando las bases de datos a unir. De hecho, ese sería el proceso más recomendado para unir más de los bases de datos.

bind_rows(list(mitad1, mitad2))
#> # A tibble: 344 x 8
#>    species island    bill_length_mm bill_depth_mm
#>    <fct>   <fct>              <dbl>         <dbl>
#>  1 Adelie  Torgersen           39.1          18.7
#>  2 Adelie  Torgersen           39.5          17.4
#>  3 Adelie  Torgersen           40.3          18  
#>  4 Adelie  Torgersen           NA            NA  
#>  5 Adelie  Torgersen           36.7          19.3
#>  6 Adelie  Torgersen           39.3          20.6
#>  7 Adelie  Torgersen           38.9          17.8
#>  8 Adelie  Torgersen           39.2          19.6
#>  9 Adelie  Torgersen           34.1          18.1
#> 10 Adelie  Torgersen           42            20.2
#> # ... with 334 more rows, and 4 more variables:
#> #   flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
#> #   year <int>

Para unir columnas se utiliza la función bind_cols(). Una salvedad importante es las bases de datos tienen que tener el mismo número de filas para ser fusionadas.

bind_cols(list(mitad1, mitad2))
#> # A tibble: 172 x 16
#>    species...1 island...2 bill_length_mm..~ bill_depth_mm..~
#>    <fct>       <fct>                  <dbl>            <dbl>
#>  1 Adelie      Torgersen               39.1             18.7
#>  2 Adelie      Torgersen               39.5             17.4
#>  3 Adelie      Torgersen               40.3             18  
#>  4 Adelie      Torgersen               NA               NA  
#>  5 Adelie      Torgersen               36.7             19.3
#>  6 Adelie      Torgersen               39.3             20.6
#>  7 Adelie      Torgersen               38.9             17.8
#>  8 Adelie      Torgersen               39.2             19.6
#>  9 Adelie      Torgersen               34.1             18.1
#> 10 Adelie      Torgersen               42               20.2
#> # ... with 162 more rows, and 12 more variables:
#> #   flipper_length_mm...5 <int>, body_mass_g...6 <int>,
#> #   sex...7 <fct>, year...8 <int>, species...9 <fct>,
#> #   island...10 <fct>, bill_length_mm...11 <dbl>,
#> #   bill_depth_mm...12 <dbl>, flipper_length_mm...13 <int>,
#> #   body_mass_g...14 <int>, sex...15 <fct>, year...16 <int>

Dentro de dplyr hay varias funciones asociadas al verbo join.

La función inner_join() crea una nueva tabla a partir de coincidencias.

df1 = data.frame(ID = c(1, 2, 3, 4, 5),
                 w = c('a', 'b', 'c', 'd', 'e'),
                 z=letters[1:5])
df2 = data.frame(ID = c(1, 7, 3, 6, 8),
                 a = c('z', 'b', 'k', 'd', 'l'),
                 d =letters[2:6])
inner_join(df1, df2, by = "ID")
#>   ID w z a d
#> 1  1 a a z b
#> 2  3 c c k d

La función left_join() devuelve todas las filas de la tabla izquierda, incluso si no hay coincidencias. Imputa NA cuando hay información faltante

left_join(df1, df2, by = "ID")
#>   ID w z    a    d
#> 1  1 a a    z    b
#> 2  2 b b <NA> <NA>
#> 3  3 c c    k    d
#> 4  4 d d <NA> <NA>
#> 5  5 e e <NA> <NA>

Se puede combinar verticalmente usando intersect()donde se selecciona filas únicas que son comunes a ambas tablas

mtcars$model <- rownames(mtcars)
first <- mtcars[1:20, ]
second <- mtcars[10:32, ]
intersect(first, second)
#>                      mpg cyl  disp  hp drat    wt  qsec vs
#> Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1
#> Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1
#> Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0
#> Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0
#> Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0
#> Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0
#> Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0
#> Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0
#> Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1
#> Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1
#> Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1
#>                     am gear carb               model
#> Merc 280             0    4    4            Merc 280
#> Merc 280C            0    4    4           Merc 280C
#> Merc 450SE           0    3    3          Merc 450SE
#> Merc 450SL           0    3    3          Merc 450SL
#> Merc 450SLC          0    3    3         Merc 450SLC
#> Cadillac Fleetwood   0    3    4  Cadillac Fleetwood
#> Lincoln Continental  0    3    4 Lincoln Continental
#> Chrysler Imperial    0    3    4   Chrysler Imperial
#> Fiat 128             1    4    1            Fiat 128
#> Honda Civic          1    4    2         Honda Civic
#> Toyota Corolla       1    4    1      Toyota Corolla

2.4 Reordenamiento de datos con Tidyr

Los datos tidy generalmente existen en dos formas: 1)datos a lo ancho y 2)datos a lo largo. Ambos tipos de datos se usan y se necesitan en el análisis de datos y, afortunadamente, existen herramientas que pueden llevar de un formato a otro. Obtener los datos en el formato correcto será crucial más adelante al resumir los datos y visualizarlos.

Los datos a lo ancho tienen una columna para cada variable y una fila para cada observación. Los datos a menudo se ingresan y almacenan de esta manera. Los datos ordenados a lo largo, por otro lado, tienen una columna que indica el tipo de variable contenida en esa fila y luego, en una columna separada, el valor de esa variable. Cada fila contiene una sola observación para una sola variable. Sigue siendo un conjunto de datos ordenado, pero la información se almacena en un formato largo.

Si bien los formatos de datos largos son menos legibles que los datos a lo ancho, a menudo es mucho más fácil trabajar con estos últimos durante el análisis. Por lo tanto, para pasar de cómo se almacenan los datos a menudo (a lo ancho) a trabajar con los datos durante el análisis (a lo largo), podemos utilizar algunas funciones del paquete tidyr

2.4.1 Uso de la función pivot_longer()

Como los datos a menudo se almacenan en formatos a lo ancho, es probable que la función pivot_longer() sea muy utilizada. Esto le permitirá obtener los datos en un formato a lo largo que será fácil de usar para análisis posteriores. De ejemplo usaremos la base de datos airquality sobre mediciones en la calidad del aire en Nueva York en 1973. Notar que se puede usar el parámetro names_to para asignar un nombre a la nueva columna generada y el parámetro values_to para la columna con los valores.

data("airquality")
head(airquality)
#>   Ozone Solar.R Wind Temp Month Day
#> 1    41     190  7.4   67     5   1
#> 2    36     118  8.0   72     5   2
#> 3    12     149 12.6   74     5   3
#> 4    18     313 11.5   62     5   4
#> 5    NA      NA 14.3   56     5   5
#> 6    28      NA 14.9   66     5   6
largo <- airquality %>%
  pivot_longer(c(Ozone, Solar.R, Wind, Temp), 
                 names_to = "variable", values_to = "value")
largo
#> # A tibble: 612 x 4
#>    Month   Day variable value
#>    <int> <int> <chr>    <dbl>
#>  1     5     1 Ozone     41  
#>  2     5     1 Solar.R  190  
#>  3     5     1 Wind       7.4
#>  4     5     1 Temp      67  
#>  5     5     2 Ozone     36  
#>  6     5     2 Solar.R  118  
#>  7     5     2 Wind       8  
#>  8     5     2 Temp      72  
#>  9     5     3 Ozone     12  
#> 10     5     3 Solar.R  149  
#> # ... with 602 more rows

2.4.2 Uso de la función pivot_wider()

Para volver del formato largo a su forma original puede usar pivot_wider(). Se deben especificar dos columnas: la columna que contiene los nombres de lo que deberían ser sus columnas en formato ancho, usando el parámetro names_from y la columna que contiene los valores values_from.

ancho <- largo %>%
  pivot_wider(names_from = "variable", 
              values_from = "value")
ancho
#> # A tibble: 153 x 6
#>    Month   Day Ozone Solar.R  Wind  Temp
#>    <int> <int> <dbl>   <dbl> <dbl> <dbl>
#>  1     5     1    41     190   7.4    67
#>  2     5     2    36     118   8      72
#>  3     5     3    12     149  12.6    74
#>  4     5     4    18     313  11.5    62
#>  5     5     5    NA      NA  14.3    56
#>  6     5     6    28      NA  14.9    66
#>  7     5     7    23     299   8.6    65
#>  8     5     8    19      99  13.8    59
#>  9     5     9     8      19  20.1    61
#> 10     5    10    NA     194   8.6    69
#> # ... with 143 more rows

2.4.3 Aplicaciones más elaboradas de las tablas dinámicas

En el siguiente ejemplo usaremos pivot_longer()para resumir y visualiar datos de vegetación en los prados de dunas en Holanda contenida en el paquete para manejo estadistico de datos ecológicos vegan. Este set de datos se compone de dos tablas, la primera contiene la abundancia de 30 especies en 20 sitios y la segunda contiene la información ambiental correspondiente a cada sitio (Jongman et al. 1987).

library(vegan)
library(tidyr)
data("dune","dune.env") 
dune2= bind_cols(list(dune, dune.env))

tab.din <- dune2 %>%
  pivot_longer(Achimill:Callcusp, 
               names_to = "especie", values_to = "abundancia")
tab.din
#> # A tibble: 600 x 7
#>       A1 Moisture Management Use   Manure especie abundancia
#>    <dbl> <ord>    <fct>      <ord> <ord>  <chr>        <dbl>
#>  1   2.8 1        SF         Hayp~ 4      Achimi~          1
#>  2   2.8 1        SF         Hayp~ 4      Agrost~          0
#>  3   2.8 1        SF         Hayp~ 4      Airapr~          0
#>  4   2.8 1        SF         Hayp~ 4      Alopge~          0
#>  5   2.8 1        SF         Hayp~ 4      Anthod~          0
#>  6   2.8 1        SF         Hayp~ 4      Bellpe~          0
#>  7   2.8 1        SF         Hayp~ 4      Bromho~          0
#>  8   2.8 1        SF         Hayp~ 4      Chenal~          0
#>  9   2.8 1        SF         Hayp~ 4      Cirsar~          0
#> 10   2.8 1        SF         Hayp~ 4      Comapa~          0
#> # ... with 590 more rows

Luego se puede visualizar de la siguiente manera

library(ggplot2)
ggplot(data = tab.din,aes(x = especie,y = abundancia)) +
  geom_point(size=0.5) + xlab(label = "especie") +
  facet_grid(Management~ ., scales = "free_x", space = "free_x")+
  theme_bw() +
  theme(axis.text.x = element_text(size=7,angle = 90,vjust = 0.5, hjust=1),
        axis.text.y = element_text(size = 6),
        axis.title.y = element_text(size = 8),
        axis.title.x = element_blank())
2

Figure 2.1: 2

2.5 Programación funcional con el paquete purr

La programación funcional es un enfoque de programación donde el código evaluado se trata como una función matemática. A menudo se promociona y utiliza debido al hecho de que se puede escribir un código más limpio, más corto y con menos redundancias. En última instancia, el objetivo es tener un código más simple que minimice el tiempo necesario para la depuración, prueba y mantenimiento. En el enfoque básico de R, se utilizaban loops (bucles) para realizar iteraciones, por ejemplo, de una operación en cada columna de un data.frame. Este enfoque ha sido criticado por ser lento e innecesario.

2.5.1 Enfoque clásico de loops

En el siguiente ejemplo se puede ver la utilización de loops para calcular la mediana en la base de datos iris.

library(dplyr)
data(iris)
iris2= select(iris,1:4)
output <- vector("double", ncol(iris2)) 

for (i in seq_along(iris2)) {          
  output[[i]] <- median(iris2[[i]])      
}
output
#> [1] 5.80 3.00 4.35 1.30

Otra alternativa es crear una función. La ventaja de crear funciones es que esta se puede utilizar para cualquier otro data.frame, es decir, permite generalizar el código.

col_median <- function(df) {
  output <- vector("double", length(df))
  for (i in seq_along(df)) {
    output[i] <- median(df[[i]])
  }
  output
}

col_median(iris2) # Llama y ejecuta función
#> [1] 5.80 3.00 4.35 1.30

2.5.2 El enfoque de purrr

purrr mejora el conjunto de herramientas de programación funcional de R al proporcionar un conjunto completo y consistente de herramientas para trabajar con funciones y vectores. Por ejemplo, la familia de funciones map() permiten reemplazar muchos “for loops” con código que es más breve y más fácil de leer.

En general, la función map()permite llevar a cabo la operación de interés para una sola ocurrencia (p.e. calcular la mediana para una sola columna). Luego, realiza esa operación en todo el data.frame, por lo que a esta función también se le denomina como un “iterador”.

Otras funciones más específicas de la familia map() son las siguientes:

El uso genérico de la familia de funciones map()es el siguiente:

map(.x, .f, ...)
map(INPUT, FUNCTION_TO_APPLY, OPTIONAL_OTHER_STUFF)

Para el caso en particular que estabamos analizando, usamos la función map_dbl() para iterar sobre las columnas y calcular la mediana. Notar que incluso se muestra el nombre de la variable en la salida.

library(purrr)
map_dbl(iris2, median)
#> Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
#>         5.80         3.00         4.35         1.30

Además de la función principal, se pueden pasar argumentos adicionales. Estos van después de la función principal especificada. Por ejemplo, a continuación, especificamos que nos gustaría eliminar los NA, especificando un argumento que se pasará como parte de la función mean()

data("airquality")
map_dbl(airquality, mean, na.rm = TRUE)
#>      Ozone    Solar.R       Wind       Temp      Month 
#>  42.129310 185.931507   9.957516  77.882353   6.993464 
#>        Day 
#>  15.803922

2.5.3 Manejo de múltiples archivos

Hasta el momento hemos trabajado generalmente con datos en un solo archivo. Sin embargo, muchas veces hay que trabajar con archivos que están en múltiples carpetas.

En el siguiente ejemplo veremos cómo se puede separar un data.frame en múltiples archivos que se pueden guardar en un nuevo directorio. Luego volveremos a combinar estos múltiples archivos en un solo data.frame. Para esto estaremos utilizando los paquetes fs, purrr y readr y la base de datos iris.

library(dplyr)
library(readr)
library(fs)
library(purrr)

carpeta= dir_create("iris_sep/")
iris %>%
  group_by(Species) %>%
  group_walk(~ write.csv(.x, file = file.path(carpeta, paste0(.y$Species,".csv"))))

csv <- dir_ls(path = carpeta, regexp = "\\.csv$")
csv
#> iris_sep/setosa.csv     iris_sep/versicolor.csv 
#> iris_sep/virginica.csv

Con la función dir_create() del paquete fs se crea un nuevo subdirectorio en la carpeta de trabajo. Con la función group_by() se divide iris en tres data.frames según la especie. Con la función group_walk() generamos una función que permite escribir cada data.frame como un nuevo archivo csv, al cual se le asigna el nombre de la especie. La función dir_ls() permite ver la lista de archivos en la subcarpeta que tengan extensión csv.

Ahora, volveremos a leer y combinar los tres archivos .csv con las funciones map_dfr() y read_csv(). Además se utiliza el argumento .id para rastrear la fuente de cada fila del data.frame generado.

csv %>% 
  map_dfr(read_csv, .id= "source")
#> New names:
#> Rows: 50 Columns: 5
#> -- Column specification
#> ------------------------------------ Delimiter: "," dbl
#> (5): ...1, Sepal.Length, Sepal.Width, Petal.Length, ...
#> i Use `spec()` to retrieve the full column specification
#> for this data. i Specify the column types or set
#> `show_col_types = FALSE` to quiet this message.
#> New names:
#> Rows: 50 Columns: 5
#> -- Column specification
#> ------------------------------------ Delimiter: "," dbl
#> (5): ...1, Sepal.Length, Sepal.Width, Petal.Length, ...
#> i Use `spec()` to retrieve the full column specification
#> for this data. i Specify the column types or set
#> `show_col_types = FALSE` to quiet this message.
#> New names:
#> Rows: 50 Columns: 5
#> -- Column specification
#> ------------------------------------ Delimiter: "," dbl
#> (5): ...1, Sepal.Length, Sepal.Width, Petal.Length, ...
#> i Use `spec()` to retrieve the full column specification
#> for this data. i Specify the column types or set
#> `show_col_types = FALSE` to quiet this message.
#> * `` -> `...1`
#> # A tibble: 150 x 6
#>    source         ...1 Sepal.Length Sepal.Width Petal.Length
#>    <chr>         <dbl>        <dbl>       <dbl>        <dbl>
#>  1 iris_sep/set~     1          5.1         3.5          1.4
#>  2 iris_sep/set~     2          4.9         3            1.4
#>  3 iris_sep/set~     3          4.7         3.2          1.3
#>  4 iris_sep/set~     4          4.6         3.1          1.5
#>  5 iris_sep/set~     5          5           3.6          1.4
#>  6 iris_sep/set~     6          5.4         3.9          1.7
#>  7 iris_sep/set~     7          4.6         3.4          1.4
#>  8 iris_sep/set~     8          5           3.4          1.5
#>  9 iris_sep/set~     9          4.4         2.9          1.4
#> 10 iris_sep/set~    10          4.9         3.1          1.5
#> # ... with 140 more rows, and 1 more variable:
#> #   Petal.Width <dbl>

Solución a ejercicios

Ejercicio 1

penguins %>% 
  filter(species == "Chinstrap") %>% 
  filter(body_mass_g < 3100 & bill_length_mm >= 40) %>% 
  select(contains("mm"), where(is.factor))

Ejercicio 2

iris %>%
  group_by(Species) %>% 
  select(contains("Length")) %>% 
  summarise(
    across(.fns = list(avg=mean,sd=sd),
           .names = "{.col}.{.fn}"))