Capitulo 7 data.table

El package data.table es un paquete que lleva la eficiencia al siguiente nivel. Como dije, la sintaxis es algo menos intuitiva que el lenguaje tidyverse pero todo sea por la eficiencia :).

Los Data Table pueden ser utilizados como data.rame. Si se hace un class(...) de un data.table se imprime una lista con dos valores: “data.table” “data.frame”, identificando que los objetos data.able también pueden ser considerados como data.frame y las librerías que solo usan data.frame no tendrían problemas al usar data.table.

7.1 Importar y exportar datos

El package data.table tiene sus propias funciones para importar y exportar datos. Las funciones son fread() y fwrite(). Estas funciones sirven para cargar archivos tabulares, o de texto, separados por cierto separados. Aquí tenemos los argumentos más importantes de estas funciones:

  • x: el objeto que queremos exportar. En caso de importar, esto no hace falta.
  • file: el lugar donde queremos guardar el archivo y su extensión.
  • sep: el delimitador entre columnas. En un archivo csv por lo general es “,”
  • dec: el separador decimal.
  • dateTimeAs: formato en el que guardar objetos de fecha. Por defecto es “ISO”.

Vamos a probar a cargar “Historicos”

data.link = "C:/Users/ip30/Dropbox/Terreno/cursos/Formadores IT/Curso intro R/Curso R base/datos/"
HS <- fread(paste0(data.link,"Historicos_siniestros.txt"),sep=";",dec=",")
str(HS)

## Classes 'data.table' and 'data.frame':   29096 obs. of  15 variables:
##  $ CO_SINIESTRO               : int  2361644 2361644 2361644 2361644 2361644 2361644 2361644 2361644 2361644 2361644 ...
##  $ IN_ORDEN                   : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ FX_OPERACION               : chr  "04Jul2018" "01Aug2018" "02Aug2018" "10Aug2018" ...
##  $ FX_DECLARACION_SINIESTRO   : chr  "04Jul2018" "01Aug2018" "01Aug2018" "01Aug2018" ...
##  $ FX_ENTRADA_SINIESTRO       : chr  "04Jul2018" "01Aug2018" "01Aug2018" "01Aug2018" ...
##  $ FX_ADMISION_SINIESTRO      : chr  "" "" "02Aug2018" "02Aug2018" ...
##  $ CO_SITUACION_SINIESTRO     : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ CO_MOTIVO_RECHAZO_SINIESTRO: int  0 0 0 0 0 0 0 0 0 0 ...
##  $ CO_TIPO_ESTADO_SINIESTRO   : int  9 4 5 5 5 1 1 3 3 3 ...
##  $ IM_CREDITO_TOTAL           : num  7000 7000 4200 4200 4200 4200 4200 4200 4200 4200 ...
##  $ IM_CREDITO_PENDIENTE       : num  7000 7000 4200 4200 4200 4200 4200 4200 4200 4200 ...
##  $ IM_SUMA_ASEGURADA          : num  0 0 3780 3780 3780 3780 3780 3780 3780 3780 ...
##  $ IM_SUMA_GARANTIZADA        : num  0 0 3213 3213 3213 ...
##  $ IM_SINIESTRO_CREDITO       : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ IM_SINIESTRO_CREDITO_ACU   : num  0 0 0 0 0 0 0 0 0 0 ...
##  - attr(*, ".internal.selfref")=<externalptr>

Como vemos, el formato data.table es algo diferente a los tibble de tidyverse, pero no os preocupeis, es muy parecido :)

Cargar fechas es una de las limitaciones actuales de read.table, pero seguramente pronto renovarán la función para ello. Podemos usar la función que vimos anydate() o anytime() para poner bien las variables que nos interesan.

7.2 Sintaxis base

Esta es la base para manejarnos con una data.table: DT[ i , j , by ]. Tiene tres partes que hemos llamado i, j y by:

  • i: corresponde a las líneas con las que queremos trabajar.
  • j: corresponde a las columnas y la usaremos para hacer cálculos y resúmenes, seleccionar variables y crear nuevas variables.
  • by: corresponde a las agrupaciones que queremos hacer.

7.2.1 La parte i

La parte i se usa para filtrar las líneas con las que queremos trabajar. Por ejemplo:

IRIS = datasets::iris
setDT(IRIS)
IRIS[2:6,,]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1:          4.9         3.0          1.4         0.2  setosa
## 2:          4.7         3.2          1.3         0.2  setosa
## 3:          4.6         3.1          1.5         0.2  setosa
## 4:          5.0         3.6          1.4         0.2  setosa
## 5:          5.4         3.9          1.7         0.4  setosa
IRIS[2:6, , ] 
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1:          4.9         3.0          1.4         0.2  setosa
## 2:          4.7         3.2          1.3         0.2  setosa
## 3:          4.6         3.1          1.5         0.2  setosa
## 4:          5.0         3.6          1.4         0.2  setosa
## 5:          5.4         3.9          1.7         0.4  setosa

