There are a few options.
Use vectors
During each loop, it's expensive to do df$x because it takes memory to do it. Instead, you can pre-assign vectors and subset the vectors.
#easiest - extract the vectors before the loop C <- df[['c']] #used big C because c() is a function a <- df[['a']] b <- df[['b']] x <- df[['x']] for(i in seq_along(x)[-1]) x[i] <- C[i] * a[i-1] / x[i-1L] + b[i] * a[i]
Use a function
Turning your loop into a function will improve performance due to the optimization from compiling.
f_recurse = function(a, b, C, x){ for (i in seq_along(x)[-1]) x[i] <- C[i] * a[i-1] / x[i-1L] + b[i] * a[i] x } f_recurse(df$a, df$b, df$c, df$x)
Use Rcpp
Finally, if the response is still too laggy, you can try to use Rcpp. Note, Rcpp updates in place so while I return a vector, there's really no need - the df$x has also been updated.
library(Rcpp) cppFunction(' NumericVector f_recurse_rcpp(IntegerVector a, IntegerVector b, NumericVector C, NumericVector x){ for (int i = 1; i < x.size(); ++i){ x[i] = C[i] * a[i-1] / x[i - 1] + b[i] * a[i]; } return(x); } ') f_recurse_rcpp(df$a, df$b, df$c, df$x)
Performance
In all, we get close to a 1,000 times performance increase. The table below is from bench::mark which also checks for equality.
# A tibble: 4 x 13 expression min median `itr/sec` mem_alloc <bch:expr> <bch:t> <bch:t> <dbl> <bch:byt> 1 OP 8.27ms 8.8ms 106. 62.04KB 2 extract 6.21ms 7.49ms 126. 46.16KB 3 f_recurse(df$a, df$b, df$c, df$x) 13.1us 28.8us 33295. 0B 4 f_recurse_rcpp(df$a, df$b, df$c, df$x) 8.6us 10us 98240. 2.49KB
And here's an example with a 1,000 row data.frame and then 10,000 row
df <- data.frame(a = sample(1000L), b = sample(1001:2000), c = seq(1000, 11000, length.out = 1000), x = rep(3, 1000L)) # A tibble: 4 x 13 expression min median `itr/sec` mem_alloc <bch:expr> <bch:t> <bch:tm> <dbl> <bch:byt> 1 OP 23.9ms 24.38ms 39.4 7.73MB 2 extract 6.5ms 7.71ms 123. 69.84KB 3 f_recurse(df$a, df$b, df$c, df$x) 265.7us 271.9us 3596. 23.68KB 4 f_recurse_rcpp(df$a, df$b, df$c, df$x) 17.4us 18.9us 51845. 2.49KB df <- data.frame(a = sample(10000L), b = sample(10001:20000), c = seq(1000, 11000, length.out = 10000), x = rep(3, 10000L)) # A tibble: 4 x 13 expression min median `itr/sec` mem_alloc <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> 1 OP 353.17ms 412.62ms 2.42 763.38MB 2 extract 8.75ms 8.95ms 107. 280.77KB 3 f_recurse(df$a, df$b, df$c, df$x) 2.58ms 2.61ms 376. 234.62KB 4 f_recurse_rcpp(df$a, df$b, df$c, df$x) 98.6us 112.7us 8169. 2.49KB