I have been trying to avoid the use of for-loops in R in order to speed up calculations and simplify, relying on vector functions instead where possible. I've succeeded so far, until running into certain amortization calculations. I hit a brick wall and had to resort to a for-loop, see MWE code below. It works, ties out fine, but I'd like to replace it with vector or other more efficient functions. Can someone please help me replace the below with vector functions?
In the full code from which this MWE is extracted, it is reactive using Shiny. The periods and vector rates, actually all variables, change drastically depending on user inputs. The MWE example inputs variables are simplified.
In any case, the below is a very awkward, chainsaw approach and needs to be slimmed down. But I don't know how, having approached this from a complete XLS mindset where I have the most experience. If a for-loop is the only viable option for these sorts of calculations, I welcome any suggestions for improving the below MWE.
At the very bottom is code for a flawed attempt to "vectorize" but results are inaccurate when the vector variables change over periods. I show one of the problems with this vectorized approach in the image at the bottom where ending/beginning balances don't match when moving from one period to the next (the for-loop MWE code doesn't have those problems - it's functional but super clumsy).
For-loop MWE code:
periods <- 10
beginBal <- 1000
yield_vector <- c(0.30,0.30,0.30,0.30,0.30,0.28,0.26,0.20,0.18,0.20)
npr_vector <- c(0.30,0.30,0.30,0.30,0.30,0.30,0.30,0.30,0.30,0.30)
mpr_vector <- c(0.20,0.20,0.20,0.20,0.20,0.20,0.20,0.20,0.20,0.20)
default_vector <- c(0.10,0.10,0.10,0.10,0.10,0.09,0.08,0.07,0.06,0.05)
amort <- data.frame(period=seq(1,periods,1),
beginBal=rep(NA,periods),
yield=rep(NA,periods,),
purchases=rep(NA,periods),
payments=rep(NA,periods),
defaults=rep(NA,periods),
endBal=rep(NA,periods))
# Completes first row of data frame
amort[1,2] <- beginBal
amort[1,3] <- beginBal * yield_vector[1]/12
amort[1,4] <- beginBal * npr_vector[1]
amort[1,5] <- beginBal * mpr_vector[1]
amort[1,6] <- beginBal * default_vector[1] / 12
amort[1,7] <- beginBal + amort[1,4] - amort[1,5] - amort[1,6]
# Completes remaining rows of data frame
for(i in 2:nrow(amort)){
amort[i,2] <- amort[i-1,7]
amort[i,3] <- amort[i,2] * yield_vector[i]/12
amort[i,4] <- amort[i,2] * npr_vector[i]
amort[i,5] <- amort[i,2] * mpr_vector[i]
amort[i,6] <- amort[i,2] * default_vector[i]/12
amort[i,7] <- amort[i,2] + amort[i,4] - amort[i,5] - amort[i,6]
}
amort
And here's that sleek-looking but flawed attempt to vectorize, see one of its output flaws in the below image (these problems don't arise in the above for-loop MWE):
amort <- data.frame(period=seq(1,periods,1))
amort$beginBal <- beginBal*(1-(mpr_vector[]+default_vector[]/12-npr_vector[]))^(amort$period-1)
amort$yield <- amort$beginBal*yield_vector[]/12
amort$purchases <- amort$beginBal*npr_vector[]
amort$payments <- amort$beginBal*mpr_vector[]
amort$defaults <- amort$beginBal*default_vector[]/12
amort$endBal <- amort$beginBal+amort$purchases-amort$payments-amort$defaults
amort <- cbind(amort,yield_vector,npr_vector,mpr_vector,default_vector)
amort