O también podemos usar nombres de columnas y condiciones para hacer esto:

IRIS[Species %in% c("setosa","virginica")][1:4]
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1:          5.1         3.5          1.4         0.2  setosa
## 2:          4.9         3.0          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

Y podemos poner varias condiciones:

IRIS[Species == "setosa" | Species=="virginica" ][1:5]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1:          5.1         3.5          1.4         0.2  setosa
## 2:          4.9         3.0          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.0         3.6          1.4         0.2  setosa
IRIS[Species %in% c("setosa","virginica") & Petal.Width < 1][1:3]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1:          5.1         3.5          1.4         0.2  setosa
## 2:          4.9         3.0          1.4         0.2  setosa
## 3:          4.7         3.2          1.3         0.2  setosa

Fijaros, que he concatenado varios [] seguidos, y le hemos dado la forma IRIS[][]. Esto es una concatenación de acciones, muy parecido a lo que haciamos con %>% en tidyverse.

7.2.2 La parte j

La parte j se usa para seleccionar columnas, crear nuevas variables y aplicar cosas o funciones sobre ellas ellas.

7.2.2.1 Operaciones y resumen de variales

Podemos sacar valores de interés

IRIS[1:20,mean(Petal.Length)]
## [1] 1.435
IRIS[,table(Species)]
## Species
##     setosa versicolor  virginica 
##         50         50         50

En la primera línea hemos sacado la media de “Sepal.Length” para las 20 primeras observaciones. Y en la segunda línea hemos sacado cuantas observaciones tenemos para cada “Species” :)

Si queremos seleccionar varias columnas en la parte j y queremos hacer cosas con ellas, debemos usar el verbo .(). Es como si usásemos el verbo list()

IRIS[,.(mean(Sepal.Width),mean(Petal.Length))]
##          V1    V2
## 1: 3.057333 3.758

Y podemos ponerle nombres para ser claros:

IRIS[,.(media_SW = mean(Sepal.Width),media_PL = mean(Petal.Length))]
##    media_SW media_PL
## 1: 3.057333    3.758

7.2.2.2 Seleccionar

Podemos seleccionar variables.

IRIS[1:4,.(Petal.Length,Petal.Width)]
##    Petal.Length Petal.Width
## 1:          1.4         0.2
## 2:          1.4         0.2
## 3:          1.3         0.2
## 4:          1.5         0.2

7.2.2.3 Crear

Mediante := también podemos crear nuevas columnas:

IRIS[, sepal_area := Sepal.Length*Sepal.Width][1:3]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species sepal_area
## 1:          5.1         3.5          1.4         0.2  setosa      17.85
## 2:          4.9         3.0          1.4         0.2  setosa      14.70
## 3:          4.7         3.2          1.3         0.2  setosa      15.04

Y si queremos hacer esto con varias variables:

IRIS[, `:=`(sepal_area = Sepal.Length*Sepal.Width,
            petal_area = Petal.Length*Petal.Width)][1:3]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species sepal_area
## 1:          5.1         3.5          1.4         0.2  setosa      17.85
## 2:          4.9         3.0          1.4         0.2  setosa      14.70
## 3:          4.7         3.2          1.3         0.2  setosa      15.04
##    petal_area
## 1:       0.28
## 2:       0.28
## 3:       0.26

# o
IRIS[, c("sepal_area", "petal_area") := .(Sepal.Length*Sepal.Width, Petal.Length*Petal.Width)][1:3]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species sepal_area
## 1:          5.1         3.5          1.4         0.2  setosa      17.85
## 2:          4.9         3.0          1.4         0.2  setosa      14.70
## 3:          4.7         3.2          1.3         0.2  setosa      15.04
##    petal_area
## 1:       0.28
## 2:       0.28
## 3:       0.26

y si queremos crear variables de forma secuencial:

IRIS[, c("sepal_area", "cuadrado_sepal_area") := .(a <- Sepal.Length*Sepal.Width, a^2)][1:3]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species sepal_area
## 1:          5.1         3.5          1.4         0.2  setosa      17.85
## 2:          4.9         3.0          1.4         0.2  setosa      14.70
## 3:          4.7         3.2          1.3         0.2  setosa      15.04
##    petal_area cuadrado_sepal_area
## 1:       0.28            318.6225
## 2:       0.28            216.0900
## 3:       0.26            226.2016

7.2.3 La parte by

Como podemos imaginar, se hacen las agrupaciones que puedan interesarnos para sacar operaciones sobre la parte j. Por ejemplo:

IRIS[,.(media_PL = mean(Petal.Length)),by=Species]
##       Species media_PL
## 1:     setosa    1.462
## 2: versicolor    4.260
## 3:  virginica    5.552
IRIS[,.(media_PL = mean(Petal.Length)), Species]
##       Species media_PL
## 1:     setosa    1.462
## 2: versicolor    4.260
## 3:  virginica    5.552

