R: L oop требует длительного времени работы, предложения по лучшей структуре - PullRequest
1 голос
/ 28 мая 2020

В настоящее время у меня есть набор данных с квартальными доходами по 10 индексам. Мой набор данных (составные ставки) структурирован так, что в первом столбце у нас есть «Сценарий», а во втором столбце - «Квартал», а следующие 10 представляют собой квартальную доходность индекса. Прогноз составляет 50 кварталов, поэтому строки 1-51 отражают кварталы 0-50 для сценария 1, а строки 52-102 отражают кварталы 0-50 для сценария 2, et c для 1000 сценариев ios.

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

for(i in 1:nrow(compoundrates)){
  if(compoundrates[i, "Quarter"] == 0){
    compoundrates[i, -c(1:2)] <- 1
  } else{
    compoundrates[i, -c(1:2)] <- compoundrates[i, -c(1:2)] * compoundrates[i - 1, -c(1:2)]
  }
}

l oop прост и работает так, как я хочу. Однако с 51000 строк это займет около 13 минут. Есть ли способ ускорить код? Я попытался придумать векторизованное решение, но мог только подумать, что мне нужно l oop через все строки набора данных. Хотя 13 минут - это еще не конец света, у меня есть другие наборы данных с более длинными прогнозами, до 200 кварталов, что займет очень много времени.

Возможно, для горизонтального поворота набора данных потребуется всего 50 циклов, а не 51000, но подумал, что посмотрю, есть ли у кого-нибудь еще более элегантное решение.

Изменить: здесь приведен образец первых двух строк моего набора данных:

> dput(head(compoundrates[, 1:4])) # First part of data, only 2 indices
structure(list(Scenario = c(1L, 1L, 1L, 1L, 1L, 1L), Quarter = c(0, 
1, 2, 3, 4, 5), US = c(1, 1.06658609144463, 1.1022314574062, 
1.1683883540847, 1.29134306037902, 1.28907212981088), MidCap = c(1, 
1.10361590084936, 1.12966579678275, 1.21702573464001, 1.2674372889915, 
1.37286942499386)), row.names = c(NA, -6L), class = c("grouped_df", 
"tbl_df", "tbl", "data.frame"), groups = structure(list(Scenario = 1L, 
    .rows = list(1:6)), row.names = c(NA, -1L), class = c("tbl_df", 
"tbl", "data.frame"), .drop = TRUE))

1 Ответ

0 голосов
/ 28 мая 2020

Попробуйте это, он использует векторизованные функции, в основном именно то, что вы пытаетесь сделать с for l oop. Он создает новые столбцы, чтобы вы могли видеть, что происходит. Векторизованная функция обычно работает намного быстрее, чем для циклов.

library(tidyverse)
compoundrates %>% 
  group_by(Scenario) %>% 
  arrange(Quarter) %>% 
  mutate(US_lag = lag(US),
         MidCap_lag = lag(MidCap),
         US_cum = US *US_lag,
         MidCap_cum = MidCap *MidCap_lag) %>% 
  mutate_all(~ifelse(is.na(.), 1, .))

Это должно сделать кумулятивный продукт, который вы просили

compoundrates %>% 
  group_by(Scenario) %>% 
  arrange(Quarter) %>% 
  mutate(US_cum = cumprod(US),
         MidCap_cum = cumprod(MidCap)) 

Согласно предложению @ carl-witthoft, вот эталонный тест. Я сделал фрейм данных из 60000 строк, сгруппированных по 6 четвертям в OP.

Unit: milliseconds
                                                                                                expr
     big_data %>% 
        group_by(Scenario) %>% 
          mutate(US_cum = cumprod(US),     
                 MidCap_cum = cumprod(MidCap))


         min      lq     mean   median      uq     max neval
 63.1487 70.0257 77.16906 73.72995 79.7645 147.167   100
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...