Почему lapply() не сохраняет мои ключи данных.

У меня есть куча data.tables в списке. Я хочу применить unique() к каждой таблице данных в моем списке, но при этом уничтожаются все мои ключи data.table.

Вот пример:

A <- data.table(a = rep(c("a","b"), each = 3), b = runif(6), key = "a")
B <- data.table(x = runif(6), b = runif(6), key = "x")

blah <- unique(A)

Здесь blah все еще есть ключ, и все правильно в мире:

key(blah)

# [1] "a"

Но если я добавлю данные data.tables в список и использую lapply(), ключи будут уничтожены:

dt.list <- list(A, B)

unique.list <- lapply(dt.list, unique) # Keys destroyed here

lapply(unique.list, key) 

# [[1]]
# NULL

# [[2]]
# NULL

Это, вероятно, связано с тем, что я не понимаю, что это означает для ключей, которые будут назначены "по ссылке", поскольку у меня были другие проблемы с исчезновением ключей.

Итак:

  • Почему lapply не сохраняет мои ключи?
  • Что значит сказать, что клавиши назначаются "по ссылке"?
  • Должен ли я даже хранить данные в списке?
  • Как я могу безопасно хранить/манипулировать data.tables, не опасаясь потерять ключи?

EDIT:

Для чего это стоит, ужасный цикл for тоже работает отлично:

unique.list <- list()

for (i in 1:length(dt.list)) {
  unique.list[[i]] <- unique(dt.list[[i]])
}

lapply(unique.list, key)

# [[1]]
# [1] "a"

# [[2]]
# [1] "x"

Но это R, а for петли - злые.

Ответ 1

Интересно отметить, что разница между этими двумя разными результатами

lapply(dt.list, unique) 
lapply(dt.list, function(x) unique(x)) 

Если вы используете последний, результаты будут такими, как вы ожидали.


По-видимому, неожиданное поведение связано с тем, что первый оператор lapplyвызывая unique.data.frame (т.е. от {base}), а второй вызывает unique.data.table

Ответ 2

Хороший вопрос. Оказывается, он задокументирован в ?lapply (см. Раздел "Примечание" ):

По историческим причинам вызовы, создаваемые в результате, неоценимы, и код был написан (например, bquote), который опирается на это. Эта означает, что записанный вызов всегда имеет форму FUN (X [[0L]],...), с заменой 0L на текущий целочисленный индекс. Обычно это не проблема, но может быть, если FUN использует sys.call или match.call или если это примитивная функция, использующая вызов. Это означает, что это часто безопаснее вызвать примитивные функции с помощью обертки, так что, например, lapply (ll, function (x) is.numeric(x)) требуется в R 2.7.1 для обеспечения этот метод отправляется для is.numeric происходит правильно.