Sacamos la media de “Petal.Length” por “Species”. Igual que antes, si queremos agrupar por varias variables, usaremos .():

IRIS[,col := rep(c("A","B"),length.out=nrow(IRIS))] ## hemos creado una nueva variables en IRIS
IRIS[1:7]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species sepal_area
## 1:          5.1         3.5          1.4         0.2  setosa      17.85
## 2:          4.9         3.0          1.4         0.2  setosa      14.70
## 3:          4.7         3.2          1.3         0.2  setosa      15.04
## 4:          4.6         3.1          1.5         0.2  setosa      14.26
## 5:          5.0         3.6          1.4         0.2  setosa      18.00
## 6:          5.4         3.9          1.7         0.4  setosa      21.06
## 7:          4.6         3.4          1.4         0.3  setosa      15.64
##    petal_area cuadrado_sepal_area col
## 1:       0.28            318.6225   A
## 2:       0.28            216.0900   B
## 3:       0.26            226.2016   A
## 4:       0.30            203.3476   B
## 5:       0.28            324.0000   A
## 6:       0.68            443.5236   B
## 7:       0.42            244.6096   A

IRIS[,.(media_PL = mean(Petal.Length)),by=.(Species,col)]
##       Species col media_PL
## 1:     setosa   A    1.456
## 2:     setosa   B    1.468
## 3: versicolor   A    4.308
## 4: versicolor   B    4.212
## 5:  virginica   A    5.564
## 6:  virginica   B    5.540

7.2.4 Algunas otras funciones interesantes:

7.2.4.1 .N

.N en i nos da la última fila. En j el número de filas en un grupo.

IRIS[.N,1:3]
##    Sepal.Length Sepal.Width Petal.Length
## 1:          5.9           3          5.1

# o
IRIS[nrow(IRIS),1:3]
##    Sepal.Length Sepal.Width Petal.Length
## 1:          5.9           3          5.1

IRIS[ , .N , Species]
##       Species  N
## 1:     setosa 50
## 2: versicolor 50
## 3:  virginica 50

7.2.4.2 .I

.I en j nos da un vector con los números de fila de los dato en la table (filtrados por i ).

(a <- IRIS[,.I[Species=="virginica"]])
##  [1] 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
## [20] 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
## [39] 139 140 141 142 143 144 145 146 147 148 149 150

IRIS[a[1:3]]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species sepal_area
## 1:          6.3         3.3          6.0         2.5 virginica      20.79
## 2:          5.8         2.7          5.1         1.9 virginica      15.66
## 3:          7.1         3.0          5.9         2.1 virginica      21.30
##    petal_area cuadrado_sepal_area col
## 1:      15.00            432.2241   A
## 2:       9.69            245.2356   B
## 3:      12.39            453.6900   A

7.2.4.3 .SD

.SD en j nos trabaja sobre el subgrupo de datos seleccionados en by.

IRIS[,index := 1:nrow(IRIS)] #  crear variable index
IRIS[,.SD[1],Species] ## primera fila de cada grupoIRIS
##       Species Sepal.Length Sepal.Width Petal.Length Petal.Width sepal_area
## 1:     setosa          5.1         3.5          1.4         0.2      17.85
## 2: versicolor          7.0         3.2          4.7         1.4      22.40
## 3:  virginica          6.3         3.3          6.0         2.5      20.79
##    petal_area cuadrado_sepal_area col index
## 1:       0.28            318.6225   A     1
## 2:       6.58            501.7600   A    51
## 3:      15.00            432.2241   A   101


IRIS[,.SD[.N],Species] ## última fila de cada grupo
##      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

IRIS[,.SD[c(1,.N)],Species] ## primera y última fila de cada grupo
##      Species Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1:     setosa          5.1         3.5          1.4         0.2
## 2:     setosa          5.0         3.3          1.4         0.2
## 3: versicolor          7.0         3.2          4.7         1.4
## 4: versicolor          5.7         2.8          4.1         1.3
## 5:  virginica          6.3         3.3          6.0         2.5
## 6:  virginica          5.9         3.0          5.1         1.8

Y dentro de SD podemos hacer aplicar funciones usando la función vectorizada lapply():

.SDcols especifica las columnas de la data.table que se incluyen en .SD. Por lo tanto, si queremos sacar la media de las variables “Sepal.Width”, “Sepal.Length” y “Petal.Length” por especies, podemos elegir trabajar sólo con esas columnas usando .SDcols:

IRIS[ , lapply(.SD, mean), .SDcols = c("Sepal.Width", "Sepal.Length", "Petal.Length") , by=Species]
##      Species Sepal.Width Sepal.Length Petal.Length
## 1:     setosa       3.428        5.006        1.462
## 2: versicolor       2.770        5.936        4.260
## 3:  virginica       2.974        6.588        5.552

7.2.4.4 .GRP

.GRP en j nos da la indexación de los grupos que tenemos.

IRIS[ , i := .GRP, by = Species]
IRIS
     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species i col
