Agregar nuevas columnas a un data.table por referencia dentro de una función que no siempre funciona

Al escribir un paquete que se basa en data.table , descubrí un comportamiento extraño. Tengo una función que elimina y reordena algunas columnas por referencia, y funciona muy bien, lo que significa que el data.table que pasé fue modificado sin asignar la salida de la función. Tengo otra función que agrega nuevas columnas, pero esos cambios no siempre persisten en la data.table que se pasó.

Aquí hay un pequeño ejemplo:

 library(data.table) # I'm using 1.9.4 test <- data.table(id = letters[1:2], val=1:2) foobar <- function(dt, col) { dt[, (col) := 1] invisible(dt) } test # id val #1: a 1 #2: b 2 saveRDS(test, "test.rds") test2 <- readRDS("test.rds") all.equal(test, test2) #[1] TRUE foobar(test, "new") test # id val new #1: a 1 1 #2: b 2 1 foobar(test2, "new") test2 # id val #1: a 1 #2: b 2 

¿Que pasó? ¿Qué hay de diferente en test2 ? Puedo modificar las columnas existentes in situ en:

 foobar(test, "val") test # id val new #1: a 1 1 #2: b 1 1 foobar(test2, "val") test2 # id val #1: a 1 #2: b 1 

Pero agregar test2 todavía no funciona:

 foobar(test2, "someothercol") .Last.value # id val someothercol #1: a 1 1 #2: b 1 1 test2 # id val #1: a 1 #2: b 1 

No puedo precisar todos los casos en los que veo este comportamiento, pero guardar y leer desde RDS es el primer caso en el que puedo replicar de manera confiable. Escribir y leer desde un archivo CSV no parece tener el mismo problema.

¿Es esto un problema de puntero a este respecto , como serializar un data.table destruye los punteros sobre asignados? ¿Hay alguna forma simple de restaurarlos? ¿Cómo podría verificarlos dentro de mi función, para poder restaurar los punteros o el error si la operación no va a funcionar?

Sé que puedo asignar el resultado de la función como una solución, pero eso no es muy data.table -y. ¿No sería eso también crear una copia temporal en la memoria?

Respuesta a la solución de Arun

Arun ha dado instrucciones de que se trata de un problema de puntero, que se puede diagnosticar con truelength y se puede truelength con setDT o alloc.col . Me encontré con un problema encapsulando su solución en una función (continuando desde el código anterior):

 func <- function(dt) {if (!truelength(dt)) setDT(dt)} func2 <- function(dt) {if (!truelength(dt)) alloc.col(dt)} test2 <- readRDS("test.rds") truelength(test2) #[1] 0 truelength(func(test2)) #[1] 100 truelength(test2) #[1] 0 truelength(func2(test2)) #[1] 100 truelength(test2) #[1] 0 

Por lo tanto, parece que la copia local dentro de la función se está modificando correctamente, pero la versión de referencia no lo está. Por qué no?

¿Es esto un problema de puntero a este respecto, como serializar un data.table destruye los punteros sobre asignados?

Sí, la carga desde el disco establece el puntero externo en NULL. Tendremos que sobreasignar de nuevo.

¿Hay alguna forma simple de restaurarlos?

Sí. Puede probar la truelength() de truelength() de la tabla de datos, y si es 0 , entonces use setDT() o alloc.col() en ella.

 truelength(test2) # [1] 0 if (!truelength(test2)) setDT(test2) truelength(test2) # [1] 100 foobar(test2, "new") test2[] # id val new # 1: a 1 1 # 2: b 2 1 

Esto probablemente debería ir como preguntas frecuentes (no recuerdo haberlo visto allí).
Ya en la sección Preguntas frecuentes en Mensajes de advertencia.