Agregar nuevas columnas a una referencia de tablas de datos dentro de una función no siempre funciona
data.table (1)
¿Se trata de un problema de puntero para este problema, como la serialización de una tabla de datos destruye los punteros sobre asignados?
Sí, la carga desde el disco establece el puntero externo en NULL. Tendremos que sobre-asignar de nuevo.
¿Hay una forma sencilla de restaurarlos?
Sí. Puedes probar la truelength()
de la tabla de datos, y si es 0
, entonces usa setDT()
o alloc.col()
en él.
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 un FAQ (no recuerdo haberlo visto allí).
Ya en FAQ en la sección de Mensajes de Advertencia.
Al escribir un paquete que se basa en data.table
, he descubierto algún comportamiento extraño. Tengo una función que elimina y reordena alguna referencia por columna, y funciona bien, lo que significa que la data.table
que pasé se modificó sin asignar la salida de la función. Sin embargo, 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 en el lugar, ya sea 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 a 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 que veo este comportamiento, pero guardar y leer desde RDS es el primer caso que puedo replicar de manera confiable. Escribir y leer desde un CSV no parece tener el mismo problema.
¿Se trata de un problema de puntero para este problema , como la serialización de una tabla de datos destruye los punteros sobre asignados? ¿Hay una forma sencilla 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 la salida de la función como una solución alternativa, pero eso no es una data.table
muy -y. ¿No sería eso también crear una copia temporal en la memoria?
Respuesta a la solución de Arun.
Arun ha indicado que es un problema de puntero, que puede diagnosticarse con truelength
y setDT
con setDT
o alloc.col
. Me encontré con un problema al encapsular 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
Así que parece que la copia local dentro de la función se está modificando adecuadamente, pero la versión de referencia no lo está. Por qué no?