R цикл Vectorize FOR с использованием предыдущих значений итерации - PullRequest
4 голосов
/ 07 ноября 2019

Можно ли векторизовать / ускорить выполнение цикла FOR, использующего предыдущие значения итерации?

В приведенном ниже репродуктивном примере:

  • текущее производствовычисляется из текущего запаса
  • текущее производство обновляет СЛЕДУЮЩИЙ запас
  • , на следующей итерации использовался обновленный запас для определения текущего производства и т. д ...

Так что мне нужно вычислять запас на каждой итерации, чтобы вычислить производственную уставку ... Можно ли избежать (медленного) цикла for?

Текущая реализация занимает около 45 секунд для строк по 50 тыс.

# Dummy functions for the examples. Real code is more complicated
function1 <- function(energy, stock, critical) {
    if (stock < critical) {
        return (energy)
    } else {
        return(0)
    }
}
function2 <- function(power) {
  return(round(power/100))
}
# Dummy data
d <- data.frame( "energy"= c(660, 660, 660, 660),
                 "stock" = c(20,   0,    0, 0),
                 "delivery" = c(0, 0, 2, 0),
                 "critical" = c(50, 50 ,50, 50),
                 "power" = c(0, 0, 0, 0),
                 "production" = c(0, 0, 0, 0) )

for (i in 1:length(d$energy)) {

  # Computing power, based on CUURENT stock
  d$power[i] <- function1(d$energy[i], d$stock[i], d$critical[i])

  # Computing production
  d$production[i] <- function2(d$power[i])

  # Updating NEXT stock with current production / delivery
  if (i < length(d$energy)) {
    d$stock[i+1] <- d$stock[i] + d$production[i] - d$delivery[i]
  }
}

View(d)

Ответы [ 3 ]

3 голосов
/ 07 ноября 2019

Одной из возможностей является использование пакета dplyr, который является частью tidyverse.

library(dplyr)

d %>%
  mutate(power = function1(energy, stock, critical),
         production = function2(power),
         stock_new = cumsum(stock + lag(production - delivery, 1, default = 0)))

  energy stock delivery critical power production stock_new
1    660    20        0       10   500          5        20
2    660     0        0       10   500          5        25
3    660     0        2       10   500          5        30
4    660     0        0       10   500          5        33

. Это легко работает, если функции function1 и function2 векторизованы. Если нет, вам придется использовать purrr::map внутри mutate.

2 голосов
/ 07 ноября 2019

В базе вы можете использовать Reduce с accumulate = TRUE, например:

fun  <- function(x,y) {
    ttStock <- x[[2]] + x[[6]] - x[[3]]
    ttPower <- function1(y[[1]], ttStock, y[[4]])
    ttProduction <- function2(ttPower)
    c(y[[1]], ttStock, y[[3]], y[[4]], ttPower, ttProduction)
}
d$power[1] <- function1(d$energy[1], d$stock[1], d$critical[1])
d$production[1] <- function2(d$power[1])
do.call(rbind, Reduce(fun, as.data.frame(t(d[-1,])), d[1,], accumulate = TRUE))
#  energy stock delivery critical power production
#1    660    20        0       50   660          7
#2    660    27        0       50   660          7
#3    660    34        0       50   660          7
#4    660    39        2       50   660          7

Чтобы сделать это проще, я заполняю power и production в первой строкеd.

0 голосов
/ 07 ноября 2019

Как насчет сохранения состояния между вызовами функций.

my_env <- new.env(parent = emptyenv())
my_env$stock <- d$stock[0]

f <- function(item){
   power <- function1()
   production <- function1()/100
   stock <- my_env$stock 
   ....
   rest of the businesss logic
   ...
}

apply(d, 2, f)

...