объединение отдельных вызовов data.table в один групповой вызов - PullRequest
0 голосов
/ 08 апреля 2020

Я пытаюсь повысить эффективность следующего простого синтаксиса data.table, поэтому я пытаюсь объединить его в один вызов без повторного вызова by = "group".

#data
library(data.table)
DT <- data.table(group = c(rep("a", 40), rep("b", 40)),
                 other = rnorm(80),
                 num = c(1:80))

#reduce this to one "by" call
DT[, c1 := ifelse(num <= 7, NA, num), by = "group"]
DT[, sprintf("c%d", 2:10) := shift(c1, 1:9, type = 'lag'), by = "group"]
DT[, d1 := shift(c10, 1, type = 'lag'), by = "group"]
DT[, sprintf("d%d", 2:10) := shift(d1, 1:9, type = 'lag'), by = "group"]
DT[, e1 := shift(d10, 1, type = 'lag'), by = "group"]
DT[, sprintf("e%d", 2:10) := shift(e1, 1:9, type = 'lag'), by = "group"]

Что-то вроде

DT[, .(c1 := ifelse(num <= 7, NA, num),
       sprintf("c%d", 2:10) := shift(c1, 1:9, type = 'lag'),
       d1 := shift(c10, 1, type = 'lag'),
       sprintf("d%d", 2:10) := shift(d1, 1:9, type = 'lag'),
       e1 := shift(d10, 1, type = 'lag'),
       sprintf("e%d", 2:10) := shift(e1, 1:9, type = 'lag')), by = "group"]

Редактировать:

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

Есть предложения?

Спасибо

Ответы [ 3 ]

2 голосов
/ 09 апреля 2020

Вот вариант:

ix <- 2L:10L
m <- 1L:9L
DT[, c(sprintf("c%d", ix), sprintf("d%d", ix), sprintf("e%d", ix)) := {
    c1 = replace(num, num <= 7L, NA_integer_)
    lc = shift(c1, m)

    d1 = shift(lc[[9L]])
    ld = shift(d1, m)

    e1 = shift(ld[[9L]])
    c(lc, ld, shift(e1, m))
}, group]
1 голос
/ 09 апреля 2020

Вы можете вызвать by один раз, используя тот факт, что (1) каждый столбец в аргументе j data.table становится столбцом в возвращаемом data.table, и что (2) можно использовать фигурные скобки для промежуточных расчетов в j. Поскольку значение по умолчанию для аргумента type в функции shift равно lag, я не указал его. Обратите внимание, что последняя строка в фигурных скобках, lst, является единственным возвращаемым объектом.

DT[, {
  nms = paste0(rep(c("c", "d", "e"), each = 10), 1:10)
  lst = setNames(vector("list", 30), nms) 

  lst[["c1"]] = ifelse(num <= 7, NA, num)
  lst[sprintf("c%d", 2:10)] = shift(lst[["c1"]], 1:9)

  lst[["d1"]] = shift(lst[["c10"]], 1)
  lst[sprintf("d%d", 2:10)] = shift(lst[["d1"]], 1:9)

  lst[["e1"]] = shift(lst[["d10"]], 1)
  lst[sprintf("e%d", 2:10)] = shift(lst[["e1"]], 1:9)
  lst
}, by = group]

Вывод содержит 30 столбцов: c1, ..., c10, d1, ..., d10 и е1, ..., е10

1 голос
/ 08 апреля 2020
# You can write function:
f <- function(num) {
  c1 <- ifelse(num <= 7, NA, num)
  cl <- shift(c1, 1:9, type = 'lag')
  names(cl) <- sprintf("c%d", 2:10)
  d1 <- shift(cl[9], 1, type = 'lag')
  dl <- shift(d1, 1:9, type = 'lag')
  names(dl) <- sprintf("d%d", 2:10)
  e1 <-  shift(dl[9], 1, type = 'lag')
  el <- shift(e1, 1:9, type = 'lag')
  names(el) <- sprintf("e%d", 2:10)
  c(c1 = list(c1), cl, d1 = d1, dl, e1 = e1, el) # list of desired columns
}

x <- DT[, f(num), by = group] # apply it by group
DT <- cbind(DT, x[, -'group']) # add to initial data

Может быть, это будет быстрее. Кроме того, функция, вероятно, могла бы быть написана лучше. Убедитесь, что функция возвращает список с нужными именами столбцов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...