Skip to content
Prev 317831 / 398503 Next

How to do a backward calculation for each record in a dataset

Some (quite a few!) years ago I wrote myself a wee function called
compInt() ("compound interest") to do --- I think --- just what you require.
I have attached the code for this function and a help file for it.

If anyone else wants this code, and if the attachments don't get through 
the list,
let me know and I can send the stuff to you directly.

     cheers,

         Rolf Turner
On 02/18/2013 10:34 PM, Prakasit Singkateera wrote:
-------------- next part --------------
compInt <- function(P=NULL,r=NULL,n=NULL,a=NULL) {
#
# Function compInt.  To calculate one of the parameters P,r,n,a,
# associated with the compound interest formula,
#
#
#                     12a
#  (1 + r/12)^n = ------------
#                  (12a - rP)
#
# given the other three.
# P = principle, r = annual interest rate (compounded monthly),
# n = number of months until loan is paid off; a = monthly payment.
#

chk <- sum(c(is.null(P),is.null(r),is.null(n),is.null(a)))
if(chk > 1) stop("Must specify either ONE or ZERO non-null arguments.\n")

if(!is.null(P) && (!is.numeric(P) || length(P) != 1 || P <= 0))
	stop("Argument \"P\" must be a positive numeric scalar.\n")
if(!is.null(r) && (!is.numeric(r) || length(r) != 1 || r <= 0))
	stop("Argument \"r\" must be a positive numeric scalar.\n")
if(!is.null(n) && (!is.numeric(n) || length(n) != 1 || n <= 0 ||
                   !isTRUE(all.equal(n,round(n)))))
	stop("Argument \"n\" must be a positive integer scalar.\n")
if(!is.null(a) && (!is.numeric(a) || length(a) != 1 || a <= 0))
	stop("Argument \"a\" must be a positive numeric scalar.\n")

if(chk==0) {
	A <- ((1+r/12)^n)*(P - 12*a/r) + 12*a/r
	A <- max(A,0)
	if(isTRUE(all.equal(A,0))) {
		nlast <- ceiling(Recall(P=P,r=r,a=a))
		attributes(nlast) <- NULL
	} else nlast <- NULL
	A <- c(A=A)
	if(!is.null(nlast)) attr(A,"lastNonZero") <- nlast
	return(A)
}

if(is.null(P))
	return(c(P=(12*a/r)*(1 - (1+r/12)^(-n))))

if(is.null(r)) {
	if(P/a > n) stop("You would need a negative interest rate!\n")
	if(n==1) return(c(r=12*(a-P)/P))
	fff <- function(r,P,n,a) {
		fval <- n*log(1+r/12) + log(12*a-r*P) - log(12*a)
		J    <- n/(12+r) - P/(12*a - r*P)
		list(fval=fval,jacobian=J)
	}
	r1 <- 12*(1+n/P)/(n-1)
	r2 <- 0.99*12*a/P
	rr <- seq(r1,r2,length=100)
	ss <- fff(rr,P,n,a)$fval
	r0 <- rr[which.min(abs(ss))]
	return(c(r=newt(fff,start=r0,P=P,n=n,a=a)))
}

if(is.null(n)) {
	if(r*P >= 12*a) return(Inf)
	n <- (log(12*a) - log(12*a - r*P))/log(1+r/12)
	nl <- floor(n)
	A <- Recall(P,r,nl,a)
	n <- c(n=ceiling(n))
	attr(n,"lastPayment") <- unname(A)
	return(n)
}

if(is.null(a))
	return(c(a=r*P/(12*(1 - (1+r/12)^(-n)))))
}
-------------- next part --------------
\name{compInt}
\alias{compInt}
\title{
Compound Interest
}
\description{
Calculate one of the parameters \code{P}, \code{r}, \code{n},
\code{a}, associated with the compound interest formula, i.e.:
\deqn{(1 + r/12)^n = \frac{12a}{12a -rP}}{(1+r/12)^n = 12a/(12a -rP)}
given the other three. Alternatively calculate the remaining amount
owing, given all four parameters.  In the compound interest formula
\eqn{P} = principle, \eqn{a} = annual interest rate (compounded
monthly), \eqn{n} = number of months until loan is paid off and
\eqn{a} = monthly payment.
}
\usage{
compInt(P = NULL, r = NULL, n = NULL, a = NULL)
}
\arguments{
  \item{P}{
  Positive numeric scalar equal to the principle of the loan.
}
  \item{r}{
  Positive numeric scalar equal to the annual interest rate (given as a
  \emph{fraction} and NOT as a percentage), compounded monthly.
}
  \item{n}{
  Positive integer scalar equal to the number of months until the loan
  is paid off.
}
  \item{a}{
  Positive numeric scalar equal to the amount of the monthly payment.
}
}
\details{
  Either three or four of the four arguments must be specified.  If one
  argument is left unspecified (i.e. left \code{NULL}) then its value
  will be calculated by the function.  If the unspecified argument is \code{n}
  then the returned value has an attribute \code{lastPayment} giving the
  amount of the last payment (which is in general less than \code{a}).

  If all four arguments are specified then the function calculates
  the amount \code{A} remaining to be paid off after \code{n} payments
  have been made.  If \code{A} is zero then the returned value
  has an attribute \code{lastNonZero} which is the payment number
  corresponding to the last non-zero payment.
}
\value{
  A numeric scalar equal to the value of the argument which was
  left \code{NULL}, or if no argument was left \code{NULL}, a numeric
  scalar equal to the amount remaining to be paid off after \code{n}
  payments have been made.  (See \bold{Details}.)
}
\author{Rolf Turner
  \email{r.turner at auckland.ac.nz}
  \url{http://www.math.unb.ca/~rolf}
}
\note{
  The formula was related to me by Ron Sandland, way back in the good
  old days when I was working for D.M.S. Sydney.  I originally coded
  it up in Splus.  Just now (29/October/2011) I dug around in stored
  files, turned up the code, and turned it into an R function.
}

\section{Warnings}{
  The interest rate \code{r} is interpreted as a \emph{fraction}
  and NOT as a percentage.  E.g. if you are thinking of an interest
  rate of 15\% per annum, then \code{r} should be entered as 0.15.

  The monetary values \code{P}, \code{a}, and \code{A}
  returned by the function are \emph{NOT} rounded to the nearest
  \dQuote{cent}, but rather are left with their usual floating
  point representation.
}

\examples{
compInt(P=800,r=0.15,a=40)
compInt(P=800,r=0.15,n=24)
compInt(P=800,n=24,a=40)
compInt(r=0.15,n=24,a=40)
compInt(P=800,r=0.15,n=24,a=40)
compInt(P=800,r=0.15,n=24,a=30)
}
\keyword{ utilities }