R - readRDS() и load() не дают идентичные data.tables в качестве оригинала

Фон

Я попытался заменить некоторые файлы CSV на файлы rds для повышения эффективности. Это промежуточные файлы, которые будут служить в качестве входных данных для других R-скриптов.

Вопрос

Я начал расследование, когда мои скрипты потерпели неудачу, и обнаружил, что readRDS() и load() не возвращают идентичные data tables в качестве оригинала. Это должно произойти? Или я что-то пропустил?

Пример кода

library( data.table )

aDT <- data.table( a=1:10, b=LETTERS[1:10] )
saveRDS( aDT, file = "aDT.rds")
bDT <- readRDS( file = "aDT.rds" )
identical( aDT, bDT, ignore.environment = T )  # Gives 'False'

aDF <- data.frame( a=1:10, b=LETTERS[1:10] )
saveRDS( aDF, file = "aDF.rds")
bDF <- readRDS( file = "aDF.rds" )
identical( aDF, bDF, ignore.environment = T )  # Gives 'True'

# Using 'save'& 'load' doesn't help either
aDT2 <- data.table( a=1:10, b=LETTERS[1:10] )
save( aDT2, file = "aDT2.RData")
bDT2 <- aDT2; rm( aDT2 )
load( file = "aDT2.RData" )
identical( aDT2, bDT2, ignore.environment = T )  # Gives 'False'

Я запускаю R ver 3.2.0 на Linux Mint и тестировал с помощью data.table версии 1.9.4 и 1.9.5 (последний).

Поиск в SO и google возвратил этот и этот, но я не думаю, что они отвечают на эту проблему, Я все еще пытаюсь понять, почему мои сценарии не удались, когда я переключился на rds, но я начинаю с этого.

Было бы очень благодарно, если бы могли помочь знающие члены SO. Спасибо!

Edit:

Привет всем, мне удалось найти способ решить проблему - разместили решение ниже. Прошу прощения, если он довольно неэлегантный. Теперь у меня есть еще 2 вопроса:

(1) Есть ли лучший способ?

(2) Можно ли что-то сделать с кодом R и/или data.table, чтобы решить эту проблему? Я имею в виду, что эта проблема вызывает непредсказуемые ошибки, и это не первое, что приходит на ум. Мой 2 центов стоит.

Ответ 1

Недавно загруженный data.table не знает значения указателя уже загруженного. Вы можете сказать это с помощью

attributes(bDT)$.internal.selfref <- attributes(aDT)$.internal.selfref
identical( aDT, bDT, ignore.environment = T )
# [1] TRUE

data.frame не сохраняйте этот атрибут, возможно, потому, что они не делают изменения на месте.

Ответ 2

Возможно, это связано с указателями:

 attributes(aDT)
$names
[1] "a" "b"

$row.names
 [1]  1  2  3  4  5  6  7  8  9 10

$class
[1] "data.table" "data.frame"

$.internal.selfref
<pointer: 0x0000000000390788>

> attributes(bDT)
$names
[1] "a" "b"

$row.names
 [1]  1  2  3  4  5  6  7  8  9 10

$class
[1] "data.table" "data.frame"

$.internal.selfref
<pointer: (nil)>

> attributes(bDF)
$names
[1] "a" "b"

$row.names
 [1]  1  2  3  4  5  6  7  8  9 10

$class
[1] "data.frame"

> attributes(aDF)
$names
[1] "a" "b"

$row.names
 [1]  1  2  3  4  5  6  7  8  9 10

$class
[1] "data.frame"

Вы можете внимательно посмотреть, что будет с помощью команды .Internal(inspect(.)):

.Internal(inspect(aDT))

 .Internal(inspect(bDT))

Ответ 3

Мне посчастливилось найти способ решения проблемы (отказ от ответственности: это довольно неэлегантный способ, но он работает!) - добавление, затем удаление фиктивного столбца в загруженном data table приводит к тому, что identical будет "True". Я также успешно заменил csv промежуточными файлами rds в моем собственном коде.

Честно говоря, я не понимаю достаточной внутренней работы R и data table, чтобы знать, почему она работает, поэтому любые объяснения и/или более элегантные решения будут приветствоваться.

library( data.table )

aDT <- data.table( a=1:10, b=LETTERS[1:10] )
saveRDS( aDT, file = "aDT.rds")
bDT <- readRDS( file = "aDT.rds" )
identical( aDT, bDT, ignore.environment = T )  # Gives 'False'

bDT[ , aaa := NA ]; bDT[ , aaa := NULL ]
identical( aDT, bDT, ignore.environment = T )  # Now gives 'True'


# Using the add-del-col 'trick' works here too
aDT2 <- data.table( a=1:10, b=LETTERS[1:10] )
save( aDT2, file = "aDT2.RData")
bDT2 <- aDT2; rm( aDT2 )
load( file = "aDT2.RData" )
identical( aDT2, bDT2, ignore.environment = T )  # Gives 'False'

aDT2[ , aaa := NA ]; aDT2[ , aaa := NULL ]
identical( aDT2, bDT2, ignore.environment = T )  # Now gives 'True'