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

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

Вот пример:

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

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

So:

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

ИЗМЕНИТЬ:

Как бы то ни было, ужасный цикл 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 петель — это зло.


person Paul Murray    schedule 18.02.2013    source источник
comment
интересно, unique.list[[1]] != unique(A). Мое предположение, хотя и просто предположение, заключается в том, что, возможно, то, что вызывается в операторе lapply, является {base} уникальным, а не {data.table} unique.   -  person Ricardo Saporta    schedule 18.02.2013
comment
Я думаю ты прав. Я только что заметил, что - наряду с уничтожением ключей - unique даже не выполняет свою работу при передаче через lapply   -  person Paul Murray    schedule 18.02.2013
comment
@PaulMurray +1 Хороший вопрос, но destroy кажется слишком сильным. В этом случае он сохраняет их.   -  person Matt Dowle    schedule 18.02.2013
comment
@MatthewDowle отредактировано для языка   -  person Paul Murray    schedule 18.02.2013
comment
или вы должны были пройти весь путь, например, data.table и, как ни странно, аннигилировать ключи таблицы   -  person statquant    schedule 19.02.2013


Ответы (2)


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

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

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


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

person Ricardo Saporta    schedule 18.02.2013
comment
Вау, спасибо. И странно. Это работает как решение, но я все еще хочу знать, что происходит. Я единственный, кто хочет хранить data.tables в списках? Я отправлю это как отчет об ошибке – думаете, это справедливо? - person Paul Murray; 18.02.2013
comment
@Plmrry Я думаю, что когда вы сохраняете свои data.table в виде списка, вы теряете большое преимущество того, что предлагает data.table, а именно эффективность использования памяти за счет предотвращения ненужного дублирования. Лично я храню список имен своих data.tables, а затем беру их по get( имени ). Немного уродливее код, но быстрее. - person Ricardo Saporta; 18.02.2013
comment
@Plmrrry, что касается отчета об ошибке, это может быть не ошибка, а просто то, как работает match.fun(). хотя я не мог сказать - person Ricardo Saporta; 18.02.2013
comment
Да, похоже, мне следует просто отказаться от хранения data.table в списках. У меня есть пара функций, которые очищают XML и выдают список реляционных таблиц, но я полагаю, что я должен просто оставить их как есть в среде и сохранить их все как RData... Я все еще ищу лучшие практики, когда это приходится иметь дело с большим количеством таблиц данных (или фреймов), особенно когда я хочу иметь возможность передавать их в качестве аргументов. - person Paul Murray; 18.02.2013
comment
@PaulMurray У меня обычно одна большая таблица, а не список из множества меньших. Для чего-то вроде парсинга я бы, вероятно, соскоблил в list из data.table, да, но затем использовал бы rbindlist для создания большого. insert() строк по ссылке есть в списке желаний. - person Matt Dowle; 18.02.2013
comment
@MatthewDowle Ну, это отдельные реляционные таблицы, поэтому я не могу их rbind. Например, моя функция может очищать школы, студентов и штаты, каждый из которых связан с другим через реляционную таблицу, например. школы.студенты. - person Paul Murray; 18.02.2013
comment
@MatthewDowle Я не знал, что есть rbindlist! - person Ricardo Saporta; 18.02.2013
comment
@RicardoSaporta Чего еще ты не знал, чего не знал? Вы видели unknownR::unk()? :-) Ему можно передать список пакетов типа data.table. - person Matt Dowle; 19.02.2013
comment
@PaulMurray В таком случае ты прав. Я не рассматривал такой вариант использования. Что-то вроде динамической базы данных, судя по звуку. Если вам нужны улучшения, рад их добавить. За исключением этой проблемы lapply, похоже, это проблема R, поэтому я не уверен, что мы можем сделать. Если только мы не замаскируем сам lapply (или можно было бы локально). - person Matt Dowle; 19.02.2013
comment
@MatthewDowle, я не знаю;) - person Ricardo Saporta; 20.02.2013

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

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

person Matt Dowle    schedule 18.02.2013