r data.table

¿Cuándo debo usar setDT() en lugar de data.table() para crear un data.table?



(2)

Tengo dificultades para captar la esencia de la función setDT() . Cuando leo el código en SO, con frecuencia me encuentro con el uso de setDT() para crear una tabla de datos. Por supuesto, el uso de data.table() es ubicuo. Siento que comprendo sólidamente la naturaleza de data.table() pero la relevancia de setDT() me elude. ?setDT me dice esto:

setDT convierte las listas (con nombre y sin nombre) y data.frames a data.tables por referencia .

tanto como:

En el lenguaje data.table , todas las funciones set* cambian su entrada por referencia. Es decir, no se realiza ninguna copia, excepto la memoria de trabajo temporal, que es tan grande como una columna.

Entonces, esto me hace pensar que solo debería usar setDT() para hacer una tabla de datos, ¿verdad? ¿Es setDT() simplemente una lista para convertir data.table?

library(data.table) a <- letters[c(19,20,1,3,11,15,22,5,18,6,12,15,23)] b <- seq(1,41,pi) ab <- data.frame(a,b) d <- data.table(ab) e <- setDT(ab) str(d) #Classes ‘data.table’ and ''data.frame'': 13 obs. of 2 variables: # $ a: Factor w/ 12 levels "a","c","e","f",..: 9 10 1 2 5 7 11 3 8 4 ... # $ b: num 1 4.14 7.28 10.42 13.57 ... # - attr(*, ".internal.selfref")=<externalptr> str(e) #Classes ‘data.table’ and ''data.frame'': 13 obs. of 2 variables: # $ a: Factor w/ 12 levels "a","c","e","f",..: 9 10 1 2 5 7 11 3 8 4 ... # $ b: num 1 4.14 7.28 10.42 13.57 ... # - attr(*, ".internal.selfref")=<externalptr>

Al parecer no hay diferencia en este caso. En otro caso la diferencia es evidente:

ba <- list(a,b) f <- data.table(ba) g <- setDT(ba) str(f) #Classes ‘data.table’ and ''data.frame'': 2 obs. of 1 variable: # $ ba:List of 2 # ..$ : chr "s" "t" "a" "c" ... # ..$ : num 1 4.14 7.28 10.42 13.57 ... # - attr(*, ".internal.selfref")=<externalptr> str(g) #Classes ‘data.table’ and ''data.frame'': 13 obs. of 2 variables: # $ V1: chr "s" "t" "a" "c" ... # $ V2: num 1 4.14 7.28 10.42 13.57 ... # - attr(*, ".internal.selfref")=<externalptr>

¿Cuándo debo usar setDT() ? ¿Qué hace que setDT() relevante? ¿Por qué no hacer que la función data.table() original data.table() capaz de hacer lo que setDT() puede hacer?


setDT() no reemplaza a data.table() . Es un reemplazo más eficiente para as.data.table() que se puede usar con ciertos tipos de objetos.

  • mydata <- as.data.table(mydata) copiará el objeto detrás de mydata , convertirá la copia en una data.table y luego cambiará el símbolo de mydata para que apunte a la copia.
  • setDT(mydata) cambiará el objeto detrás de mydata a data.table . No se realiza ninguna copia.

Entonces, ¿cuál es una situación realista para usar setDT() ? Cuando no puedes controlar la clase de los datos originales. Por ejemplo, la mayoría de los paquetes para trabajar con bases de datos dan salida a data.frame . En ese caso, tu código sería algo así como

mydata <- dbGetQuery(conn, "SELECT * FROM mytable") # Returns a data.frame setDT(mydata) # Make it a data.table

¿Cuándo debería usar as.data.table(x) ? Cada vez que x no es una list o un data.frame . El uso más común es para matrices.


Actualizar:

@Roland hace algunos buenos puntos en la sección de comentarios, y la publicación es mejor para ellos. Aunque originalmente me centré en los problemas de desbordamiento de la memoria, señaló que incluso si esto no ocurre, la administración de la memoria de varias copias toma un tiempo considerable, lo que es una preocupación más común en la vida cotidiana. También se han agregado ejemplos de ambos problemas.

Me gusta esta pregunta en porque creo que realmente se trata de evitar el desbordamiento de pila en R cuando se trata de conjuntos de datos más grandes. 😊 ¡Aquellos que no están familiarizados con la familia de operaciones de set pueden beneficiarse de esta discusión!

Se debe usar setDT() cuando se trabaja con conjuntos de datos más grandes que ocupan una cantidad considerable de RAM porque la operación modificará cada objeto en su lugar, conservando la memoria. Para datos que es un porcentaje muy pequeño de RAM, el uso de la copia y modificación de data.table está bien.

La creación de la función setDT realidad se inspiró en el siguiente hilo en el desbordamiento de pila, que trata sobre trabajar con un gran conjunto de datos (varios GB). Verá el tono de Matt Dowle en una sugerencia del nombre ''setDT''.

Convertir un marco de datos en una tabla de datos sin copia

Un poco más de profundidad:

Con R, los datos se almacenan en la memoria. Esto acelera las cosas considerablemente porque RAM es mucho más rápido de acceder que los dispositivos de almacenamiento. Sin embargo, puede surgir un problema cuando el conjunto de datos de uno es una gran parte de la RAM. ¿Por qué? Debido a que la base R tiene una tendencia a hacer copias de cada data.frame cuando se les aplican algunas operaciones. Esto ha mejorado después de la versión 3.1, pero el direccionamiento está fuera del alcance de esta publicación. Si uno está data.frame múltiples data.frame s o list s en una data.frame o data.table , su uso de memoria se expandirá bastante rápido porque en algún momento durante la operación, existen múltiples copias de sus datos en la RAM. Si el conjunto de datos es lo suficientemente grande, puede quedarse sin memoria cuando se produzcan todas las copias, y su pila se desbordará. Vea el ejemplo de esto a continuación. Obtenemos un error, y la dirección de memoria y la clase de objeto original no cambian.

> N <- 1e8 > P <- 1e2 > data <- as.data.frame(rep(data.frame(rnorm(N)), P)) > > pryr::object_size(data) 800 MB > > tracemem(data) [1] "<0000000006D2DF18>" > > data <- data.table(data) Error: cannot allocate vector of size 762.9 Mb > > tracemem(data) [1] "<0000000006D2DF18>" > class(data) [1] "data.frame" >

La capacidad de simplemente modificar el objeto en su lugar sin copiar es un gran problema. Eso es lo que hace setDT cuando toma una list o data.frame y devuelve una data.table . El mismo ejemplo anterior que usa setDT , ahora funciona bien y sin error. La dirección de la clase y la memoria cambian, y no se realizan copias.

> tracemem(data) [1] "<0000000006D2DF18>" > class(data) [1] "data.frame" > > setDT(data) > > tracemem(data) [1] "<0000000006A8C758>" > class(data) [1] "data.table" "data.frame"

@Roland señala que para la mayoría de las personas, la mayor preocupación es la velocidad, que se ve afectada por el uso intensivo de la gestión de la memoria. Aquí hay un ejemplo con datos más pequeños que no setDT la CPU, e ilustra qué tan rápido es setDT para este trabajo. Observe los resultados de ''tracemem'' a raíz de los data <- data.table(data) , haciendo copias de los data . Contraste eso con setDT(data) que no imprime una sola copia. Tenemos que llamar a tracemem(data) para ver la nueva dirección de memoria.

> N <- 1e5 > P <- 1e2 > data <- as.data.frame(rep(data.frame(rnorm(N)), P)) > pryr::object_size(data) 808 kB > # data.table method > tracemem(data) [1] "<0000000019098438>" > data <- data.table(data) tracemem[0x0000000019098438 -> 0x0000000007aad7d8]: data.table tracemem[0x0000000007aad7d8 -> 0x0000000007c518b8]: copy as.data.table.data.frame as.data.table data.table tracemem[0x0000000007aad7d8 -> 0x0000000018e454c8]: as.list.data.frame as.list vapply copy as.data.table.data.frame as.data.table data.table > class(data) [1] "data.table" "data.frame" > > # setDT method > # back to data.frame > data <- as.data.frame(data) > class(data) [1] "data.frame" > tracemem(data) [1] "<00000000125BE1A0>" > setDT(data) > tracemem(data) [1] "<00000000125C2840>" > class(data) [1] "data.table" "data.frame" >

¿Cómo afecta esto el tiempo? Como podemos ver, setDT es mucho más rápido para ello.

> # timing example > data <- as.data.frame(rep(data.frame(rnorm(N)), P)) > microbenchmark(setDT(data), data <- data.table(data)) Unit: microseconds expr min lq mean median max neval uq setDT(data) 49.948 55.7635 69.66017 73.553 100.238 100 79.198 data <- data.table(data) 54594.289 61238.8830 81545.64432 64179.131 611632.427 100 68647.917

Las funciones de configuración se pueden utilizar en muchas áreas, no solo al convertir objetos en tablas de datos. Puede encontrar más información sobre la semántica de referencia y cómo aplicarlos en otros lugares llamando a la viñeta sobre el tema.

library(data.table) vignette("datatable-reference-semantics")

Esta es una gran pregunta y aquellos que piensen en usar R con conjuntos de datos más grandes o que solo quieran acelerar los activos de manipulación de datos, pueden beneficiarse de estar familiarizados con las mejoras significativas de rendimiento de la semántica de referencia de datos.