Capítulo 3 Funcionamiento avanzado de R
3.1 Introducción
En el capitulo anterior se mostraron de manera muy rápida las funciones básicas de R, esto porque de ahora en adelante se va a enfocar en el uso de funciones del tidyverse (Wickham et al., 2019). Este meta-paquete es uno que engloba a un montón de paquetes que se rigen bajo el paradigma de datos ordenados (tidy data). Datos ordenados quiere decir una observación por fila y cada variable en su columna. Lo que se cubre en este capitulo y más se puede encontrar en “R for Data Science” (Grolemund & Wickham, 2016).
En este capitulo se van a utilizar los siguientes paquetes:
library(babynames)
library(nycflights13)
library(gapminder)
library(DT)
library(rio)
library(tidyverse)
Los tres primeros corresponden con conjuntos de datos. Así mismo se vuelven a importar los datos con que se venia trabajando:
data("airquality")
dat1 <- import("data/LungCapData2.csv", setclass = 'tibble')
titanic <- import("data/titanic.csv", setclass = 'tibble')
Los paquetes más usados e importantes del tidyverse son:
- dplyr: Manipulación de datos mediante selección, filtrado, creación, agrupamiento, arreglo y resumen de tablas
- tidyr: Convierte datos a ordenados y viceversa
- ggplot2: Paquete para crear gráficos de alta calidad y personalizables
- purrr: Brinda funciones para la racionero sobre vectores, listas y tablas
- forcats: Manipulación de datos categóricos (factores)
- stringr: Manipulación de datos de texto
- lubridate: Manipulación de fechas
Un punto importante a destacar, es que en todas (sino la mayoría) de las funciones del tidyverse la tabla de datos es el primer argumento.
3.2 Operadores lógicos
Operadores lógicos permiten hacer comparaciones o pruebas, donde usualmente el resultado es TRUE
o FALSE
. En términos numéricos TRUE
equivale a 1 y FALSE
a 0. Los operadores lógicos más usados son:
<
, menor que<=
, menor o igual que==
, igual que!=
, no igual que>
, mayor que>=
, mayor o igual que%in%
, pertenencia
Aquí se asigna 4 a x
y se aplican varios de los operadores lógicos para cuestionar el contenido de el objeto x
.
x <- 4
x == 4
## [1] TRUE
x != 4
## [1] FALSE
x < 4
## [1] FALSE
x <= 5
## [1] TRUE
Estos operadores se pueden usar igualmente en vectores. Recordando que el resultado de una operación lógica es TRUE
o FALSE
, el vector resultante es del tipo lógico. Si se desea acceder a los elementos que cumplen la condición hay que aplicar el vector lógico sobre el vector, donde va a extraer los elementos que coinciden con TRUE
.
Aquí se crea un vector numérico, y se tratan de extraer los valores menores a 70. Se muestra la forma básica de R (vector[condicion]
) y una forma más directa e intuitiva que ofrece purrr.
y <- c(95, 90, 58, 87, 62, 75)
y < 70
## [1] FALSE FALSE TRUE FALSE TRUE FALSE
y[y < 70]
## [1] 58 62
keep(y, ~.x < 70) # purrr
## [1] 58 62
3.3 Operador de secuencia (Pipe operator)
Uno de los operadores básicos en el tidyverse es el pipe operator (%>%
). Este permite que el resultado antes del operador sea la (primer) entrada de lo que se va a hacer después del operador (x %>% f(y)
es lo mismo que f(x,y)
).
El shortcut para escribirlo es:
- Mac: Cmd + Shift + M
- Windows: Ctrl + Shift + M
La ventaja es que permite encadenar operaciones sin necesidad de salvar objetos intermedios y es más fácil de leer que encerrar operaciones una dentro de la otra. Se ejemplifica con un caso sencillo, donde se tiene un vector de errores y se quiere calcular el error cuadrático medio (RMSE por sus siglas en ingles).
set.seed(26)
e = runif(50,-10,10)
round(sqrt(mean(e^2)),3) # forma clasica
## [1] 5.595
e %>% .^2 %>% mean() %>% sqrt() %>% round(3) # usando el operador
## [1] 5.595
Lo anterior se lee: agarre el vector e
, eleve sus valores al cuadrado, después calcule la media, después sáquele la raíz y por ultimo redondeélo a 3 cifras.
3.4 Resumen de variables
Para resumir datos la función principal es summarise
, que colapsa una o varias columnas a un dato resumen. Muchas veces se tiene una variable agrupadora (factor) en los datos y se requiere calcular estadísticas por grupo, para esto se usa group_by
junto con summarise
. En group_by
se pueden incluir más de una variable agrupadora.
Funciones que ayudan a resumir datos son:
first(n)
, el primer elemento del vectorx
last(x)
, el ultimo elemento del vectorx
nth(x, n)
, el elementon
del vectorx
n()
, el numero de filas en una tabla u observaciones en un grupon_distinct(x)
, el numero de valores únicos en el vectorx
dat1 %>%
group_by(Gender) %>%
summarise(mean(Age))
## # A tibble: 2 x 2
## Gender `mean(Age)`
## <chr> <dbl>
## 1 female 9.84
## 2 male 10.0
dat1 %>%
group_by(Gender,Smoke) %>%
summarise(mean(Age))
## # A tibble: 4 x 3
## Gender Smoke `mean(Age)`
## <chr> <chr> <dbl>
## 1 female no 9.37
## 2 female yes 13.3
## 3 male no 9.69
## 4 male yes 13.9
dat1 %>%
group_by(Gender) %>%
summarise(N=n(),
mean(Age),
mean(Height),
mean(LungCap))
## # A tibble: 2 x 5
## Gender N `mean(Age)` `mean(Height)` `mean(LungCap)`
## <chr> <int> <dbl> <dbl> <dbl>
## 1 female 318 9.84 60.2 5.35
## 2 male 336 10.0 62.0 6.44
3.5 Selección y renombre de variables
Para seleccionar columnas la función es select
, la cual puede usar números o nombres y los nombres no tienen que llevar comillas. Esto también permite reordenar las columnas de una tabla. Para el caso de obtener una columna como vector se usa pull
con el numero o nombre de la columna a jalar. Durante la selección se puede cambiar el nombre de la variable, o usando rename
.
Funciones que ayudan a seleccionar variables son:
starts_with('X')
, todas las columnas que empiezan con ‘X’,ends_with('X')
, todas las columnas que terminan con ‘X’,contains('X')
, todas las columnas que contienen ‘X’,matches('X')
, todas las columnas que coinciden con ‘X’
Aquí se mezcla funciones de resumen y selección para crear resumen de las variables seleccionadas.
dat1 %>%
group_by(Gender) %>%
select(Age,Height) %>%
summarise_all(mean)
## # A tibble: 2 x 3
## Gender Age Height
## <chr> <dbl> <dbl>
## 1 female 9.84 60.2
## 2 male 10.0 62.0
dat1 %>%
group_by(Gender) %>%
select(Age,Height) %>%
summarise_all(.funs = list(~mean(.),
~sd(.)))
## # A tibble: 2 x 5
## Gender Age_mean Height_mean Age_sd Height_sd
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 female 9.84 60.2 2.93 4.79
## 2 male 10.0 62.0 2.98 6.33
Aquí se muestra la selección de variables, que resulta en un reordenamiento de las mismas. Así mismo se puede deseleccionar lo que no se quiere, usando -
para indicar las columnas que no se quieren.
airquality %>%
select(Temp,Wind,Ozone)
## # A tibble: 153 x 3
## Temp Wind Ozone
## <int> <dbl> <int>
## 1 67 7.4 41
## 2 72 8 36
## 3 74 12.6 12
## 4 62 11.5 18
## 5 56 14.3 NA
## 6 66 14.9 28
## 7 65 8.6 23
## 8 59 13.8 19
## 9 61 20.1 8
## 10 69 8.6 NA
## # … with 143 more rows
dat1 %>%
select(-Smoke)
## # A tibble: 654 x 4
## Age LungCap Height Gender
## <int> <dbl> <dbl> <chr>
## 1 9 3.12 57 female
## 2 8 3.17 67.5 female
## 3 7 3.16 54.5 female
## 4 9 2.67 53 male
## 5 9 3.68 57 male
## 6 8 5.01 61 female
## 7 6 3.76 58 female
## 8 6 2.24 56 female
## 9 8 3.96 58.5 female
## 10 9 3.83 60 female
## # … with 644 more rows
dat1 %>%
pull(Age)
## [1] 9 8 7 9 9 8 6 6 8 9 6 8 8 8 8 7 5 6 9 9 5 5 4 7 9
## [26] 3 9 5 8 9 5 9 8 7 5 8 9 8 8 8 9 8 5 8 5 9 7 8 6 8
## [51] 5 9 9 8 6 9 9 7 4 8 8 8 6 4 8 6 9 7 5 9 8 8 9 9 9
## [76] 7 5 5 9 6 7 6 8 8 7 8 7 9 5 9 9 9 7 8 8 9 9 9 7 8
## [101] 8 7 9 4 9 6 8 6 7 7 8 7 7 7 7 8 7 5 8 7 9 7 7 6 8
## [126] 8 8 9 7 8 9 8 8 9 8 6 6 8 9 5 7 9 6 9 9 9 6 8 9 8
## [151] 8 9 9 9 7 8 6 9 9 9 7 8 5 8 9 6 9 6 8 5 7 7 4 9 8
## [176] 9 9 9 5 9 7 6 9 9 9 7 5 8 9 7 9 8 9 6 6 8 9 5 6 6
## [201] 9 7 9 8 5 7 6 9 7 9 9 8 9 7 9 4 9 5 8 9 8 3 9 8 6
## [226] 9 8 8 7 6 8 9 4 7 8 8 9 6 8 6 8 9 8 7 9 8 7 9 8 9
## [251] 6 8 9 8 9 9 8 7 5 7 8 9 9 6 8 7 9 7 7 5 9 9 8 8 9
## [276] 6 7 5 9 5 7 6 8 7 8 4 8 5 8 7 7 9 9 8 9 6 8 9 4 6
## [301] 7 9 8 6 8 7 5 8 7 11 10 14 11 11 12 10 11 10 14 13 14 12 12 10 13
## [326] 10 11 10 11 10 13 14 11 10 11 13 10 10 12 10 10 10 11 11 11 10 11 11 13 13
## [351] 11 11 14 11 10 10 10 14 13 10 14 10 11 13 12 13 10 13 11 14 11 13 11 11 10
## [376] 11 11 10 11 13 12 10 10 14 11 10 11 10 11 13 13 10 11 11 12 10 10 11 10 11
## [401] 14 13 12 11 11 11 14 12 10 12 11 10 11 13 10 10 11 13 10 11 10 13 11 10 11
## [426] 11 14 11 13 11 11 10 13 10 13 10 12 10 14 12 10 11 14 12 10 10 10 10 12 13
## [451] 11 12 11 12 11 11 12 12 13 11 12 10 12 13 10 12 10 12 10 11 10 12 14 10 10
## [476] 12 10 10 13 12 12 11 13 12 10 11 11 13 12 13 13 10 12 12 14 11 10 13 11 11
## [501] 13 12 10 10 12 13 11 10 11 11 11 11 11 14 12 13 13 10 12 10 10 12 11 12 11
## [526] 11 12 12 14 11 10 11 12 13 12 11 11 11 14 11 13 12 10 12 13 10 10 10 10 14
## [551] 12 11 11 12 14 14 10 11 11 10 10 12 12 11 12 10 12 13 10 12 10 13 12 10 12
## [576] 10 11 12 11 12 10 13 12 11 11 11 11 12 14 11 11 12 14 11 13 11 10 13 12 11
## [601] 13 14 10 11 11 15 15 18 19 19 16 17 15 15 15 15 15 19 18 16 17 16 15 15 15
## [626] 18 17 15 17 17 16 17 15 15 16 16 15 18 15 16 17 16 16 15 18 15 16 17 16 16
## [651] 15 18 16 15
3.5.1 select
helpers
Se muestran diferentes usos y resultados de usar select
helpers para facilidad de selección de columnas que cumplan con ciertos criterios. A su vez, se ejemplifica el renombrar las columnas durante la selección o usando rename
(nuevo_nombre = nombre_actual). Un operador especial es everything()
que selecciona todo; esto es útil cuando se quiere reordenar y poner una o varias columnas de primero y después el resto sin tener que escribir todos los nombres.
select(storms, name:pressure) # columnas desde name hasta pressure
## # A tibble: 10,010 x 11
## name year month day hour lat long status category wind pressure
## <chr> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <chr> <ord> <int> <int>
## 1 Amy 1975 6 27 0 27.5 -79 tropical d… -1 25 1013
## 2 Amy 1975 6 27 6 28.5 -79 tropical d… -1 25 1013
## 3 Amy 1975 6 27 12 29.5 -79 tropical d… -1 25 1013
## 4 Amy 1975 6 27 18 30.5 -79 tropical d… -1 25 1013
## 5 Amy 1975 6 28 0 31.5 -78.8 tropical d… -1 25 1012
## 6 Amy 1975 6 28 6 32.4 -78.7 tropical d… -1 25 1012
## 7 Amy 1975 6 28 12 33.3 -78 tropical d… -1 25 1011
## 8 Amy 1975 6 28 18 34 -77 tropical d… -1 30 1006
## 9 Amy 1975 6 29 0 34.4 -75.8 tropical s… 0 35 1004
## 10 Amy 1975 6 29 6 34 -74.8 tropical s… 0 40 1002
## # … with 10,000 more rows
storms %>%
select(-c(name, pressure)) # columnas menos name y pressure
## # A tibble: 10,010 x 11
## year month day hour lat long status category wind ts_diameter
## <dbl> <dbl> <int> <dbl> <dbl> <dbl> <chr> <ord> <int> <dbl>
## 1 1975 6 27 0 27.5 -79 tropi… -1 25 NA
## 2 1975 6 27 6 28.5 -79 tropi… -1 25 NA
## 3 1975 6 27 12 29.5 -79 tropi… -1 25 NA
## 4 1975 6 27 18 30.5 -79 tropi… -1 25 NA
## 5 1975 6 28 0 31.5 -78.8 tropi… -1 25 NA
## 6 1975 6 28 6 32.4 -78.7 tropi… -1 25 NA
## 7 1975 6 28 12 33.3 -78 tropi… -1 25 NA
## 8 1975 6 28 18 34 -77 tropi… -1 30 NA
## 9 1975 6 29 0 34.4 -75.8 tropi… 0 35 NA
## 10 1975 6 29 6 34 -74.8 tropi… 0 40 NA
## # … with 10,000 more rows, and 1 more variable: hu_diameter <dbl>
iris %>%
select(starts_with("Sepal")) # columnas que empiezan con 'Sepal'
## # A tibble: 150 x 2
## Sepal.Length Sepal.Width
## <dbl> <dbl>
## 1 5.1 3.5
## 2 4.9 3
## 3 4.7 3.2
## 4 4.6 3.1
## 5 5 3.6
## 6 5.4 3.9
## 7 4.6 3.4
## 8 5 3.4
## 9 4.4 2.9
## 10 4.9 3.1
## # … with 140 more rows
iris %>%
select(ends_with("Width")) # columnas que terminan con 'Width'
## # A tibble: 150 x 2
## Sepal.Width Petal.Width
## <dbl> <dbl>
## 1 3.5 0.2
## 2 3 0.2
## 3 3.2 0.2
## 4 3.1 0.2
## 5 3.6 0.2
## 6 3.9 0.4
## 7 3.4 0.3
## 8 3.4 0.2
## 9 2.9 0.2
## 10 3.1 0.1
## # … with 140 more rows
storms %>%
select(contains("d")) # columnas que contienen 'd'
## # A tibble: 10,010 x 4
## day wind ts_diameter hu_diameter
## <int> <int> <dbl> <dbl>
## 1 27 25 NA NA
## 2 27 25 NA NA
## 3 27 25 NA NA
## 4 27 25 NA NA
## 5 28 25 NA NA
## 6 28 25 NA NA
## 7 28 25 NA NA
## 8 28 30 NA NA
## 9 29 35 NA NA
## 10 29 40 NA NA
## # … with 10,000 more rows
iris %>%
select(Especie = Species, everything()) # renombrar seleccion y seleccionar el resto
## # A tibble: 150 x 5
## Especie Sepal.Length Sepal.Width Petal.Length Petal.Width
## <fct> <dbl> <dbl> <dbl> <dbl>
## 1 setosa 5.1 3.5 1.4 0.2
## 2 setosa 4.9 3 1.4 0.2
## 3 setosa 4.7 3.2 1.3 0.2
## 4 setosa 4.6 3.1 1.5 0.2
## 5 setosa 5 3.6 1.4 0.2
## 6 setosa 5.4 3.9 1.7 0.4
## 7 setosa 4.6 3.4 1.4 0.3
## 8 setosa 5 3.4 1.5 0.2
## 9 setosa 4.4 2.9 1.4 0.2
## 10 setosa 4.9 3.1 1.5 0.1
## # … with 140 more rows
iris %>%
rename(Especie = Species) # renombrar columna
## # A tibble: 150 x 5
## Sepal.Length Sepal.Width Petal.Length Petal.Width Especie
## <dbl> <dbl> <dbl> <dbl> <fct>
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
## 7 4.6 3.4 1.4 0.3 setosa
## 8 5 3.4 1.5 0.2 setosa
## 9 4.4 2.9 1.4 0.2 setosa
## 10 4.9 3.1 1.5 0.1 setosa
## # … with 140 more rows
3.6 Filtrado de observaciones
Para filtrar observaciones de acuerdo a uno a varios criterios se usa la función filter
, así como operadores lógicos y funciones auxiliares.
Funciones que ayudan a filtrar observaciones son las mismas de los Operadores lógicos.
Dos de las funciones auxiliares más útiles son:
between(x,left,right)
que filtra observaciones para la variablex
que se encuentren entreleft
(limite inferior) yright
(limite superior); esta es más útil para variables numéricas,x %in% c(a,b,c)
que filtra observaciones para la variablex
que se encuentren en el vectorc(a,b,c)
; esta es más útil para variables de texto o factor
Se muestran diferentes ejemplos de como filtrar observaciones. Cuando se requiere que una observación cumpla varios criterios, estas condiciones se pueden separar por medio de comas (,
), que es lo mismo que usar el operador lógico &
. Si se requiere una u otra condición se puede usar el operador lógico |
, pero en ese caso y dependiendo de lo deseado es mejor usar between()
o %in%
.
filter(airquality,Temp > 85)
## # A tibble: 34 x 6
## Ozone Solar.R Wind Temp Month Day
## <int> <int> <dbl> <int> <int> <int>
## 1 NA 273 6.9 87 6 8
## 2 71 291 13.8 90 6 9
## 3 39 323 11.5 87 6 10
## 4 NA 259 10.9 93 6 11
## 5 NA 250 9.2 92 6 12
## 6 77 276 5.1 88 7 7
## 7 97 267 6.3 92 7 8
## 8 97 272 5.7 92 7 9
## 9 85 175 7.4 89 7 10
## 10 NA 291 14.9 91 7 14
## # … with 24 more rows
airquality %>%
filter(Temp > 85)
## # A tibble: 34 x 6
## Ozone Solar.R Wind Temp Month Day
## <int> <int> <dbl> <int> <int> <int>
## 1 NA 273 6.9 87 6 8
## 2 71 291 13.8 90 6 9
## 3 39 323 11.5 87 6 10
## 4 NA 259 10.9 93 6 11
## 5 NA 250 9.2 92 6 12
## 6 77 276 5.1 88 7 7
## 7 97 267 6.3 92 7 8
## 8 97 272 5.7 92 7 9
## 9 85 175 7.4 89 7 10
## 10 NA 291 14.9 91 7 14
## # … with 24 more rows
airquality %>%
filter(Temp > 75, Wind > 10)
## # A tibble: 38 x 6
## Ozone Solar.R Wind Temp Month Day
## <int> <int> <dbl> <int> <int> <int>
## 1 45 252 14.9 81 5 29
## 2 NA 264 14.3 79 6 6
## 3 71 291 13.8 90 6 9
## 4 39 323 11.5 87 6 10
## 5 NA 259 10.9 93 6 11
## 6 NA 332 13.8 80 6 14
## 7 NA 322 11.5 79 6 15
## 8 21 191 14.9 77 6 16
## 9 13 137 10.3 76 6 20
## 10 NA 98 11.5 80 6 28
## # … with 28 more rows
airquality %>%
filter(between(Temp, 70, 80))
## # A tibble: 53 x 6
## Ozone Solar.R Wind Temp Month Day
## <int> <int> <dbl> <int> <int> <int>
## 1 36 118 8 72 5 2
## 2 12 149 12.6 74 5 3
## 3 7 NA 6.9 74 5 11
## 4 11 320 16.6 73 5 22
## 5 115 223 5.7 79 5 30
## 6 37 279 7.4 76 5 31
## 7 NA 286 8.6 78 6 1
## 8 NA 287 9.7 74 6 2
## 9 NA 264 14.3 79 6 6
## 10 NA 332 13.8 80 6 14
## # … with 43 more rows
airquality %>%
filter(Temp > 75, Wind > 10) %>%
select(Ozone,Solar.R)
## # A tibble: 38 x 2
## Ozone Solar.R
## <int> <int>
## 1 45 252
## 2 NA 264
## 3 71 291
## 4 39 323
## 5 NA 259
## 6 NA 332
## 7 NA 322
## 8 21 191
## 9 13 137
## 10 NA 98
## # … with 28 more rows
babynames %>%
filter(name %in% c("Acura", "Lexus", "Yugo"))
## # A tibble: 57 x 5
## year sex name n prop
## <dbl> <chr> <chr> <int> <dbl>
## 1 1990 F Lexus 36 0.0000175
## 2 1990 M Lexus 12 0.00000558
## 3 1991 F Lexus 102 0.0000502
## 4 1991 M Lexus 16 0.00000755
## 5 1992 F Lexus 193 0.0000963
## 6 1992 M Lexus 25 0.0000119
## 7 1993 F Lexus 285 0.000145
## 8 1993 M Lexus 30 0.0000145
## 9 1994 F Lexus 381 0.000195
## 10 1994 F Acura 6 0.00000308
## # … with 47 more rows
3.7 Orden de acuerdo a variables
arrange
se usa para ordenar los datos de acuerdo a una o más variables, donde por defecto lo hace de manera ascendente, para ordenarlos de manera descendente se encierra la variable dentro de desc(var)
. Si se ordena por una variable numérica se hará de menor a mayor o viceversa, si se ordena por una variable factor se hará de acuerdo al orden de los niveles del factor, y si se ordena por una variable de texto se hará por orden alfabético.
airquality %>%
arrange(Temp)
## # A tibble: 153 x 6
## Ozone Solar.R Wind Temp Month Day
## <int> <int> <dbl> <int> <int> <int>
## 1 NA NA 14.3 56 5 5
## 2 6 78 18.4 57 5 18
## 3 NA 66 16.6 57 5 25
## 4 NA NA 8 57 5 27
## 5 18 65 13.2 58 5 15
## 6 NA 266 14.9 58 5 26
## 7 19 99 13.8 59 5 8
## 8 1 8 9.7 59 5 21
## 9 8 19 20.1 61 5 9
## 10 4 25 9.7 61 5 23
## # … with 143 more rows
airquality %>%
arrange(desc(Temp))
## # A tibble: 153 x 6
## Ozone Solar.R Wind Temp Month Day
## <int> <int> <dbl> <int> <int> <int>
## 1 76 203 9.7 97 8 28
## 2 84 237 6.3 96 8 30
## 3 118 225 2.3 94 8 29
## 4 85 188 6.3 94 8 31
## 5 NA 259 10.9 93 6 11
## 6 73 183 2.8 93 9 3
## 7 91 189 4.6 93 9 4
## 8 NA 250 9.2 92 6 12
## 9 97 267 6.3 92 7 8
## 10 97 272 5.7 92 7 9
## # … with 143 more rows
gss_cat %>%
arrange(marital)
## # A tibble: 21,483 x 9
## year marital age race rincome partyid relig denom tvhours
## <int> <fct> <int> <fct> <fct> <fct> <fct> <fct> <int>
## 1 2000 No answ… 28 Other $10000 - 1… Ind,near dem Buddhi… Not appl… 2
## 2 2006 No answ… NA White Not applic… Strong repu… Protes… Other NA
## 3 2006 No answ… NA White No answer Strong repu… None Not appl… 2
## 4 2006 No answ… 63 White No answer Strong demo… None Not appl… NA
## 5 2006 No answ… 40 Other $20000 - 2… Not str dem… Protes… No denom… NA
## 6 2006 No answ… 45 White No answer No answer No ans… No answer NA
## 7 2006 No answ… NA White No answer No answer No ans… No answer NA
## 8 2008 No answ… 62 White Not applic… Strong demo… Protes… Episcopal NA
## 9 2008 No answ… 43 White No answer Independent Christ… No denom… 1
## 10 2008 No answ… 50 White No answer Ind,near dem Protes… Other 4
## # … with 21,473 more rows
3.8 Creación de variables
Para crear o modificar variables se usa mutate
. Algunas veces se requiere o desea categorizar una variable continua de acuerdo a ciertos criterios o puntos de quiebre; lo anterior puede realizarse por medio de lo que se conoce como if statements, donde una función que realiza la misma tarea pero de forma más eficiente es case_when
.
En el primer ejemplo se trabaja con la tabla del titanic
, donde se tienen varias variables como texto (‘Pclass’, ‘Survived’, ‘Sex’) y se quieren convertir a factor, por lo que simplemente se re-definen estas variables. Este cambio se puede ver con glimpse
para el antes y después, donde el tipo de variable cambia.
glimpse(titanic)
## Rows: 891
## Columns: 12
## $ PassengerId <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17…
## $ Survived <int> 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, …
## $ Pclass <int> 3, 1, 3, 1, 3, 3, 1, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3, 2, 3, …
## $ Name <chr> "Braund, Mr. Owen Harris", "Cumings, Mrs. John Bradley (F…
## $ Sex <chr> "male", "female", "female", "female", "male", "male", "ma…
## $ Age <dbl> 22, 38, 26, 35, 35, NA, 54, 2, 27, 14, 4, 58, 20, 39, 14,…
## $ SibSp <int> 1, 1, 0, 1, 0, 0, 0, 3, 0, 1, 1, 0, 0, 1, 0, 0, 4, 0, 1, …
## $ Parch <int> 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 5, 0, 0, 1, 0, 0, …
## $ Ticket <chr> "A/5 21171", "PC 17599", "STON/O2. 3101282", "113803", "3…
## $ Fare <dbl> 7.2500, 71.2833, 7.9250, 53.1000, 8.0500, 8.4583, 51.8625…
## $ Cabin <chr> "", "C85", "", "C123", "", "", "E46", "", "", "", "G6", "…
## $ Embarked <chr> "S", "C", "S", "S", "S", "Q", "S", "S", "S", "C", "S", "S…
titanic = titanic %>%
mutate(Pclass = as_factor(Pclass),
Survived = as_factor(Survived),
Sex = as_factor(Sex))
glimpse(titanic)
## Rows: 891
## Columns: 12
## $ PassengerId <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17…
## $ Survived <fct> 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, …
## $ Pclass <fct> 3, 1, 3, 1, 3, 3, 1, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3, 2, 3, …
## $ Name <chr> "Braund, Mr. Owen Harris", "Cumings, Mrs. John Bradley (F…
## $ Sex <fct> male, female, female, female, male, male, male, male, fem…
## $ Age <dbl> 22, 38, 26, 35, 35, NA, 54, 2, 27, 14, 4, 58, 20, 39, 14,…
## $ SibSp <int> 1, 1, 0, 1, 0, 0, 0, 3, 0, 1, 1, 0, 0, 1, 0, 0, 4, 0, 1, …
## $ Parch <int> 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 5, 0, 0, 1, 0, 0, …
## $ Ticket <chr> "A/5 21171", "PC 17599", "STON/O2. 3101282", "113803", "3…
## $ Fare <dbl> 7.2500, 71.2833, 7.9250, 53.1000, 8.0500, 8.4583, 51.8625…
## $ Cabin <chr> "", "C85", "", "C123", "", "", "E46", "", "", "", "G6", "…
## $ Embarked <chr> "S", "C", "S", "S", "S", "Q", "S", "S", "S", "C", "S", "S…
Se pueden crear variables nuevas que dependen de otra en la tabla. En el ejemplo se calcula la altura en centímetros a partir de la altura en pulgadas (1 pulgada = 2.54 cm)
dat1 %>%
mutate(Altura = Height*2.54)
## # A tibble: 654 x 6
## Age LungCap Height Gender Smoke Altura
## <int> <dbl> <dbl> <chr> <chr> <dbl>
## 1 9 3.12 57 female no 145.
## 2 8 3.17 67.5 female no 171.
## 3 7 3.16 54.5 female no 138.
## 4 9 2.67 53 male no 135.
## 5 9 3.68 57 male no 145.
## 6 8 5.01 61 female no 155.
## 7 6 3.76 58 female no 147.
## 8 6 2.24 56 female no 142.
## 9 8 3.96 58.5 female no 149.
## 10 9 3.83 60 female no 152.
## # … with 644 more rows
En el tercer ejemplo se re define la variable ‘Month’ pasándola a factor donde se le cambian las etiquetas a algo más explicito. A su vez, se define una nueva variable condicionada en los valores de otra (sensación dependiendo del valor de la temperatura). Aquí se ejemplifica case_when
, donde la estructura es:
case_when(condicion1 ~ resultado1,
condicion2 ~ resultado2,
T ~ resultado3)
airq = airquality %>%
mutate(Month = factor(Month,
levels = 5:9,
labels = c("Mayo", "Junio", "Julio",
"Agosto", "Setiembre")),
Sensation = case_when(Temp < 60 ~ 'Cold',
Temp < 70 ~ 'Cool',
Temp < 85 ~ 'Warm',
T ~ 'Hot') %>%
as.factor())
airq
## # A tibble: 153 x 7
## Ozone Solar.R Wind Temp Month Day Sensation
## <int> <int> <dbl> <int> <fct> <int> <fct>
## 1 41 190 7.4 67 Mayo 1 Cool
## 2 36 118 8 72 Mayo 2 Warm
## 3 12 149 12.6 74 Mayo 3 Warm
## 4 18 313 11.5 62 Mayo 4 Cool
## 5 NA NA 14.3 56 Mayo 5 Cold
## 6 28 NA 14.9 66 Mayo 6 Cool
## 7 23 299 8.6 65 Mayo 7 Cool
## 8 19 99 13.8 59 Mayo 8 Cold
## 9 8 19 20.1 61 Mayo 9 Cool
## 10 NA 194 8.6 69 Mayo 10 Cool
## # … with 143 more rows
airquality %>%
as_tibble()
## # A tibble: 153 x 6
## Ozone Solar.R Wind Temp Month Day
## <int> <int> <dbl> <int> <int> <int>
## 1 41 190 7.4 67 5 1
## 2 36 118 8 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
## 7 23 299 8.6 65 5 7
## 8 19 99 13.8 59 5 8
## 9 8 19 20.1 61 5 9
## 10 NA 194 8.6 69 5 10
## # … with 143 more rows
3.9 Conteo de variables cualitativas
Para contar casos de variables discretas de una manera más expedita se puede usar count
. Esta función realiza un agrupamiento (group_by
) y resumen (summarise
) a la vez.
mpg %>%
count(manufacturer, year)
## # A tibble: 30 x 3
## manufacturer year n
## <chr> <int> <int>
## 1 audi 1999 9
## 2 audi 2008 9
## 3 chevrolet 1999 7
## 4 chevrolet 2008 12
## 5 dodge 1999 16
## 6 dodge 2008 21
## 7 ford 1999 15
## 8 ford 2008 10
## 9 honda 1999 5
## 10 honda 2008 4
## # … with 20 more rows
3.10 Tabla interactiva
Este es un ejemplo de como convertir una tabla estática a interactiva. Se usa el paquete DT (Xie et al., 2019) y la función datatable
, donde se pueden definir otra serie de argumentos. Tiene la ventaja de que para columnas numéricas puedo filtrar por medio de sliders, y para columnas de facto puedo seleccionar los niveles.
airq %>%
DT::datatable(filter = 'top', options = list(dom = 't'))
3.11 Datos relacionales
En caso de tener datos de observaciones en diferentes tablas, estas se pueden unir para juntar los datos en una única tabla (uniones de transformación), o relacionar para filtrar los datos de una tabla con respecto a otra (uniones de filtro).
De manera general las uniones se van a realizar de acuerdo a las columnas que tengan el mismo nombre en ambas tablas. Si se desea especificar una columna en especifico se usa el argumento by = 'col'
. Si el nombre difiere entre las tablas se define la unión de acuerdo a by = c('a' = 'b')
, donde 'a'
corresponde con el nombre de la columna en la primer tabla, y 'b'
corresponde con el nombre de la columna en la segunda tabla. Esto aplica para todas las funciones de unión (*_join
).
3.11.1 Uniones de transformación
Estas uniones agregan columnas de una tabla a otra.
Un tipo de unión es left_join(x, y)
, donde se unen los datos de la tabla de la derecha (y
) a la de la izquierda (x
) de acuerdo a una columna en común, y manteniendo todas las observaciones de x
.
flights %>%
left_join(airlines)
## # A tibble: 336,776 x 20
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
## <int> <int> <int> <int> <int> <dbl> <int> <int>
## 1 2013 1 1 517 515 2 830 819
## 2 2013 1 1 533 529 4 850 830
## 3 2013 1 1 542 540 2 923 850
## 4 2013 1 1 544 545 -1 1004 1022
## 5 2013 1 1 554 600 -6 812 837
## 6 2013 1 1 554 558 -4 740 728
## 7 2013 1 1 555 600 -5 913 854
## 8 2013 1 1 557 600 -3 709 723
## 9 2013 1 1 557 600 -3 838 846
## 10 2013 1 1 558 600 -2 753 745
## # … with 336,766 more rows, and 12 more variables: arr_delay <dbl>,
## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>,
## # name <chr>
flights %>%
left_join(airports, c("dest" = "faa"))
## # A tibble: 336,776 x 26
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
## <int> <int> <int> <int> <int> <dbl> <int> <int>
## 1 2013 1 1 517 515 2 830 819
## 2 2013 1 1 533 529 4 850 830
## 3 2013 1 1 542 540 2 923 850
## 4 2013 1 1 544 545 -1 1004 1022
## 5 2013 1 1 554 600 -6 812 837
## 6 2013 1 1 554 558 -4 740 728
## 7 2013 1 1 555 600 -5 913 854
## 8 2013 1 1 557 600 -3 709 723
## 9 2013 1 1 557 600 -3 838 846
## 10 2013 1 1 558 600 -2 753 745
## # … with 336,766 more rows, and 18 more variables: arr_delay <dbl>,
## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>,
## # name <chr>, lat <dbl>, lon <dbl>, alt <int>, tz <dbl>, dst <chr>,
## # tzone <chr>
Otro tipo de unión es inner_join(x, y)
, donde se mantienen observaciones que se encuentran en ambas tablas.
df1 <- tibble(x = c(1, 2), y = 2:1)
df2 <- tibble(x = c(1, 3), a = 10, b = "a")
df1 %>%
inner_join(df2)
## # A tibble: 1 x 4
## x y a b
## <dbl> <int> <dbl> <chr>
## 1 1 2 10 a
Otro tipo de unión es full_join(x, y)
, donde se mantienen todas las observaciones de ambas tablas.
df1 %>%
full_join(df2)
## # A tibble: 3 x 4
## x y a b
## <dbl> <int> <dbl> <chr>
## 1 1 2 10 a
## 2 2 1 NA <NA>
## 3 3 NA 10 a
3.11.2 Uniones de filtro
Se filtran las observaciones de una tabla de acuerdo a si coinciden o no con las de otra tabla.
Un tipo es semi_join(x, y)
, donde se mantienen todas las observaciones de x
que coinciden con observaciones en y
, pero sin agregar columnas de y
. El opuesto seria anti_join(x, y)
, donde se eliminan todas las observaciones de x
que coinciden con observaciones en y
, pero sin agregar columnas de y
.
df1 <- tibble(x = c(1, 1, 3, 4), y = 1:4)
df2 <- tibble(x = c(1, 1, 2), z = c("a", "b", "a"))
df1 %>%
semi_join(df2)
## # A tibble: 2 x 2
## x y
## <dbl> <int>
## 1 1 1
## 2 1 2
df1 %>%
anti_join(df2)
## # A tibble: 2 x 2
## x y
## <dbl> <int>
## 1 3 3
## 2 4 4
3.12 Datos ordenados (Tidy data)
3.12.1 Formatos largo y ancho
Los datos ordenados corresponden con cada variable en su columna, cada fila corresponde con una observación, y en las celdas van los valores correspondientes. Esto corresponde con un formato largo (Figura 3.1).
El ejemplo que se muestra a continuación no esta ordenado. La tabla tiene 3 variables pero no definidas correctamente. Una variable seria el país, otra seria el año (las columnas), y la tercera seria el numero de casos (las celdas). Esto se conoce como datos en formato ancho (En algunos casos puede ser necesario este formato, pero en la mayoría de ocasiones se prefiere el formato largo).
casos <- tribble(
~pais, ~"2011", ~"2012", ~"2013",
"FR", 7000, 6900, 7000,
"DE", 5800, 6000, 6200,
"US", 15000, 14000, 13000
)
Para pasar de un formato ancho a largo, se usa la función pivot_longer(cols, names_to, values_to)
, donde cols
son las columnas a agrupar en una sola, names_to
es el nombre que se le va a dar a la columna que va a contener las columnas a agrupar, y values_to
es el nombre que se le va a dar a la columna que va a contener los valores de las celdas y que corresponden con una variable.
En este caso se van a agrupar todas las columnas menos el país, se le va a llamar ‘anho’ y lo que estaba en las celdas pasa a ser la columna ‘casos’.
casos_tidy = casos %>%
pivot_longer(cols = -pais, names_to = 'anho', values_to = 'casos')
casos_tidy
## # A tibble: 9 x 3
## pais anho casos
## <chr> <chr> <dbl>
## 1 FR 2011 7000
## 2 FR 2012 6900
## 3 FR 2013 7000
## 4 DE 2011 5800
## 5 DE 2012 6000
## 6 DE 2013 6200
## 7 US 2011 15000
## 8 US 2012 14000
## 9 US 2013 13000
De igual manera se puede volver al formato ancho con pivot_wider(id_cols, names_from, values_from)
, donde id_cols
es una columna que identifica a cada observación, names_from
es la columna a usar para nuevas columnas, y values_from
es la columna donde están los valores a poner en las celdas.
casos_tidy %>%
pivot_wider(id_cols = pais, names_from = anho, values_from = casos)
## # A tibble: 3 x 4
## pais `2011` `2012` `2013`
## <chr> <dbl> <dbl> <dbl>
## 1 FR 7000 6900 7000
## 2 DE 5800 6000 6200
## 3 US 15000 14000 13000
3.12.2 Separar y unir
Otro caso de datos no ordenados es cuando una columna contiene 2 o más datos, por lo que es necesario separar cada dato en un su propia columna.
En el ejemplo la columna ‘tasa’ corresponde con ‘casos’ y ‘poblacion’, por lo que hay que separarla. La función separate
tiene el argumento into
que corresponde con un vector de texto donde se deben definir los nombres de las columnas resultantes.
casos2 <- tribble(
~pais, ~anho, ~tasa,
"Afghanistan", 2001, '745/19987071',
"Brasil", 2001, '37737/172006362',
"China", 2001, '212258/1272915272'
)
casos2 %>%
separate(tasa, into = c('casos', 'poblacion'))
## # A tibble: 3 x 4
## pais anho casos poblacion
## <chr> <dbl> <chr> <chr>
## 1 Afghanistan 2001 745 19987071
## 2 Brasil 2001 37737 172006362
## 3 China 2001 212258 1272915272
Por defecto separate
va a separar la columna en cualquier carácter especial que encuentre. Si se quiere especificar se puede usar el argumento sep
.
casos2 %>%
separate(tasa, into = c('casos', 'poblacion'), sep = '/')
## # A tibble: 3 x 4
## pais anho casos poblacion
## <chr> <dbl> <chr> <chr>
## 1 Afghanistan 2001 745 19987071
## 2 Brasil 2001 37737 172006362
## 3 China 2001 212258 1272915272
El tipo de columna resultante de separate
es de texto, pero en algunos casos ese no es el tipo deseado, por lo que se le puede pedir a la función que trate de adivinar y convertir las columnas al tipo correcto por medio del argumento convert = TRUE
.
casos2_sep = casos2 %>%
separate(tasa, into = c('casos', 'poblacion'), convert = T)
casos2_sep
## # A tibble: 3 x 4
## pais anho casos poblacion
## <chr> <dbl> <int> <int>
## 1 Afghanistan 2001 745 19987071
## 2 Brasil 2001 37737 172006362
## 3 China 2001 212258 1272915272
El unir columnas se hace por medio de unite
, donde se le pasan, primero, el nombre de la nueva columna, y segundo los nombres de las columnas a unir, así como el carácter a usar para separar los datos.
casos2_sep %>%
unite(tasa, casos, poblacion, sep = '-')
## # A tibble: 3 x 3
## pais anho tasa
## <chr> <dbl> <chr>
## 1 Afghanistan 2001 745-19987071
## 2 Brasil 2001 37737-172006362
## 3 China 2001 212258-1272915272
3.13 Datos anidados (Nesting)
Esta es una de las ventajas de los tibbles, donde una columna puede ser una lista, y como una lista puede contener lo que sea, esto permite flexibilidad en el análisis y manipulación de datos, como se va a ver en el próximo capitulo.
Esto es muy usado junto con group_by
, donde primero se agrupa la tabla y luego se crea una columna donde para cada grupo se va a tener su tabla única (las observaciones que corresponden con ese grupo) y diferente al resto.
iris %>%
group_by(Species) %>%
nest()
## # A tibble: 3 x 2
## Species data
## <fct> <list>
## 1 setosa <tibble [50 × 4]>
## 2 versicolor <tibble [50 × 4]>
## 3 virginica <tibble [50 × 4]>
airq %>%
group_by(Month) %>%
nest()
## # A tibble: 5 x 2
## Month data
## <fct> <list>
## 1 Mayo <tibble [31 × 6]>
## 2 Junio <tibble [30 × 6]>
## 3 Julio <tibble [31 × 6]>
## 4 Agosto <tibble [31 × 6]>
## 5 Setiembre <tibble [30 × 6]>
3.14 Recursos
Se presentan recursos a consultar para ahondar más en los temas presentados.
ModernDive Libro que cubre diversos temas desde una perspectiva moderna.
Strings in R Para manipular caracteres.
Referencias
Grolemund, G., & Wickham, H. (2016). R for Data Science. O’Reilly. https://bookdown.org/roy_schumacher/r4ds/
Wickham, H., Averick, M., Bryan, J., Chang, W., McGowan, L. D., François, R., Grolemund, G., Hayes, A., Henry, L., Hester, J., Kuhn, M., Pedersen, T. L., Miller, E., Bache, S. M., Müller, K., Ooms, J., Robinson, D., Seidel, D. P., Spinu, V., … Yutani, H. (2019). Welcome to the tidyverse. Journal of Open Source Software, 4(43), 1686. https://doi.org/10.21105/joss.01686
Xie, Y., Cheng, J., & Tan, X. (2019). DT: A Wrapper of the JavaScript Library ’DataTables’. https://CRAN.R-project.org/package=DT