library(data.table)
q <- c(0.001,
0.002,
0.003,
0.003,
0.004,
0.004,
0.005,
0.007,
0.009,
0.011)
w <- c(0.05,
0.07,
0.08,
0.10,
0.14,
0.20,
0.20,
0.20,
0.10,
0.04)
P <- 100
S <- 25000
r <- 0.02
dt <- as.data.table(cbind(q,w))
npv <- function(cf, r, S, P) {
cf[, inforce := shift(cumprod(1 - q - w), fill = 1)
][, lapses := inforce * w
][, deaths := inforce * q
][, claims := deaths * S
][, premiums := inforce * P
][, ncf := premiums - claims
][, d := (1/(1+r))^(.I)
][, sum(ncf*d)]
}
npv(dt,r,S,P)
#> [1] 50.32483
microbenchmark::microbenchmark(npv(dt,r,S,P))
#> Unit: milliseconds
#> expr min lq mean median uq max neval
#> npv(dt, r, S, P) 2.5791 2.71035 2.964293 2.85625 3.10385 6.0357 100
Created on 2021-01-15 by the reprex package (v0.3.0)
Thanks - good to see. On the benchmarks, just making sure I understand them?
R using data.table is 2.8ms ~= 2800μs
R analytically is 9μs (median)
Julia analytically is 3μs (median) after the initial JIT compilation
So the 'pure'/base language solution trumps using data.tables, and Julia is 3x the speed of R on equivalent code.
My gripe with this (and not a reflection on your impressive coding skills!) is that this is very bespoke code for the problem, and likely to cause anyone reading it a fair bit of head scratching to work out what it does. I think the balance between speed and readibility is an important discussion so will share that on the commons.