##   1:          5.1         3.5          1.4         0.2    setosa 1   A
##   2:          4.9         3.0          1.4         0.2    setosa 1   B
##   3:          4.7         3.2          1.3         0.2    setosa 1   A
##   4:          4.6         3.1          1.5         0.2    setosa 1   B
##   5:          5.0         3.6          1.4         0.2    setosa 1   A
##  ---                                                                  
## 146:          6.7         3.0          5.2         2.3 virginica 3   B
## 147:          6.3         2.5          5.0         1.9 virginica 3   A
## 148:          6.5         3.0          5.2         2.0 virginica 3   B
## 149:          6.2         3.4          5.4         2.3 virginica 3   A
## 150:          5.9         3.0          5.1         1.8 virginica 3   B

# o 
IRIS[ , i := .GRP, by = .(Species,col)]
IRIS
     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species i col
##   1:          5.1         3.5          1.4         0.2    setosa 1   A
##   2:          4.9         3.0          1.4         0.2    setosa 2   B
##   3:          4.7         3.2          1.3         0.2    setosa 1   A
##   4:          4.6         3.1          1.5         0.2    setosa 2   B
##   5:          5.0         3.6          1.4         0.2    setosa 1   A
##  ---                                                                  
## 146:          6.7         3.0          5.2         2.3 virginica 6   B
## 147:          6.3         2.5          5.0         1.9 virginica 5   A
## 148:          6.5         3.0          5.2         2.0 virginica 6   B
## 149:          6.2         3.4          5.4         2.3 virginica 5   A
## 150:          5.9         3.0          5.1         1.8 virginica 6   B

7.2.5 Algunos ejercicios

7.2.5.1 Base de datos de juguete

Volvemos al paquete nycflights13 y la base de datos data.frame flights.

Utiliza data.table() para responder a las siguientes preguntas:

  • ¿Cuantos vuelos se realizan en total cada mes?
  • ¿Qué aeropuerto acumula el mayor número de salidas de vuelos en todo el año?
  • ¿Qué compañía acumula el mayor número de salida de vuelos en los meses de verano (jun-sep.)?

Lo primero que debemos hacer es convertir la tabla a formato data.table(), i.e.:

vuelos = as.data.table(flights)

7.2.5.2 Nuestra base de datos

  • Cargar los datos “Siniestros” e “HistoricosSiniestros”, y poner todas las variables como queremos.

  • Para poner bien las fechas, recordad como se nombran variables en R y el paquete anytime. Si teneis una base de datos enorme, la función anytime() puedee ser un poco lenta. El paquete fasttime es el más eficiente, pero puede ser complicado de usar dependiendo el formato de fecha/hora que tengamos.

  • Crear la variable “Mes_FX_FACTURA”. Recordad el paquete lubridate.

  • En “HistoricosSiniestros” sacar el último registro de todos los CO_registro

  • Agrupar las sumas garantizadas por més de declaración

  • Agrupar las sumas garantizadas por día de declaración a partir del 5 de agosto

  • Ordenar por día el resultado

  • Hacedme una gráfica para ver la suma garantizada

7.3 Concatenar

Como ya hemos visto, en data.table podemos concatenar acciones con diferentes [] seguidos:

(DT <- data.table(V1=c(1L,2L), V2=LETTERS[1:3],  V3=round(rnorm(4),4),V4=1:12))
##     V1 V2      V3 V4
##  1:  1  A  1.0963  1
##  2:  2  B -0.9033  2
##  3:  1  C -1.1891  3
##  4:  2  A  1.0650  4
##  5:  1  B  1.0963  5
##  6:  2  C -0.9033  6
##  7:  1  A -1.1891  7
##  8:  2  B  1.0650  8
##  9:  1  C  1.0963  9
## 10:  2  A -0.9033 10
## 11:  1  B -1.1891 11
## 12:  2  C  1.0650 12

DT[,V4.cumsum:=cumsum(V4)]
DT
##     V1 V2      V3 V4 V4.cumsum
##  1:  1  A  1.0963  1         1
##  2:  2  B -0.9033  2         3
##  3:  1  C -1.1891  3         6
##  4:  2  A  1.0650  4        10
##  5:  1  B  1.0963  5        15
##  6:  2  C -0.9033  6        21
##  7:  1  A -1.1891  7        28
##  8:  2  B  1.0650  8        36
##  9:  1  C  1.0963  9        45
## 10:  2  A -0.9033 10        55
## 11:  1  B -1.1891 11        66
## 12:  2  C  1.0650 12        78

DT[,V4.cumsum:=cumsum(V4)] [order(-V3)]
##      V1 V2      V3 V4 V4.cumsum
##  1:  1  A  1.0963  1         1
##  2:  1  B  1.0963  5        15
##  3:  1  C  1.0963  9        45
##  4:  2  A  1.0650  4        10
##  5:  2  B  1.0650  8        36
##  6:  2  C  1.0650 12        78
##  7:  2  B -0.9033  2         3
##  8:  2  C -0.9033  6        21
##  9:  2  A -0.9033 10        55
## 10:  1  C -1.1891  3         6
## 11:  1  A -1.1891  7        28
## 12:  1  B -1.1891 11        66

