Split data.table

У меня есть таблица данных, которую я хочу разбить на две части. Я делаю это следующим образом:

dt <- data.table(a=c(1,2,3,3),b=c(1,1,2,2))
sdt <- split(dt,dt$b==2)

но если я хочу сделать что-то вроде этого следующим шагом

sdt[[1]][,c:=.N,by=a]

Появляется следующее предупреждающее сообщение.

Предупреждающее сообщение: В [.data.table (sdt [[1]], := (c,.N), by = a): Недопустимый .internal.selfref обнаружен и исправлен, взяв копию целую таблицу, так что: = может добавить этот новый столбец по ссылке. Загар более ранняя точка, эта таблица данных была скопирована R. Избегайте ключа < -, имена < - и attr < - которые в R в настоящее время (и странно) могут копировать все Таблица данных. Вместо этого используйте синтаксис set *, чтобы избежать копирования: setkey(), setnames() и setattr(). Кроме того, список (DT1, DT2) скопирует весь DT1 и DT2 (R list() копирует именованные объекты), вместо этого используйте reflist(), если необходимо (должно быть реализовано). Если это сообщение не помогает, пожалуйста, сообщите о datatable-help, чтобы устранить основную причину.

Просто интересно, есть ли лучший способ разбить таблицу так, чтобы она была более эффективной (и не получила бы это сообщение)?

Ответ 1

Это работает в v1.8.7 (и может работать и в версии 1.8.6):

> sdt = lapply(split(1:nrow(dt), dt$b==2), function(x)dt[x])
> sdt
$`FALSE`
   a b
1: 1 1
2: 2 1

$`TRUE`
   a b
1: 3 2
2: 3 2

> sdt[[1]][,c:=.N,by=a]     # now no warning
> sdt
$`FALSE`
   a b c
1: 1 1 1
2: 2 1 1

$`TRUE`
   a b
1: 3 2
2: 3 2

Но, как сказал @mnel, это неэффективно. Пожалуйста, избегайте расщепления, если это возможно.

Ответ 2

Я искал способ сделать раскол в data.table, я столкнулся с этим старым вопросом.

Когда-то раскол - это то, что вы хотите сделать, а метод data.table "by" не удобен.

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

SplitDataTable <- function(dt,attr) {
  boundaries=c(0,which(head(dt[[attr]],-1)!=tail(dt[[attr]],-1)),nrow(dt))
  return(
    mapply(
      function(start,end) {dt[start:end,]},
      head(boundaries,-1)+1,
      tail(boundaries,-1),
      SIMPLIFY=F))
}

Ответ 3

Как упоминалось выше (@jangorecki), пакет data.table уже имеет свою собственную функцию для разбиения. В этом упрощенном случае мы можем использовать:

> dt <- data.table(a = c(1, 2, 3, 3), b = c(1, 1, 2, 2))
> split(dt, by = "b")
$'1'
   a b
1: 1 1
2: 2 1

$'2'
   a b
1: 3 2
2: 3 2

Для более сложных/конкретных случаев я бы порекомендовал создать новую переменную в data.table, используя функции по ссылке := или set, а затем вызвать функцию split. Если вы заботитесь о производительности, всегда оставайтесь в среде data.table, например, dt[, SplitCriteria := (...)], а не вычисляйте переменную разделения извне.