Как эффективно определить максимальную разницу между значением переменной в каждой строке и той же переменной в последующих значениях строки в data.table в R - PullRequest
0 голосов
/ 20 января 2019

Какой наиболее эффективный способ определения максимальной положительной разницы между значением (X) для каждой строки и последующими значениями той же переменной (X) в группе (Y) в data.table в R.

Пример:

set.seed(1)
dt <- data.table(X = sample(100:200, 500455, replace = TRUE),
                 Y = unlist(sapply(10:1000, function(x) rep(x, x))))

Вот мое решение, которое я считаю неэффективным и медленным:

dt[, max_diff := vapply(1:.N, function(x) max(X[x:.N] - X[x]), numeric(1)), by = Y]
head(dt, 21)

      X  Y max_diff
 1: 126 10      69
 2: 137 10      58
 3: 157 10      38
 4: 191 10       4
 5: 120 10      75
 6: 190 10       5
 7: 195 10       0
 8: 166 10       0
 9: 163 10       0
10: 106 10       0
11: 120 11      80
12: 117 11      83
13: 169 11      31
14: 138 11      62
15: 177 11      23
16: 150 11      50
17: 172 11      28
18: 200 11       0
19: 138 11      56
20: 178 11      16
21: 194 11       0

Если вы можете посоветовать эффективное (более быстрое) решение?

1 Ответ

0 голосов
/ 20 января 2019

Вот решение dplyr, которое примерно в 20 раз быстрее и дает те же результаты. Я предполагаю, что аналог data.table будет еще быстрее. (РЕДАКТИРОВАТЬ: см. Снизу - это!)

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

Во-первых, оригинальное решение занимает около 4 секунд на моей машине:

tictoc::tic("OP data.table") 
dt[, max_diff := vapply(1:.N, function(x) max(X[x:.N] - X[x]), numeric(1)), by = Y]
tictoc::toc()
# OP data.table: 4.594 sec elapsed

Но всего за 0,2 секунды мы можем взять эту таблицу данных, преобразовать ее во фрейм данных, добавить номер строки orig_row, сгруппировать по Y, отсортировать в обратном порядке по orig_row, взять разницу между X и совокупным максимумом. X, разгруппировать и перегруппировать в исходном порядке:

library(dplyr)
tictoc::tic("dplyr") 
dt2 <- dt %>% 
  as_data_frame() %>%
  mutate(orig_row = row_number()) %>%

  group_by(Y) %>%
  arrange(-orig_row) %>%
  mutate(max_diff2 = cummax(X) - X) %>%
  ungroup() %>%
  arrange(orig_row)
tictoc::toc()
# dplyr: 0.166 sec elapsed

all.equal(dt2$max_diff, dt2$max_diff2)
#[1] TRUE

РЕДАКТИРОВАТЬ: как @david-arenburg предлагает в комментариях, это может быть сделано молниеносно в data.table с элегантной строкой:

dt[.N:1, max_diff2 := cummax(X) - X, by = Y]

На моем компьютере это примерно в 2-4 раза быстрее, чем приведенное выше решение dplyr.

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