Как я могу определить два наименьших значения (и индексы), перемещающиеся по столбцу в R?

#r #data.table #vectorization #rolling-computation

Вопрос:

Я уже несколько дней ищу ответ на этот вопрос. Я думаю, что я очень близок, но я не могу понять, как это сделать.

У меня есть таблица data.table с двумя такими столбцами:

 df = data.table(a = c(8,3,6,12,15,21,4,5,1,32,13), b = c(12,3,1,66,4,7,32,6,76,2,11))

 

Я хотел бы получить наименьшие два значения и их индексы в скользящем четырехдневном окне. Rfast::nth, кажется, дает мне все, что мне нужно, за исключением того, что я не могу его векторизовать. Очевидно, я делаю что-то не так.

Мне нужно, чтобы результат выглядел следующим образом:

      a  b low lowIdx low2 low2Idx
 1:  8 12  NA     NA   NA      NA
 2:  3  3  NA     NA   NA      NA
 3:  6  1  NA     NA   NA      NA
 4: 12 66   3      2    6       3
 5: 15  4   3      1    6       2
 6: 21  7   6      1    12      2
 7:  4 32   4      4    12      1
 8:  5  6   4      3    4       3
 9:  1 76   1      4    4       2
10: 32  2   1      3    4       1
11: 13 11   1      2    5       1
 

Я попытался сделать это с помощью следующих различных форм:

 n <- nrow(df)
df$low[4:n] <- Rfast::nth(df[(n-3):n]$a, 1)
df$lowIdx[4:n] <- Rfast::nth(df$a, 1, index.return = TRUE)
df$low2[4:n] <- Rfast::nth(df[(n-3):n]$a, 2)
df$low2Idx[4:n] <- Rfast::nth(df$a, 2, index.return = TRUE)
 

Я также пытался работать с frollapply, но безрезультатно.

Спасибо тебе, Пит

Комментарии:

1. low2 и low2Idx являются вторым по величине значением и индексом, верно? Почему их значение в строке 8 одинаковое?

2. Ронак, да, это верно, low2 и low2Idx являются вторым по величине значением и индексом. Спасибо всем вам за помощь!

3. Кроме того, в конечном итоге это будет против нескольких колов для 11 миллионов записей. Я сделаю несколько таймингов и сообщу, какой из них самый быстрый. Еще раз спасибо всем за помощь.

4. Rfast также есть colnth то, что вам нужно.

Ответ №1:

Библиотека runner также помогает.

 library(dplyr)
library(runner)
df %>% mutate(low = runner(x=a, k=4, f = function(x) ifelse(length(x) ==4, min(x), NA)),
              lowIdx = runner(x = a, k =4, function(x) ifelse(length(x) ==4, which.min(x), NA)),
              Low2 = runner(x=a, k=4, f = function(x) ifelse(length(x) ==4, sort(x)[2], NA)),
              Low2Idx =runner(x=a, k=4, f = function(x) ifelse(length(x) ==4, order(x)[2], NA))
              )

     a  b low lowIdx Low2 Low2Idx
 1:  8 12  NA     NA   NA      NA
 2:  3  3  NA     NA   NA      NA
 3:  6  1  NA     NA   NA      NA
 4: 12 66   3      2    6       3
 5: 15  4   3      1    6       2
 6: 21  7   6      1   12       2
 7:  4 32   4      4   12       1
 8:  5  6   4      3    5       4
 9:  1 76   1      4    4       2
10: 32  2   1      3    4       1
11: 13 11   1      2    5       1
 

Комментарии:

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

Ответ №2:

С frollapply помощью in data.table вы можете сделать :

 library(data.table)

cols <- c('low', 'lowIdx', 'low2', 'low2Idx')
n <- 4
df[, (cols)  := .(frollapply(a, n, min), 
                  frollapply(a, n, which.min), 
                  frollapply(a, n, function(x) sort(x)[2]), 
                  frollapply(a, n, function(x) order(x)[2]))]
df

#     a  b low lowIdx low2 low2Idx
# 1:  8 12  NA     NA   NA      NA
# 2:  3  3  NA     NA   NA      NA
# 3:  6  1  NA     NA   NA      NA
# 4: 12 66   3      2    6       3
# 5: 15  4   3      1    6       2
# 6: 21  7   6      1   12       2
# 7:  4 32   4      4   12       1
# 8:  5  6   4      3    5       4
# 9:  1 76   1      4    4       2
#10: 32  2   1      3    4       1
#11: 13 11   1      2    5       1
 

Ответ №3:

Для простого примера, подобного вашему, я предлагаю использовать frollapply . Для лучшего контроля над окном, особенно если у вас есть пробелы в столбце дат, я рекомендую runner. Дополнительную информацию смотрите в документации

 df[, c('low', 'lowIdx', 'low2', 'low2Idx')  := .(
  min_run(a, k = 4, na_pad = TRUE), 
  runner(a, k = 4, function(x) ifelse(length(x) > 0, which.min(x), NA), na_pad = TRUE), 
  runner(a, k = 4, function(x) sort(x)[2], na_pad = TRUE), 
  runner(a, k = 4, function(x) order(x)[2], na_pad = TRUE)
)]
df
#      a  b low lowIdx low2 low2Idx
#  1:  8 12  NA     NA   NA      NA
#  2:  3  3  NA     NA   NA      NA
#  3:  6  1  NA     NA   NA      NA
#  4: 12 66   3      2    6       3
#  5: 15  4   3      1    6       2
#  6: 21  7   6      1   12       2
#  7:  4 32   4      4   12       1
#  8:  5  6   4      3    5       4
#  9:  1 76   1      4    4       2
# 10: 32  2   1      3    4       1
# 11: 13 11   1      2    5       1
 

Комментарии:

1. Бегун — действительно полезный пакет. Ваш ответ очень похож на мой. На самом деле я фанат вашего пакета. 🙂