DT[,V4.cumsum:=cumsum(V4)] [order(-V3)] [V4<6]
##    V1 V2      V3 V4 V4.cumsum
## 1:  1  A  1.0963  1         1
## 2:  1  B  1.0963  5        15
## 3:  2  A  1.0650  4        10
## 4:  2  B -0.9033  2         3
## 5:  1  C -1.1891  3         6

7.4 JOINS

Se puede usar sintaxis data.table (DT) o la función merge()

Tipos:

  • INNER: en DT = X[Y, nomatch=0]. En merge() = merge(X, Y, all=FALSE)
  • LEFT OUTER: en DT = Y[X]. En merge() = merge(X, Y, all.x=TRUE)
  • RIGHT OUTER: en DT = X[Y]. En merge() = merge(X, Y, all.y=TRUE)
  • FULL OUTER: en DT no se puede. En merge() = merge(X, Y, all=TRUE)

7.4.1 INNER

Sólo retorna las filas de df1 que tienen una equivalencia en df2,

(df1 <- data.table(x = c(1, 2), y = 2:1))
##    x y
## 1: 1 2
## 2: 2 1

(df2 <- data.table(x = c(1, 3), a = 10, b = "a"))
##    x  a b
## 1: 1 10 a
## 2: 3 10 a

df2[df1,nomatch=0,on=c("x")]
##    x  a b y
## 1: 1 10 a 2

# o
merge(df1, df2, all=FALSE)
##    x y  a b
## 1: 1 2 10 a

7.4.2 LEFT OUTER

Retorna todas las columnas de df1 y df2, pero sólo retorna todas las filas de df1.

df2[df1,,on=c("x")]
##    x  a    b y
## 1: 1 10    a 2
## 2: 2 NA <NA> 1

# o
merge(df1, df2, all.x=T)
##    x y  a    b
## 1: 1 2 10    a
## 2: 2 1 NA <NA>

7.4.3 RIGHT OUTER

Retorna todas las columnas de df2 y df1, pero sólo retorna todas las filas de df2.

df1[df2,,on=c("x")]
##    x  y  a b
## 1: 1  2 10 a
## 2: 3 NA 10 a

# o
merge(df1, df2, all.y=T)
##    x  y  a b
## 1: 1  2 10 a
## 2: 3 NA 10 a

7.4.4 FULL OUTER

Retorna todas las columnas de df1 y df2, y todas las filas de df1 y df2.

merge(df1, df2, all=T)
##    x  y  a    b
## 1: 1  2 10    a
## 2: 2  1 NA <NA>
## 3: 3 NA 10    a

7.4.5 setkey() y setkeyv()

Si queremos usar la notación data.table() sin usar el argumento on podemos usar la función setkey() para decirle a cada “data.table” cual es la “clave” de la base de datos:

df1 <- data.table(x = c("X", "Z"), y = 2:1)
df2 <- data.table(x = c("X", "V"), a = 10, b = "a")
setkey(df1,x)
setkey(df2,x)
df1[df2]
##    x  y  a b
## 1: V NA 10 a
## 2: X  2 10 a
df2[df1]
##    x  a    b y
## 1: X 10    a 2
## 2: Z NA <NA> 1

Y ahora, la selección de filas también es directa en base a la “key”

df1["X"]
##    x y
## 1: X 2

Si queremos pasarle varias columnas para ordenar, usaremos la función setkeyv()

df1 <- data.table(x = c(1, 2), y = 2:1)
df2 <- data.table(x = c(1, 3),y=1, a = 10, b = "a")
setkeyv(df1,c("x","y"))
setkeyv(df2,c("x","y"))
df1[df2]
##    x y  a b
## 1: 1 1 10 a
## 2: 3 1 10 a

7.4.6 ANTI JOIN

(df1 <- data.table(V1 = c(1, 1, 3, 4), V2 = 1:4))
##    V1 V2
## 1:  1  1
## 2:  1  2
## 3:  3  3
## 4:  4  4

(df2 <- data.table(V1 = c(1, 1, 2), V3 = c("a", "b", "a")))
##    V1 V3
## 1:  1  a
## 2:  1  b
## 3:  2  a

df2[!df1,,on=c("V1")]
##    V1 V3
## 1:  2  a

# y del otro lado
df1[!df2,,on=c("V1")]
##    V1 V2
## 1:  3  3
## 2:  4  4

7.5 Funciones interesantes

7.5.1 Familia set*()

7.5.1.1 Renombrar variables setnames()

setnames(IRIS, "sepal_area", "Area_de_Sepalo")
IRIS[1:2]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species i col Area_de_Sepalo petal_area
## 1:          5.1         3.5          1.4         0.2  setosa 1   A          17.85       0.28
## 2:          4.9         3.0          1.4         0.2  setosa 1   B          14.70       0.28

