data.table
dplyr
no resulta ser muy eficiente para el procesamiento de grandes cantidades de datos.data.table
nos permite realizar operaciones de manipulación de datos reduciendo los tiempos de procesamiento de manera significativa.Es un paquete del universo tidyverse
para utilizar data.table
desde con funciones similares a las de dplyr
.
En un principo tiene la ventaja de fácil usabilidad, ya que su sintaxis se asemeja a dplyr
.
Sin embargo, no es tan recomendable porque:
dplyr
están traducidas.data.table
.El objetivo de esta sesión es aprender a manipular datos con el paquete data.table
.
El objetivo de esta sesión es aprender a manipular datos con el paquete data.table
.
Para cumplir con el objetivo, revisaremos los siguientes temas:
El objetivo de esta sesión es aprender a manipular datos con el paquete data.table
.
Para cumplir con el objetivo, revisaremos los siguientes temas:
data.table
.El objetivo de esta sesión es aprender a manipular datos con el paquete data.table
.
Para cumplir con el objetivo, revisaremos los siguientes temas:
Importar y exportar datos con data.table
.
Sintaxis básica data.table
.
Manipulación de columnas.
Manipulación de filas.
Agrupación y resúmenes de datos.
El objetivo de esta sesión es aprender a manipular datos con el paquete data.table
.
Para cumplir con el objetivo, revisaremos los siguientes temas:
Importar y exportar datos con data.table
.
Sintaxis básica data.table
.
Manipulación de columnas.
Manipulación de filas.
Agrupación y resúmenes de datos.
Sintaxis avanzada data.table
.
Conectando acciones en data.table
.
Unificar tablas con data.table
.
data.table
tiene su propia versión de read.csv()
que nos permite abrir archivos .csv de manera eficiente:
fread()
library(data.table)library(dplyr)# podemos comparar el tiempo de ejecución entre read.csv y fread()tictoc::tic()censo_viviendas <- readr::read_csv("csv-viviendas-censo-2017/Microdato_Censo2017-Viviendas.csv")tictoc::toc()# 66.472 sec elapsedtictoc::tic()censo_viviendas <- data.table::fread("csv-viviendas-censo-2017/Microdato_Censo2017-Viviendas.csv")tictoc::toc()# 2.968 sec elapsed## estandarizamos los nombres de las variablescenso_viviendas <- janitor::clean_names(censo_viviendas)
data.table
tiene su propia versión de write.csv()
que nos permite exportar o guardar archivos .csv de manera eficiente:
fwrite()
tictoc::tic()readr::write_csv(censo_viviendas,"csv-viviendas-censo-2017/ejemplo_viviendas.csv")tictoc::toc()# 99.184 sec elapsedtictoc::tic()data.table::fwrite(censo_viviendas,"csv-viviendas-censo-2017/ejemplo_viviendas.csv")tictoc::toc()# 1.17 sec elapsed
Un parámetro muy útil de fwrite()
, es append = T
, esto nos permite grabar en el archivo almacenado sin necesidad de abrirlo, la manera de guardar es pegando una fila adicional por debajo.
Muy útil para almacenamientos "persistente", como en webscraping, análisis recurrentes y/o aplicativos online.
data.frame
en data.table
:data.frame
en data.table
:
Para eso existen las siguientes funciones:
dt_viviendas <- data.table(censo_viviendas) # odt_viviendas <- as.data.table(censo_viviendas)# o setdt_(censo_viviendas)
data.frame
en data.table
:
Para eso existen las siguientes funciones:
dt_viviendas <- data.table(censo_viviendas) # odt_viviendas <- as.data.table(censo_viviendas)# o setdt_(censo_viviendas)
Con esto podremos utilizar la sintaxis básica de data.table
Nos permite indexar filas.
Nos permite filtrar filas por valores o categorías.
Nos permite indexar filas.
Nos permite filtrar filas por valores o categorías.
Nos permite aplicar funciones específicas por filas.
Filtremos por index las primeras 6 filas
Filtremos por index las primeras 6 filas
dt_viviendas[1:6,] # odt_viviendas[1:6]# region provicia comuna dc area zc_loc id_zona_loc nviv p01 p02 p03a cant_per p03b p03c p04 p05 cant_hog cant_per region_15r provicia_15r comuna_15r# 1: 15 152 15202 1 2 6 13225 1 3 1 5 3 5 1 4 1 1 15 152 15202# 2: 15 152 15202 1 2 6 13225 2 1 3 98 98 98 98 98 0 0 15 152 15202# 3: 15 152 15202 1 2 6 13225 3 1 1 5 3 5 2 3 1 4 15 152 15202# 4: 15 152 15202 1 2 6 13225 4 1 4 98 98 98 98 98 0 0 15 152 15202# 5: 15 152 15202 1 2 6 13225 5 3 4 98 98 98 98 98 0 0 15 152 15202# 6: 15 152 15202 1 2 6 13225 6 3 4 98 98 98 98 98 0 0 15 152 15202
filtremos filas por valores, similar a función filter()
de dplyr
filtremos filas por valores, similar a función filter()
de dplyr
dt_viviendas[region == 13] # region provicia comuna dc area zc_loc id_zona_loc nviv p01 p02 p03a cant_per p03b p03c p04 p05 cant_hog cant_per region_15r provicia_15r comuna_15r# 1: 13 136 13605 1 1 1 12952 1 1 1 1 1 1 3 1 1 4 13 136 13605# 2: 13 136 13605 1 1 1 12952 2 1 1 2 3 1 3 1 1 2 13 136 13605# 3: 13 136 13605 1 1 1 12952 3 1 1 2 3 1 3 1 1 5 13 136 13605
Se pueden utilizar los operadores similares a rbase
o dplyr
.
dt_viviendas[region == 13 & area == 1]# region provicia comuna dc area zc_loc id_zona_loc nviv p01 p02 p03a cant_per p03b p03c p04 p05 cant_hog cant_per region_15r provicia_15r comuna_15r# 1: 13 136 13605 1 1 1 12952 1 1 1 1 1 1 3 1 1 4 13 136 13605# 2: 13 136 13605 1 1 1 12952 2 1 1 2 3 1 3 1 1 2 13 136 13605# 3: 13 136 13605 1 1 1 12952 3 1 1 2 3 1 3 1 1 5 13 136 13605
arrange()
de dplyr
.arrange()
de dplyr
.dt_viviendas[order(region),]# region provicia comuna dc area zc_loc id_zona_loc nviv p01 p02 p03a cant_per p03b p03c p04 p05 cant_hog cant_per region_15r provicia_15r comuna_15r# 1: 1 14 1405 1 1 1 1726 1 1 4 98 98 98 98 98 0 0 1 14 1405# 2: 1 14 1405 1 1 1 1726 2 1 4 98 98 98 98 98 0 0 1 14 1405# 3: 1 14 1405 1 1 1 1726 3 1 1 2 1 3 2 1 1 4 1 14 1405# 4: 1 14 1405 1 1 1 1726 4 1 1 2 3 1 2 1 1 3 1 14 1405# 5: 1 14 1405 1 1 1 1726 5 1 4 98 98 98 98 98 0 0 1 14 1405# --- # 6499570: 16 161 16101 99 1 999 4633 59 1 1 4 3 1 2 1 1 2 8 84 8401# 6499571: 16 161 16101 99 1 999 4633 60 9 1 98 98 98 98 98 98 26 8 84 8401# 6499572: 16 161 16101 99 1 999 4633 61 1 1 2 3 1 3 1 1 1 8 84 8401# 6499573: 16 161 16101 99 1 999 4633 62 2 1 2 3 1 3 1 1 6 8 84 8401# 6499574: 16 161 16101 99 1 999 4633 63 1 1 3 3 1 2 1 1 3 8 84 8401
select()
de dplyr
.Nos permite seleccionar columnas o variables, similar a función select()
de dplyr
.
Aplicar funciones específicas a ciertas variables para hacer resúmenes de los datos.
Nos permite seleccionar columnas o variables, similar a función select()
de dplyr
.
Aplicar funciones específicas a ciertas variables para hacer resúmenes de los datos.
Crear nuevas variables, similar a la función mutate()
de dplyr
.
Nos permite seleccionar columnas o variables, similar a función select()
de dplyr
.
Aplicar funciones específicas a ciertas variables para hacer resúmenes de los datos.
Crear nuevas variables, similar a la función mutate()
de dplyr
.
dt_viviendas[,"region"]# region# 1: 15# 2: 15# 3: 15
O podemos utilizar un vector con los nombres de las variables como téxto, similar a la lógica de rbase
y dplyr
.
dt[,c("region","area")]# region area# 1: 15 2# 2: 15 2# 3: 15 2
Suma
# calculamos la cantidad total de hogaresdt_viviendas[,sum(cant_hog)]# 6929655
O calcular promedios u otras funciones como: min()
,max()
, median()
, sd()
, entre otras.
# calculamos el promedio de hogares por viviendasdt_viviendas[,mean(cant_hog)]# 1.066171
Utilizando .()
es posible realizar mas de una función, este argumento funciona como list()
# calculamos el promedio de hogares por viviendasdt_viviendas[,.(mean(cant_hog),sum(cant_hog))]# V1 V2# 1: 1.066171 6929655
Dentro del argumento .()
podemos asignar nuevos nombres al resumen creado.
# calculamos el promedio de hogares por viviendas dt_viviendas[,.(promedio_hogares = mean(cant_hog),suma_hogares = sum(cant_hog))]# promedio_hogares suma_hogares# 1: 1.066171 6929655
Mediante el operador :=
podemos crear nuevas columnas, esto es homólogo a la funcion mutate()
de dplyr.
p04
dt_viviendas[,hacinamiento := p04/cant_per]dt_viviendas[,c("p04","cant_per","hacinamiento")]# p04 cant_per hacinamiento# 1: 1 1 1.0000000# 2: 98 0 Inf# 3: 2 4 0.5000000# 4: 98 0 Inf
Pero hay un detalle! que son esos Inf
?
data.table
posee una version de if_else()
, llamada fifelse()
, es similar a if_else
dt_viviendas[,hacinamiento := fifelse(p04 != 98,p04/cant_per,NA_real_,na= NA)]dt_viviendas[,c("p04","cant_per","hacinamiento")] # p04 cant_per hacinamiento # 1: 1 1 1.0000000 # 2: 98 0 NA # 3: 2 4 0.5000000 # 4: 98 0 NA
data.table
posee una función que homologa a case_when()
, llamada fcase()
.
tictoc::tic()dt_censo[, residencia_habitual := fcase(p10 == 1, "En esta vivienda", p10 == 2, "En otra vivienda", p10 == 3, "En otra comuna", p10 == 4, "En otro país", default = "missing" )][]tictoc::toc()# 0.36 sec elapseddt_censo[, residencia_habitual :=NULL]tictoc::tic()censo %>% mutate(residencia_habitual = case_when(p10 == 1 ~"En esta vivienda", p10 == 2 ~"En otra vivienda", p10 == 3 ~"En otra comuna", p10 == 4 ~"En otro país" ))tictoc::toc()# 2.33 sec elapsed
group_by()
de dplyr
.Nos permite agrupar por variables categóricas, similar a función group_by()
de dplyr
.
Esta agrupación siempre se utiliza aplicando funciones específicas a ciertas variables en el parametro j para hacer resúmenes de los datos.
Nos permite agrupar por variables categóricas, similar a función group_by()
de dplyr
.
Esta agrupación siempre se utiliza aplicando funciones específicas a ciertas variables en el parametro j para hacer resúmenes de los datos.
Debemos entregar el nombre de la variable que nos interesa agrupar en el parametro by y en el parametro j la variable y función que queremos realizar.
dt_viviendas[,mean(cant_per),by = comuna]# comuna V1# 1: 15202 37.19656# 2: 15201 106.93848# 3: 15102 125.52426
Si nos interesa obtener una cuenta de categorías agrupadas podemos utilizar la función .N
en el parametro j, esto es similar a la función count()
de dplyr
.
.()
en el parametro bydt_viviendas[,.N,by = .(area, comuna)]# area comuna N# 1: 2 15202 697# 2: 1 15201 522# 3: 2 15201 1396
La función .SD
es similar a across()
, que se utiliza en summarise()
y mutate()
, nos permite generar subgrupos dentro de un data.table.
Para ejemplificar utilicemos el clásico set de datos iris
dt_iris <- data.table(iris)# extraemos la primera fila de cada especiedt_iris[,.SD[1],by=Species]# Species Sepal.Length Sepal.Width Petal.Length Petal.Width# 1: setosa 5.1 3.5 1.4 0.2# 2: versicolor 7.0 3.2 4.7 1.4# 3: virginica 6.3 3.3 6.0 2.5# extraemos la ultima fila de cada especiedt_iris[,.SD[.N],by=Species]# Species Sepal.Length Sepal.Width Petal.Length Petal.Width# 1: setosa 5.0 3.3 1.4 0.2# 2: versicolor 5.7 2.8 4.1 1.3# 3: virginica 5.9 3.0 5.1 1.8
si queremos extraer la fila con el pétalo mas largo por especie
dt_iris[,.SD[which.min(Petal.Length)],by=Species]# Species Sepal.Length Sepal.Width Petal.Length Petal.Width# 1: setosa 4.6 3.6 1.0 0.2# 2: versicolor 5.1 2.5 3.0 1.1# 3: virginica 4.9 2.5 4.5 1.7
Esto es similar a lo que hariamos con dplyr
iris %>% group_by(Species) %>% arrange(Petal.Length) %>% slice(1)# A tibble: 3 × 5# Groups: Species [3]# Sepal.Length Sepal.Width Petal.Length Petal.Width Species # <dbl> <dbl> <dbl> <dbl> <fct> # 1 4.6 3.6 1 0.2 setosa # 2 5.1 2.5 3 1.1 versicolor# 3 4.9 2.5 4.5 1.7 virginica
Esta función tiene un parámetro para seleccionar las variables de interés .SDcols
.
dt_iris <- data.table(iris)# extraemos la primera fila de cada especie, no es algo tan novedosodt_iris[,.SD[1:3],.SDcols=1:3]# Sepal.Length Sepal.Width Petal.Length#1: 5.1 3.5 1.4#2: 4.9 3.0 1.4#3: 4.7 3.2 1.3
Adicionalmente, también puede recibir patrones de expresiones regulares
# extraemos las 3 primeras filas de las variables que tengan la palabra Sepal, es un poco mas novedosodt_iris[,.SD[1:3],.SDcols=patterns("Sepal")]# Sepal.Length Sepal.Width# 1: 5.1 3.5# 2: 4.9 3.0# 3: 4.7 3.2
Con un poco de programación funcional, calculamos el promedio de las variables que tengan la palabra Sepal, por especies.
dt_iris[,lapply(.SD,mean),.SDcols=patterns("Sepal"), by = Species]# Species Sepal.Length Sepal.Width# 1: setosa 5.006 3.428# 2: versicolor 5.936 2.770# 3: virginica 6.588 2.974# Podemos sumar funciones con c()dt_iris[,c(.N,lapply(.SD,mean)),.SDcols=patterns("Sepal"), by = Species]# Species N Sepal.Length Sepal.Width# 1: setosa 50 5.006 3.428# 2: versicolor 50 5.936 2.770# 3: virginica 50 6.588 2.974
Al comparar el desempeño de data.table
con lapply()
no parece ser mejor que el de data.table
con map()
un<- Sys.time()dt_iris[,lapply(.SD,mean),by = Species]do<- Sys.time()do -un# Species Sepal.Length Sepal.Width Petal.Length Petal.Width# 1: setosa 5.006 3.428 1.462 0.246# 2: versicolor 5.936 2.770 4.260 1.326# 3: virginica 6.588 2.974 5.552 2.026# Time difference of 0.003495932 secsun<- Sys.time() dt_iris[,purrr::map(.SD,mean),by = Species]do<- Sys.time()do -un# Species Sepal.Length Sepal.Width Petal.Length Petal.Width# 1: setosa 5.006 3.428 1.462 0.246# 2: versicolor 5.936 2.770 4.260 1.326# 3: virginica 6.588 2.974 5.552 2.026# > do<- Sys.time()# Time difference of 0.002627134 secs
Adicionalmente el parametro by, puede ser reemplazado por el parametro keyby, éste nos permite agregar un número identificador al resultado de la agrupación, lo que ordenada el resultado, similar a arrange()
o sort()
.
dt_viviendas[,.N,by=region]# region N# 1: 15 76204# 2: 14 153990# 3: 13 2378490# 4: 12 65641# 5: 11 44726# 6: 10 332935
dt_viviendas[,.N,keyby=region]# region N# 1: 1 117814# 2: 2 196360# 3: 3 121101# 4: 4 308616# 5: 5 788830# 6: 6 354324
El parametro by, también nos permite llamar a variable por grupo, para agrupar por mas de una variable.
dt_viviendas[,.N,by=p01:p03c]# p01 p02 p03a p03b p03c N# 1: 3 1 5 3 5 194# 2: 1 3 98 98 98 272946# 3: 1 1 5 3 5 2547# 4: 1 4 98 98 98 256294# 5: 3 4 98 98 98 1313# --- # 786: 7 1 3 4 2 2# 787: 7 1 4 99 5 1# 788: 5 1 5 6 99 1# 789: 7 1 3 7 5 7# 790: 7 1 4 99 3 1
Calcule la media de personas por regiones y comunas.
Calcule la media de personas por regiones y comunas.
dt_viviendas[,mean(cant_per),by = .(region,comuna)]
Obtenemos el promedio de hogares para la región metropolitana
dt_viviendas[region == 13,mean(cant_hog)]# 1.038741
Obtenemos el promedio de hogares para la región metropolitana
dt_viviendas[region == 13,mean(cant_hog)]# 1.038741
Obtenemos el promedio de hogares por area de la región metropolitana,
dt_viviendas[region == 13,mean(cant_hog),by = area]# area V1# 1: 1 1.039781# 2: 2 1.012985
Al igual que el pipe de dplyr
, %>%
, existe una manera de conectar acciones en data.table
.
ya sea:
DT[ ... ][ ... ][ ... ]
Al igual que el pipe de dplyr
, %>%
, existe una manera de conectar acciones en data.table
.
ya sea:
DT[ ... ][ ... ][ ... ]
O
Al igual que el pipe de dplyr
, %>%
, existe una manera de conectar acciones en data.table
.
ya sea:
DT[ ... ][ ... ][ ... ]
O
DT[ ... ][ ... ][ ... ]
Al igual que el pipe de dplyr
, %>%
, existe una manera de conectar acciones en data.table
.
ya sea:
DT[ ... ][ ... ][ ... ]
O
DT[ ... ][ ... ][ ... ]
Ejemplo:
dt_esp <- dt_viviendas[region %in% c(1:10),][,c("area","comuna","cant_per")][,masde_4 := fifelse(cant_per > 4,1,0)]dt_esp
O
dt_esp <- dt_viviendas[region %in% c(1:10), ][,c("area","comuna","cant_per") ][,masde_4 := fifelse(cant_per > 4,1,0)]dt_esp
Si por ejemplo queremos calcular el porcentaje en variables categóricas
### como lo hariamos con data.tabletictoc::tic()dt_censo[,.N,by=comuna][ ,porc := round(100*N/sum(N),2)][]tictoc::toc()# 0.16 sec elapsed
### como lo hariamos con dplyrtictoc::tic()censo %>% group_by(comuna) %>% summarise(N=n()) %>% mutate(porc = round(100*N/sum(N),2))tictoc::toc()# 1.08 sec elapsed
Creamos dos tablas, una de conteo de viviendas por comuna y una de conteo de personas por comunas
[]
y un parametro llamado on
, donde se define la variable que unifica ambas tablas.viv_comuna <- dt_viviendas[,.N,by = .(comuna)]censo <- data.table::fread("csv-personas-censo-2017/Microdato_Censo2017-Personas.csv")censo <- janitor::clean_names(censo)dt_censo <- as.data.table(censo)pers_comuna <- dt_censo[,.N,by = comuna]
viv_comuna[pers_comuna, on = "comuna"]# comuna N i.N# 1: 15202 697 684# 2: 15201 1918 2765# 3: 15102 948 1255# 4: 15101 72641 221364
Creamos dos tablas, una de conteo de viviendas por comuna y una de conteo de personas por comunas
[]
y un parametro llamado on
, donde se define la variable que unifica ambas tablas.viv_comuna <- dt_viviendas[,.N,by = .(comuna)]censo <- data.table::fread("csv-personas-censo-2017/Microdato_Censo2017-Personas.csv")censo <- janitor::clean_names(censo)dt_censo <- as.data.table(censo)pers_comuna <- dt_censo[,.N,by = comuna]
viv_comuna[pers_comuna, on = "comuna"]# comuna N i.N# 1: 15202 697 684# 2: 15201 1918 2765# 3: 15102 948 1255# 4: 15101 72641 221364
Este ejemplo es similar a un left_join()
de dplyr.
Obtener con data.table, la comuna con mayor población por region ( con la tabla de viviendas)
Obtener con data.table, la comuna con mayor población por region ( con la tabla de viviendas)
dt_viviendas[,.(personas = sum(cant_per)),by=.(comuna,region)][,.SD[which.max(personas)],by = region]# verifiquemoslo con la tabla de personasdt_censo[,.(personas = .N),by=.(comuna,region)][,.SD[which.max(personas)],by = region]# ¿que comunas son?comunas <- read.csv2("C:/Users/Ricardo/Documents/INE/Clases R/Intermedio/data.table_intermedio/data/personas/etiquetas_persona_comuna_15r.csv")dt_censo[,.(personas = .N),by=.(comuna,region)][,.SD[which.max(personas)],by = region][comunas, on = "comuna==valor", nomatch=NULL]
Nada de esto sería posible sin:
Xaringan: Presentation Ninja, de Yihui Xie. Para generar esta presentación con la plantilla ninja ⚔
R for Data Science tiene una traducción al español realizada por la comunidad hispana de R:
data.table
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |