При написании пакета, который опирается на data.table
, я обнаружил некоторое нечетное поведение. У меня есть функция, которая удаляет и переупорядочивает некоторую колонку по ссылке, и она работает просто отлично, то есть переданный мной data.table
был изменен без назначения вывода функции. У меня есть еще одна функция, которая добавляет новые столбцы, но эти изменения не всегда сохраняются в data.table
, который был передан.
Вот небольшой пример:
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
Что случилось? Чем отличается test2
? Я могу изменить существующие столбцы на месте:
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
Но добавление в test2
все еще не работает:
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
Я не могу зафиксировать все случаи, когда я вижу это поведение, но сохранение и чтение из RDS - это первый случай, который я могу достоверно реплицировать. Запись и чтение из CSV не похоже на ту же проблему.
Является ли это проблемой указателя ala этой проблемы, например, сериализация data.table уничтожает перенапряженные указатели? Есть ли простой способ восстановить их? Как я могу проверить их внутри своей функции, чтобы я мог восстановить указатели или ошибку, если операция не будет работать?
Я знаю, что я могу назначить вывод функции в качестве обходного пути, но это не очень data.table
-y. Разве это не создало бы временную копию в памяти?
Ответ на решение Arun
Арун дал указание, что это действительно проблема с указателем, которая может быть диагностирована с помощью truelength
и исправлена с помощью setDT
или alloc.col
. Я столкнулся с проблемой, инкапсулирующей его решение в функцию (продолжая с кода выше):
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
Таким образом, похоже, что локальная копия внутри функции правильно изменена, но эталонная версия - нет. Почему бы и нет?