“sepal_area” pasa a llamarse “Area_de_Sepalo” :)

7.5.1.2 Reordenar columnas setcolorder()

setcolorder(IRIS, c("Species","Area_de_Sepalo")) ## poner las dos primeras columnas
IRIS[1:2]
##    Species Area_de_Sepalo Sepal.Length Sepal.Width Petal.Length Petal.Width i col petal_area
## 1:  setosa          17.85          5.1         3.5          1.4         0.2 1   A       0.28
## 2:  setosa          14.70          4.9         3.0          1.4         0.2 1   B       0.28

7.5.1.3 Ordenar con setorder() y setorderv()

setorder(IRIS, "Petal.Width") ## ordenar por "Petal.Width"
IRIS[1:5]
##    Species Area_de_Sepalo Sepal.Length Sepal.Width Petal.Length Petal.Width i col petal_area
## 1:  setosa          15.19          4.9         3.1          1.5         0.1 1   B       0.15
## 2:  setosa          14.40          4.8         3.0          1.4         0.1 1   A       0.14
## 3:  setosa          12.90          4.3         3.0          1.1         0.1 1   B       0.11
## 4:  setosa          21.32          5.2         4.1          1.5         0.1 1   A       0.15
## 5:  setosa          17.64          4.9         3.6          1.4         0.1 1   B       0.14

setorderv(IRIS, c("Petal.Length","Petal.Width")) ## ordenar por dos variables
IRIS[1:5]
##    Species Area_de_Sepalo Sepal.Length Sepal.Width Petal.Length Petal.Width i col petal_area
## 1:  setosa          16.56          4.6         3.6          1.0         0.2 1   A       0.20
## 2:  setosa          12.90          4.3         3.0          1.1         0.1 1   B       0.11
## 3:  setosa          23.20          5.8         4.0          1.2         0.2 1   A       0.24
## 4:  setosa          16.00          5.0         3.2          1.2         0.2 1   B       0.24
## 5:  setosa          15.04          4.7         3.2          1.3         0.2 1   A       0.26

7.5.1.4 Ordenar con setkey() y setkeyv()

Como ya vimos antes, estas funciones, ordenan de forma “oficial” los datos.

setkey(IRIS, "Petal.Width") ## ordenar por "Petal.Width"
str(IRIS)
## Classes ‘data.table’ and 'data.frame':   150 obs. of  9 variables:
##  $ Species       : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ Area_de_Sepalo: num  12.9 14.4 17.6 15.2 21.3 ...
##  $ Sepal.Length  : num  4.3 4.8 4.9 4.9 5.2 4.6 5.8 5 4.7 5.5 ...
##  $ Sepal.Width   : num  3 3 3.6 3.1 4.1 3.6 4 3.2 3.2 3.5 ...
##  $ Petal.Length  : num  1.1 1.4 1.4 1.5 1.5 1 1.2 1.2 1.3 1.3 ...
##  $ Petal.Width   : num  0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.2 0.2 ...
##  $ i             : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ col           : chr  "B" "A" "B" "B" ...
##  $ petal_area    : num  0.11 0.14 0.14 0.15 0.15 0.2 0.24 0.24 0.26 0.26 ...
##  - attr(*, ".internal.selfref")=<externalptr> 
##  - attr(*, "sorted")= chr "Petal.Width"

setkeyv(IRIS, c("Petal.Length","Petal.Width")) ## ordenar por dos variables
str(IRIS)
## Classes ‘data.table’ and 'data.frame':   150 obs. of  9 variables:
##  $ Species       : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ Area_de_Sepalo: num  16.6 12.9 23.2 16 15 ...
##  $ Sepal.Length  : num  4.6 4.3 5.8 5 4.7 5.5 4.4 4.4 5 4.5 ...
##  $ Sepal.Width   : num  3.6 3 4 3.2 3.2 3.5 3 3.2 3.5 2.3 ...
##  $ Petal.Length  : num  1 1.1 1.2 1.2 1.3 1.3 1.3 1.3 1.3 1.3 ...
##  $ Petal.Width   : num  0.2 0.1 0.2 0.2 0.2 0.2 0.2 0.2 0.3 0.3 ...
##  $ i             : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ col           : chr  "A" "B" "A" "B" ...
##  $ petal_area    : num  0.2 0.11 0.24 0.24 0.26 0.26 0.26 0.26 0.39 0.39 ...
##  - attr(*, ".internal.selfref")=<externalptr> 
##  - attr(*, "sorted")= chr [1:2] "Petal.Length" "Petal.Width"

7.5.1.5 Quitar columna := NULL

IRIS[,c("col","i"):=NULL]
IRIS[1:3]
##    Species Area_de_Sepalo Sepal.Length Sepal.Width Petal.Length Petal.Width petal_area
## 1:  setosa          16.56          4.6         3.6          1.0         0.2       0.20
## 2:  setosa          12.90          4.3         3.0          1.1         0.1       0.11
## 3:  setosa          23.20          5.8         4.0          1.2         0.2       0.24

7.5.2 fcase

“CASE” - “WHEN” de data.table. Funciona de esta manera: fcase(test, TRUE, test, TRUE, ..., default=NA)

Volvemos a la base de datos “gapminder”. Vamos a calificar entre países buenos y malos en base a su esperanza de vida.

df <- gapminder
setDT(df)

df[,.(fcase(lifeExp > mean(lifeExp),"good",
            lifeExp < mean(lifeExp),"bad"))]
##         V1
##    1:  bad
##    2:  bad
##    3:  bad
##    4:  bad
##    5:  bad
##   ---     
## 1700: good
## 1701: good
## 1702:  bad
## 1703:  bad
## 1704:  bad

df[,.(fcase(lifeExp > mean(lifeExp),"good",
            default = "bad"))]
##         V1
##    1:  bad
##    2:  bad
##    3:  bad
##    4:  bad
##    5:  bad
##   ---     
## 1700: good
## 1701: good
## 1702:  bad
## 1703:  bad
## 1704:  bad

Y si queremos poner más de una condición:


df[,.(fcase(lifeExp > mean(lifeExp) & gdpPercap > mean(gdpPercap),"good",
            lifeExp < mean(lifeExp) & gdpPercap < mean(gdpPercap),"bad",
            default="medium"))]

##           V1
##    1:    bad
##    2:    bad
##    3:    bad
##    4:    bad
##    5:    bad
##   ---       
## 1700: medium
## 1701: medium
## 1702:    bad
## 1703:    bad
## 1704:    bad

7.5.2.1 Ejercicio

Cread una nueva columna con el calificativo “good” or “bad” en base a la esperanza de vida.

7.5.3 fifelse

Funciona de esta manera: fifelse(test, yes, no, na=NA)

df[,.(fifelse(lifeExp > mean(lifeExp),"good","bad"))]
##         V1
##    1:  bad
##    2:  bad
##    3:  bad
##    4:  bad
##    5:  bad
##   ---     
## 1700: good
## 1701: good
## 1702:  bad
## 1703:  bad
## 1704:  bad

7.5.3.1 Ejercicio

Poned el calificativo “good” or “bad” comparando las medias de esperanza de vida de cada continenete para 2007. Osea, usad By = continent

7.5.4 rbindlist

Funciona de esta manera: rbindlist( l , use.names = TRUE, fill = FALSE)

Argumentos:

  • l: una lista que contiene objetos data.table, data.frame o lista.
  • use.names: TRUE junta por los nombres de las columnas que hacen “match”. FALSE las junta por posición.
  • fill: TRUE rellena usando NAs.

Tablas con las mismas variables

(DT1 = data.table(A=1:3,B=letters[1:3]))
##    A B
## 1: 1 a
## 2: 2 b
## 3: 3 c
(DT2 = data.table(A=4:5,B=letters[4:5]))
##    A B
## 1: 4 d
## 2: 5 e
l = list(DT1,DT2)
rbindlist(l)
##    A B
## 1: 1 a
## 2: 2 b
## 3: 3 c
## 4: 4 d
## 5: 5 e

Tablas con variables diferentes

DT1 = data.table(A=1:3,B=letters[1:3])
DT2 = data.table(B=letters[4:5],C=factor(1:2))
l = list(DT1,DT2)
rbindlist(l, use.names=TRUE, fill=T)
##     A B    C
## 1:  1 a <NA>
## 2:  2 b <NA>
## 3:  3 c <NA>
## 4: NA d    1
## 5: NA e    2

7.5.5 duplicated

Esta función nos da un vector lógico sobre las filas que hay duplicadas

Funciona de esta manera: duplicated(x, fromLast=FALSE, by=seq_along(x), ...)

  • x: una data.table.
  • fromLast: dirección en la que te dice la observación que se duplica.
  • by: las variables en las que fijarse para ver duplicados.
(DT <- data.table(A = rep(1:3, each=4), B = rep(1:4, each=3),
                  C = rep(1:2, 6), key = "A,B"))
##     A B C
##  1: 1 1 1
##  2: 1 1 2
##  3: 1 1 1
##  4: 1 2 2
##  5: 2 2 1
##  6: 2 2 2
##  7: 2 3 1
##  8: 2 3 2
##  9: 3 3 1
## 10: 3 4 2
## 11: 3 4 1
## 12: 3 4 2
duplicated(DT)
##  [1] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
duplicated(DT, by="B")
##  [1] FALSE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE
duplicated(DT, by=c("A", "C"))
##  [1] FALSE FALSE  TRUE  TRUE FALSE FALSE  TRUE  TRUE FALSE FALSE  TRUE  TRUE

7.5.6 unique

Funciona igual que duplicated, pero nos da la DT sin duplicados

unique(DT)
##     A B C
##  1: 1 1 1
##  2: 1 1 2
##  3: 1 2 2
##  4: 2 2 1
##  5: 2 2 2
##  6: 2 3 1
##  7: 2 3 2
##  8: 3 3 1
##  9: 3 4 2
## 10: 3 4 1
unique(DT, by="B")
##    A B C
## 1: 1 1 1
## 2: 1 2 2
## 3: 2 3 1
## 4: 3 4 2
unique(DT, by=c("A", "C"))
##    A B C
## 1: 1 1 1
## 2: 1 1 2
## 3: 2 2 1
## 4: 2 2 2
## 5: 3 3 1
## 6: 3 4 2

7.5.7 uniqueN

Esta función nos da el número de niveles diferentes en la variable. Funciona muy parecido a unique

uniqueN(DT)
## [1] 10
uniqueN(DT, by="B")
## [1] 4
uniqueN(DT, by=c("A", "C"))
## [1] 6

7.5.8 between

Esta función nos da un vector lógico. TRUE si el valor está entre los valores que proporcionemos (lower <= x & x <= upper).

Funciona de esta manera: between(x, lower, upper, incbounds=TRUE)

  • x: cualquier variable a la que le podamos aplicar un <=.
  • lower: el corte por debajo.
  • upper: el corte por encima.

También puede usarse%between%

(X = data.table(a=1:5, b=6:10, c=c(5:1)))
##    a  b c
## 1: 1  6 5
## 2: 2  7 4
## 3: 3  8 3
## 4: 4  9 2
## 5: 5 10 1
X[between(b, 7, 9)]
##    a b c
## 1: 2 7 4
## 2: 3 8 3
## 3: 4 9 2
X[b %between% c(7,9)]
##    a b c
## 1: 2 7 4
## 2: 3 8 3
## 3: 4 9 2

7.5.9 fsort

Esta función nos ordena un vector en orden ascendente os descendente:

Funciona de esta manera: fsort(x, decreasing = FALSE, na.last = FALSE)

  • x: un vector
  • decreasing: TRUE ordena de forma decreciente. FALSE, ascendiente.
  • na.last: donde ponemos los NA. TRUE al final, FALSE al principio.
(x=runif(10))
##  [1] 0.1859589 0.8206501 0.7289161 0.2601126 0.2932034 0.4763283 0.6727095
##  [8] 0.3047416 0.9650763 0.9364433
fsort(x)
##  [1] 0.1859589 0.2601126 0.2932034 0.3047416 0.4763283 0.6727095 0.7289161
##  [8] 0.8206501 0.9364433 0.9650763
fsort(x,decreasing = T)
##  [1] 0.9650763 0.9364433 0.8206501 0.7289161 0.6727095 0.4763283 0.3047416
##  [8] 0.2932034 0.2601126 0.1859589

7.5.10 like

Esta función nos busca secuencias de letras (strings) en el vector. Se usa para vectores de tipo nombre generalmente

Funciona de esta manera: like(vector, pattern, ignore.case = FALSE)

  • vector: pues eso, un vector.
  • pattern: el patrón de letras que queremos buscar
  • ignore.case: para ver si importa si es mayúscula o no.
DT = data.table(Name=c("Mary","George","Martha","Manuel"), 
                Salary=c(2,3,4,17))
DT[like(Name,"Ma")]
##      Name Salary
## 1:   Mary      2
## 2: Martha      4
## 3: Manuel     17

DT[like(Name,"ma")]
## Empty data.table (0 rows and 2 cols): Name,Salary

DT[like(Name,"ma",ignore.case=T)]
##      Name Salary
## 1:   Mary      2
## 2: Martha      4
## 3: Manuel     17

DT[like(Name,"mar",ignore.case=T)]
##      Name Salary
## 1:   Mary      2
## 2: Martha      4

7.5.11 rowid y rowidv

Esta función genera IDs differentes para cada fila dentro de un grupo.

(DT = data.table(x=c(20,10,10,30,30,20), y=c("a", "a", "a", "b", "b", "b"), z=1:6+.1))
##     x y   z
## 1: 20 a 1.1
## 2: 10 a 2.1
## 3: 10 a 3.1
## 4: 30 b 4.1
## 5: 30 b 5.1
## 6: 20 b 6.1

rowid(DT$x) 
## [1] 1 1 2 1 2 2

rowidv(DT, cols="x") 
## [1] 1 1 2 1 2 2

rowid(DT$x, prefix="grupo") # prefijo 
## [1] "grupo1" "grupo1" "grupo2" "grupo1" "grupo2" "grupo2"

rowid(DT$x, DT$y) 
## [1] 1 1 2 1 2 1

rowidv(DT, cols=c("x","y")) 
## [1] 1 1 2 1 2 1

DT[, id := rowid(x,y)]
##     x y id
## 1: 20 a  1
## 2: 10 a  1
## 3: 10 a  2
## 4: 30 b  1
## 5: 30 b  2
## 6: 20 b  1