Capítulo 4 Tidyverse
La mayoría del contenido visto en Introducción a R se ha enfocado al uso del lenguaje R y al uso de ciertas funciones sin considerar su rendimiento. Como bien se menciono en su momento, R no esta diseñado para ser rápido; lo que a través de los años ha sido un tema de interés a medida que la información aumenta y las necesidades por analizar y ejecutar procesos que traten con grandes cantidades de datos. Por tales razones, el uso de paquetes que estén diseñados para trabajar de manera eficiente ha sido fundamental en la mayoría de lenguajes de programación; tal es el caso del Tidyverse.
Como bien se especifica en su página oficial, “The tidyverse is an opinionated collection of R packages designed for data science. All packages share an underlying design philosophy, grammar, and data structures”. Estos paquetes están enfocados a tener un mejor flujo de escritura, a tener un mejor entendimiento de la estructura de los procesos, dar funciones que solucionen problemas comunes y, como ya se menciono, mejorar el rendimiento de las funciones.
Dentro de este conjunto de paquetes se encuentran funciones para leer distintos tipos de archivos como aquellos con extensión .csv y .xls, además aquellas para reconocer archivos dados por SPSS, Stata y SAS, manipular archivos JSON, XML, dar una interfaz para trabajar con APIs, hacer web scraping y tener comunicación con diferentes administradores de bases de datos como SQL, MariaDB, etc.
Se tienen paquetes especializados en la manipulación y limpieza de datos, también para crear modelos con estos, dar características especiales como catalogar y tratar a ciertas variables como fechas y factores, aplicar técnicas de expresiones regulares en dichos datos, aplicar funciones optimizadas que pueden remplazar a las de la familia apply y crear gráficas profesionales con una mayor fluidez.
Aquí se verán solo algunos de los paquetes que componen todo este “universo limpio” y se comenzará con un operador fundamental proporcionado por el paquete magritt
: %>%
, el cual se podrá utilizar casi siempre en R.
Dicho operador tiene por nombre pipe
, el cual tiene un uso similar al dado en otros lenguajes de programación como Python: .
y bash: |
. Este tiene como objetivo encadenar procesos de tal forma que el resultado dado en la cadena sirve como input del siguiente eslabón en dicha cadena, lo cual ayuda a evitar el anidamiento de funciones, minimizar la cantidad de objetos locales y facilitar la lectura e implementación de una secuencia de operaciones.
Supongase que se desea resolver la tarea sencilla de aplicar una cantidad definida de operaciones sobre un número; por ejemplo, obtener la raíz cuadrada del logaritmo natural de un número multiplicado por el cuadrado de otro, a dicho valor sumarlo con los primeros 10 naturales y, finalmente, obtener la raíz cuadrada de dicho resultado. Esto se puede resolver de la siguiente manera; supongamos que los números son 10 y 20.
sqrt(sum(sqrt(log(prod(10, 20^2))), 1:10))
[1] 7.607887
En tal caso se tuvo que anidar las funciones para aplicar estas cada resultado obtenido. Con %>%
la solución se ve de esta forma
library(magrittr)
10 %>% # 10
prod(20^2) %>% #se multiplica por 20^2
log() %>% #se obtiene el logaritmo de dicho número
sqrt() %>% #Se obtiene la raíz cuadrada
sum(1:10) %>% #A eso se le suman los primeros 10 naturales
sqrt() #Se obtiene la raíz cuadrada.
[1] 7.607887
La anterior solución tiene una estructura más fiel a como se fue resolviendo el problema poco a poco sin tiene que escribir hacia la izquierda para aplicar resultados de funciones anidadas. Más adelante se verá que el uso de dicho operador resulta fundamental para ahorra tiempo al momento de escribir código.
Otro ejemplo:
matrix(1:100, ncol = 5, byrow = 20) %>% #Creación de una matriz con los primeros 100 naturales.
rowSums(10) %>% #Se suma por renglón añadiendo 10 unidades a dicho resultado.
as.matrix() %>% #Se convierte dicho resultado a matriz.
scale() %>% #Se normalizan los datos.
sum() #Se comprueba que estos sumen media cero.
[1] 0
Como la mayoría de la información que se utilizará se tendrá que cargar con alguna librería, aquí se dejan algunos ejemplos de algunas funciones útiles en la lectura de información.
La función read_csv()
es una de las más comunes para leer archivos separados por comas, en la cual se puede dar el path del archivo o la dirección URL de dichos datos. En este caso se esta utilizando unos datos donde se relaciona la información sobre exceso de velocidad de ciertos automóviles y señales de advertencia. Para más información consúltese el siguiente enlace.
library(readr)
library(tibble)
readr::read_csv("Data/amis.csv") %>% head(10)) (amis <-
# A tibble: 10 x 5
X1 speed period warning pair
<dbl> <dbl> <dbl> <dbl> <dbl>
1 1 26 1 1 1
2 2 26 1 1 1
3 3 26 1 1 1
4 4 26 1 1 1
5 5 27 1 1 1
6 6 28 1 1 1
7 7 28 1 1 1
8 8 28 1 1 1
9 9 28 1 1 1
10 10 29 1 1 1
::read_csv("https://vincentarelbundock.github.io/Rdatasets/csv/boot/amis.csv") %>% head(10) readr
# A tibble: 10 x 5
X1 speed period warning pair
<dbl> <dbl> <dbl> <dbl> <dbl>
1 1 26 1 1 1
2 2 26 1 1 1
3 3 26 1 1 1
4 4 26 1 1 1
5 5 27 1 1 1
6 6 28 1 1 1
7 7 28 1 1 1
8 8 28 1 1 1
9 9 28 1 1 1
10 10 29 1 1 1
En caso de que se desee leer un archivo por algún otro delimitador, se puede usar la función read_delim()
y en el caso de tener archivos donde el delimitador sea “\t” usar la función read_tsv()
. Los datos que se utilizan son proporcionados por el US Census Bureau, los cuales fueron indirectamente obtenidos del siguiente enlance.
library(readxl)
readr::read_delim("Data/US Census Bureau.txt", delim = "|")) (US_Census_Bureau <-
# A tibble: 54 x 11
X1 `1900` `1901` `1902` `1903` `1904` `1905` `1906` `1907` `1908` `1909`
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 U.S. 76094 77585 79160 80632 82165 83820 85437 87000 88709 90,492
2 Northe… 21059 21401 21815 22248 22716 23214 23769 24320 24879 25,440
3 North_… 26359 26722 27126 27446 27830 28203 28524 28868 29187 29,530
4 South 24565 25114 25599 26055 26492 27003 27475 27879 28406 28,963
5 West 4112 4351 4620 4882 5127 5398 5671 5934 6234 6,557
6 AL 1830 1907 1935 1957 1978 2012 2045 2058 2070 2,108
7 AR 1314 1341 1360 1384 1419 1447 1465 1484 1513 1,545
8 AZ 124 131 138 144 151 158 167 176 186 196
9 CA 1490 1550 1623 1702 1792 1893 1976 2054 2161 2,282
10 CO 543 581 621 652 659 680 707 733 757 775
# … with 44 more rows
read_tsv("Data/US Census Bureautsv.txt")
# A tibble: 54 x 11
X1 `1900` `1901` `1902` `1903` `1904` `1905` `1906` `1907` `1908` `1909`
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 U.S. 76094 77585 79160 80632 82165 83820 85437 87000 88709 90,492
2 Northe… 21059 21401 21815 22248 22716 23214 23769 24320 24879 25,440
3 North_… 26359 26722 27126 27446 27830 28203 28524 28868 29187 29,530
4 South 24565 25114 25599 26055 26492 27003 27475 27879 28406 28,963
5 West 4112 4351 4620 4882 5127 5398 5671 5934 6234 6,557
6 AL 1830 1907 1935 1957 1978 2012 2045 2058 2070 2,108
7 AR 1314 1341 1360 1384 1419 1447 1465 1484 1513 1,545
8 AZ 124 131 138 144 151 158 167 176 186 196
9 CA 1490 1550 1623 1702 1792 1893 1976 2054 2161 2,282
10 CO 543 581 621 652 659 680 707 733 757 775
# … with 44 more rows
El paquete readxl
esta diseño para leer archivos excel. La siguiente información proporciona la población por sexo y entidad federativa según grupos de edad quinquenales de acuerdo a los tabulados básicos de la CONAPO.
readxl::read_xls("Data/Population_Sex_FEntity.xls", range = "B5:Q38")) (Population_FEntity <-
# A tibble: 33 x 16
...1 ...2 `0 a 4` `5 a 9` `10 a 14` `15 a 19` `20 a 24` `25 a 29`
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Repú… 1.03e8 9.31 10.4 11.2 10.1 8.33 7.33
2 Agua… 1.07e6 10.3 11.0 11.1 10.9 8.27 7.34
3 Baja… 2.86e6 9.41 10.3 11.1 10.7 8.43 7.82
4 Baja… 5.12e5 9.00 8.44 10.1 10.8 8.75 7.37
5 Camp… 7.56e5 8.34 10.4 12.7 9.52 9.23 7.74
6 Coah… 2.50e6 9.78 10.1 10.6 9.96 8.31 7.11
7 Coli… 5.69e5 8.09 8.43 10.0 11.0 9.29 7.11
8 Chia… 4.29e6 11.4 11.8 13.2 11.0 9.16 7.15
9 Chih… 3.24e6 7.83 10.2 11.0 9.49 7.71 6.03
10 Dist… 8.74e6 7.82 8.82 8.09 8.19 7.86 7.89
# … with 23 more rows, and 8 more variables: `30 a 34` <dbl>, `35 a 39` <dbl>,
# `40 a 44` <dbl>, `45 a 49` <dbl>, `50 a 54` <dbl>, `55 a 59` <dbl>, `60 y
# más` <dbl>, NE <dbl>
Algo que hay que tener en cuenta al trabajar con los paquetes del tidyverse es el uso de tibbles en lugar de data frames.
class(amis)
[1] "tbl_df" "tbl" "data.frame"
Un tibble es una versión moderna de un data frame que trabaja de manera perezosa (es decir, que realiza menos operaciones) evitando problemas comunes y supuestos que un data frame puede llegar a asumir. Por ejemplo, los tibbles no coercionan automáticamente los caracteres a factores, no crean nombres para las observaciones y no cambia los nombres de columnas que sean nombres no sintácticos, por ejemplo
tibble(":)" = "feliz", ":("="triste")
# A tibble: 1 x 2
`:)` `:(`
<chr> <chr>
1 feliz triste
data.frame(":)" = "feliz", ":("="triste")
X.. X...1
1 feliz triste
Se pueden utilizar variables desde la construcción del tibble
tibble(x = 1:20, y = x-1)) (t <-
# A tibble: 20 x 2
x y
<int> <dbl>
1 1 0
2 2 1
3 3 2
4 4 3
5 5 4
6 6 5
7 7 6
8 8 7
9 9 8
10 10 9
11 11 10
12 12 11
13 13 12
14 14 13
15 15 14
16 16 15
17 17 16
18 18 17
19 19 18
20 20 19
- ¿Qué sucede al ejecutar
data.frame(x = 1:20, y = x-1)
?
Además se tienen diferencias respecto a la impresión entre un tibble y un data frame, un tibble permite agregar listas directamente sin tener que usar la función I()
y los tibbles nunca hacen emparejamiento parcial.
tibble(xy=1:20, z = 1:20)
t2 <- data.frame(xy = 1:20, z = 1:20)
d2 <-str(t2$x)
NULL
str(d2$x)
int [1:20] 1 2 3 4 5 6 7 8 9 10 ...
Finalmente, los tibbles permiten hacer substracciones con el operador pipe.
#t$x
%>% .$x t
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#t[["x"]]
%>% .[["x"]] t
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
- ¿Qué hace la función
tribble()
?