Dynamic uptake refers to the modeling of multiple series of payoffs/cashflows over time, rather than the modeling of just one series of payoffs/cashflows at a time. This may arise, for example, in considering the treatment costs of multiple cohorts of patients beginning their treatment courses at different times. There are also analogies to ‘run-off triangles’ used by general insurance actuaries to model the emergence of insurance claims over time following claim events.
This package is designed to be able to calculate presente values with
dynamic uptake/multiple cohorts. This can be coupled with considerations
of dynamic pricing, as covered in
vignette("Dynamic Pricing")
.
First let us load the packages we will use for this vignette.
Let us work with the same cashflow as in
vignette("Dynamic Pricing")
, but assume all costs/prices
are constant in real terms. The discount rate is 3 % per year, real.
# A simple cashflow
cashflow <- c(110, 120, 130, 140, 150)
# (Real) discount rate of 3\% per timestep (year)
disc <- 0.03
# Discounting factor - used for base R calculations later
vt1 <- (1 + disc)^(-1 * (0:4))
First let us consider non-dynamic uptake, as covered in
vignette("Dynamic Pricing")
.
Time | Cashflow 1 | Discount factor | Product |
---|---|---|---|
1 | 110 | 1 | 110 |
2 | 120 | 0.971 | 117 |
3 | 130 | 0.943 | 123 |
4 | 140 | 0.915 | 128 |
5 | 150 | 0.888 | 133 |
Total | 650 | 610 |
In this case, there is a single cashflow series starting at timestep
1. This time, we will use the uptakes
argument for
dynamicpv::dynpv()
- although this is unnecessary since its
default is also 1.
# Uptake vector is simple
uptakes1 <- 1
# NPV calculation
pv1 <- dynpv(payoffs=cashflow, uptakes=uptakes1, discrate=disc)
pv1$results$total
#> [1] 610.4352
The result is unchanged from the
vignette("Dynamic Pricing")
, where we also showed the
function could be verified with some simple vector arithmetic.
Suppose we have one new patient each year, and we wish to calculate the total NPV in a time horizon of 5 years. Now we have a payoff triangle as follows.
Time | Cashflow 1 | Cashflow 2 | Cashflow 3 | Cashflow 4 | Cashflow 5 | Cashflow Sum | Discount factor | Product |
---|---|---|---|---|---|---|---|---|
1 | 110 | - | - | - | - | 110 | 1 | 110 |
2 | 120 | 110 | - | - | - | 230 | 0.971 | 223 |
3 | 130 | 120 | 110 | - | - | 360 | 0.943 | 339 |
4 | 140 | 130 | 120 | 110 | - | 500 | 0.915 | 458 |
5 | 150 | 140 | 130 | 120 | 110 | 650 | 0.888 | 578 |
Total | 650 | 1,708 |
With dynamicpv::dynpv()
, we just update the
uptakes
argument.
# Uptake vector is simple
uptakes2 <- rep(1, 5)
uptakes2
#> [1] 1 1 1 1 1
# NPV calculation
pv2 <- dynpv(payoffs=cashflow, uptakes=uptakes2, discrate=disc)
pv2$results
#> $ncoh
#> [1] 5
#>
#> $uptake
#> [1] 5
#>
#> $calc
#> # A tibble: 15 × 9
#> j k l t uj pk R v pv
#> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1 1 0 1 1 110 1 1 110
#> 2 1 2 0 2 1 120 1 0.971 117.
#> 3 1 3 0 3 1 130 1 0.943 123.
#> 4 1 4 0 4 1 140 1 0.915 128.
#> 5 1 5 0 5 1 150 1 0.888 133.
#> 6 2 1 0 2 1 110 1 0.971 107.
#> 7 2 2 0 3 1 120 1 0.943 113.
#> 8 2 3 0 4 1 130 1 0.915 119.
#> 9 2 4 0 5 1 140 1 0.888 124.
#> 10 3 1 0 3 1 110 1 0.943 104.
#> 11 3 2 0 4 1 120 1 0.915 110.
#> 12 3 3 0 5 1 130 1 0.888 116.
#> 13 4 1 0 4 1 110 1 0.915 101.
#> 14 4 2 0 5 1 120 1 0.888 107.
#> 15 5 1 0 5 1 110 1 0.888 97.7
#>
#> $cohpv
#> # A tibble: 5 × 3
#> j tzero spv
#> <int> <dbl> <dbl>
#> 1 1 0 610.
#> 2 2 0 463.
#> 3 3 0 329.
#> 4 4 0 207.
#> 5 5 0 97.7
#>
#> $total
#> [1] 1707.723
#>
#> $mean
#> [1] 341.5446
There are 5 patients with a total NPV of 1,708, which equates to 342 on average per patient. This can also be calculated in base R.
In general, uptake will not be constant in time. Due to factors such as varying epidemiology (prevalence and incidence), prior treatment pathways (patient testing, patient diagnostics, prior treatment usage), as well as the specific uptake of the treatment of interest (patient share), uptake will also be highly variable over time. In this setting, the total and mean NPVs must reflect weightings applicable to each cashflow.
For example, suppose the number of patients receiving treatment increases by one each year. The weighting given to cashflow 1 would then be %.
# Uptake vector is simple
uptakes3 <- 1:5
# NPV calculation
pv3 <- dynpv(payoffs=cashflow, uptakes=uptakes3, discrate=disc)
pv3$results
#> $ncoh
#> [1] 5
#>
#> $uptake
#> [1] 15
#>
#> $calc
#> # A tibble: 15 × 9
#> j k l t uj pk R v pv
#> <int> <int> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 1 1 0 1 1 110 1 1 110
#> 2 1 2 0 2 1 120 1 0.971 117.
#> 3 1 3 0 3 1 130 1 0.943 123.
#> 4 1 4 0 4 1 140 1 0.915 128.
#> 5 1 5 0 5 1 150 1 0.888 133.
#> 6 2 1 0 2 2 110 1 0.971 214.
#> 7 2 2 0 3 2 120 1 0.943 226.
#> 8 2 3 0 4 2 130 1 0.915 238.
#> 9 2 4 0 5 2 140 1 0.888 249.
#> 10 3 1 0 3 3 110 1 0.943 311.
#> 11 3 2 0 4 3 120 1 0.915 329.
#> 12 3 3 0 5 3 130 1 0.888 347.
#> 13 4 1 0 4 4 110 1 0.915 403.
#> 14 4 2 0 5 4 120 1 0.888 426.
#> 15 5 1 0 5 5 110 1 0.888 489.
#>
#> $cohpv
#> # A tibble: 5 × 3
#> j tzero spv
#> <int> <dbl> <dbl>
#> 1 1 0 610.
#> 2 2 0 927.
#> 3 3 0 987.
#> 4 4 0 829.
#> 5 5 0 489.
#>
#> $total
#> [1] 3841.785
#>
#> $mean
#> [1] 256.119
There are 15 patients with a total NPV of 3,842, which equates to 256 on average per patient. Verifying this result in base R also becomes more complicated.
The package becomes most powerful when considering both dynamic
uptake and dynamic pricing. A simple call to
dynamicpv::dynpv()
replaces what would be rather more
complicated with base functions, even in this toy example.
# Price index
pinfl <- 0.01
pindex <- (1+pinfl)^(0:4)
pindex[4:5] <- 0.5
# Nominal discount rate
nomdisc <- (1+disc)*(1+pinfl)-1
# NPV calculation
pv4 <- dynpv(payoffs=cashflow, uptakes=uptakes3, prices=pindex, discrate=nomdisc)
pv4$results
#> $ncoh
#> [1] 5
#>
#> $uptake
#> [1] 15
#>
#> $calc
#> # A tibble: 15 × 9
#> j k l t uj pk R v pv
#> <int> <int> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 1 1 0 1 1 110 1 1 110
#> 2 1 2 0 2 1 120 1.01 0.961 117.
#> 3 1 3 0 3 1 130 1.02 0.924 123.
#> 4 1 4 0 4 1 140 0.5 0.888 62.2
#> 5 1 5 0 5 1 150 0.5 0.854 64.0
#> 6 2 1 0 2 2 110 1.01 0.961 214.
#> 7 2 2 0 3 2 120 1.02 0.924 226.
#> 8 2 3 0 4 2 130 0.5 0.888 115.
#> 9 2 4 0 5 2 140 0.5 0.854 120.
#> 10 3 1 0 3 3 110 1.02 0.924 311.
#> 11 3 2 0 4 3 120 0.5 0.888 160.
#> 12 3 3 0 5 3 130 0.5 0.854 166.
#> 13 4 1 0 4 4 110 0.5 0.888 195.
#> 14 4 2 0 5 4 120 0.5 0.854 205.
#> 15 5 1 0 5 5 110 0.5 0.854 235.
#>
#> $cohpv
#> # A tibble: 5 × 3
#> j tzero spv
#> <int> <dbl> <dbl>
#> 1 1 0 475.
#> 2 2 0 675.
#> 3 3 0 637.
#> 4 4 0 400.
#> 5 5 0 235.
#>
#> $total
#> [1] 2422.633
#>
#> $mean
#> [1] 161.5089
There are 15 patients with a total NPV of 2,423, which equates to 162 on average per patient.
dynamicpv::dynpv()
function can be used to
calculate NPVs for arbitrary vectors of cashflows, at a given discount
rate and a given expected uptake over time.vignette("Dynamic Pricing")
.