Вот решение 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
.