Nothing
# Copyright (c) 2025 Merck & Co., Inc., Rahway, NJ, USA and its affiliates.
# All rights reserved.
#
# This file is part of the dynamicpv program.
#
# dynamicpv is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Classes and methods for dynpv
# =============================
#' Method to add two dynpv objects together
#'
#' Add together two objects each of class "dynpv"
#'
#' @inherit addprod params details return
#'
#' @export
"+.dynpv" <- function(e1, e2) addprod(e1, e2, mult=1)
#' Method to subtract one dynpv object from another
#'
#' Subtract one object of S7 class "dynpv" from another
#'
#' Present value of `e1-e2` is the present values from `e1` less that from `e2`.
#' Total uptake of `e1-e2` is the uptake from `e1` less that from `e2`. Take
#' care of this when using `$mean` of the summed object.
#'
#' @inherit addprod params return
#' @export
"-.dynpv" <- function(e1, e2) addprod(e1, e2, mult=-1)
#' Method to add two dynpv objects together
#'
#' Add together two objects each of S3 class "dynpv": e1 + mult * e2
#'
#' @param e1 First "dynpv" object
#' @param e2 Second "dynpv" object
#' @param mult Numeric
#'
#' Present value is present value from `e1` plus `mult` times the present value
#' from `e2`. Total uptake is the uptake from `e1` plus `mult` times the uptake
#' from `e2`. Take care of this when using `$mean` of the summed object.
#'
#' @seealso [dynpv()], [futurepv()]
#' @returns S3 object of class "dynpv"
#'
#' @export
addprod <- function(e1, e2, mult) {
# Avoid no visible binding note
j <- k <- l <- dpvno <- uj <- pv <- uj_x <- uj_y <- pv_x <- pv_y <- NULL
# Pull out xdata and subset of ydata; add dpvno=1 or 2 depending on source
xdata <- e1 |> mutate(dpvno="x")
ydata <- e2 |> mutate(dpvno="y")
# Check that j, k and l vectors align
if (length(xdata$j) != length(ydata$j)) {warning("Uptake vectors differ in length")}
stopifnot("Length of payoffs vectors differ" = max(xdata$k) == max(ydata$k))
stopifnot("Length of time-offset vectors (tzero) differ" = max(xdata$l) == max(ydata$l))
# Combine data
jdata <- bind_rows(xdata, ydata) |>
# Spread
pivot_wider(
id_cols = c(j, k, l, t),
names_from = dpvno,
values_from = c(uj, pv),
values_fill = 0
) |>
# Sum the present values from both objects
mutate(
uj = uj_x + mult * uj_y,
pv = pv_x + mult * pv_y
) |>
select(-uj_x, -uj_y, -pv_x, -pv_y) |>
as_tibble()
class(jdata) <- c("dynpv", class(jdata))
return(jdata)
}
#' Number of cohorts of uptaking patients
#'
#' Number of cohorts of uptaking patients, calculated as the length of the `uptakes` input to [dynpv()]
#'
#' @param df Tibble of class "dynpv" created by [dynpv()] or [futurepv()]
#' @seealso [dynpv()], [futurepv()]
#' @return A number
#'
#' @export
ncoh <- function(df) max(df$k)
#' Number of times at which present value calculations are performed
#'
#' Number of times at which present value calculations are performed, calculated as the length of the `tzero` input to [dynpv()]
#'
#' @inherit ncoh params return
#' @seealso [dynpv()], [futurepv()]
#' @export
ntimes <- function(df) length(unique(df$l))
#' Total number of uptaking patients
#'
#' Total number of uptaking patients, calculated as the sum of the `uptake` input to [dynpv()], or \eqn{\sum_{j=1}^T u_j}
#'
#' @inherit ncoh params
#' @seealso [dynpv()], [futurepv()]
#' @return A number or tibble
#'
#' @export
uptake <- function(df) {
# Avoid no visible binding note
uj <- sd <- j <- NULL
tempout1 <- df |>
summarize(mean=mean(uj), sd=sd(uj), .by=c(j)) |>
summarize(uptake=sum(mean))
return(tempout1$uptake)
}
#' Present value for each uptake cohort and calculation time
#'
#' Calculates the sum of the Present Value by uptake cohort (`j`) and time at which the calculation is performed (`tzero` input to [dynpv()])
#'
#' The [Present Value](https://en.wikipedia.org/wiki/Present_value) of a cashflow \eqn{p_k} for the \eqn{u_j} patients who began treatment at time \eqn{j} and who are in their \eqn{k}th timestep of treatment is as follows
#' \deqn{PV(j,k,l) = u_j \cdot p_k \cdot R_{j+k+l-1} \cdot (1+i)^{2-j-k}}
#' where \eqn{i} is the risk-free discount rate per timestep, \eqn{p_k} is the cashflow amount in today’s money, and \eqn{p_k \cdot R_{j+k+l-1}} is the nominal amount of the cashflow at the time it is incurred, allowing for an offset of \eqn{l = tzero}.
#'
#' This method returns \eqn{\sum_{k=1}^{T-j+1} PV(j,k,l)} for each value of \eqn{j} and \eqn{l}, where \eqn{T} is the time horizon of the calculation.
#'
#' @inherit uptake params return
#' @seealso [dynpv()], [futurepv()]
#' @return A number or tibble
#' @export
sum_by_coh <- function(df) {
# Avoid no visible binding note
pv <- j <- l <- NULL
tempout2 <- df |>
# Summing over k, where uj does not vary by k
summarize(spv = sum(pv), .by=c(j, l)) |>
rename(tzero = l)
result <- if (nrow(tempout2)==1) tempout2$spv else tempout2
return(result)
}
#' Total present value
#'
#' Sum of the Present Value, by time at which the calculation is performed (`tzero` input to [dynpv()])
#'
#' The [Present Value](https://en.wikipedia.org/wiki/Present_value) of a cashflow \eqn{p_k} for the \eqn{u_j} patients who began treatment at time \eqn{j} and who are in their \eqn{k}th timestep of treatment is as follows
#' \deqn{PV(j,k,l) = u_j \cdot p_k \cdot R_{j+k+l-1} \cdot (1+i)^{2-j-k}}
#' where \eqn{i} is the risk-free discount rate per timestep, \eqn{p_k} is the cashflow amount in today’s money, and \eqn{p_k \cdot R_{j+k+l-1}} is the nominal amount of the cashflow at the time it is incurred, allowing for an offset of \eqn{l = tzero}.
#'
#' The total present value by time at which the calculation is performed, \eqn{TPV(l)}, is therefore the sum of \eqn{PV(j,k,l)} over all \eqn{j} and \eqn{k} within the time horizon \eqn{T}, namely:
#' \deqn{TPV(l) = \sum_{j=1}^{T} \sum_{k=1}^{T-j+1} PV(j,k, l) \\
#' \;
#' = \sum_{j=1}^{T} \sum_{k=1}^{T-j+1} u_j \cdot p_k \cdot R_{l+j+k-1} \cdot (1+i)^{2-j-k}}
#'
#' @inherit uptake params return
#' @seealso [dynpv()], [futurepv()]
#' @return A number or tibble
#' @export
total <- function(df) {
# Avoid no visible binding note
pv <- l <- NULL
tempout3 <- df |>
summarize(total = sum(pv), .by=c(l)) |>
rename(tzero = l)
result <- if (nrow(tempout3)==1) tempout3$total else tempout3
return(result)
}
#' Mean present value per uptaking patient
#'
#' Mean of the Present Value per uptaking patient, by time at which the calculation is performed (`tzero` input to [dynpv()]).
#'
#' This is equal to [total()] divided by [uptake()].
#'
#' @param x Tibble of class "dynpv" created by [dynpv()] or [futurepv()]
#' @param ... Currently unused
#'
#' @inherit uptake return
#' @seealso [dynpv()], [futurepv()]
#' @export
mean.dynpv <- function(x, ...) {
# Avoid no visible binding note
tzero <- NULL
total <- total(x)
uptake <- uptake(x)
if (length(total)==1) {
total / uptake
} else {
total |>
mutate(
uptake = uptake,
mean = total / uptake
) |>
select(-total, -uptake)
}
}
#' Summarize a dynpv object
#'
#' @param object Tibble of class "dynpv" created by [dynpv()] or [futurepv()]
#' @param ... Currently unused
#'
#' @return A list of class "dynpv_summary" with the following elements:
#' - `ncoh`: Number of cohorts of uptaking patients, from [ncoh()]
#' - `ntimes`: Number of times at which present value calculations are performed, from [ntimes()]
#' - `uptake`: Total number of uptaking patients, from [uptake()]
#' - `sum_by_coh`: Present value for each uptake cohort and calculation time, from [sum_by_coh()]
#' - `total`: Total present value, from [total()]
#' - `mean`: Mean present value per uptaking patient, from [mean()], equal to `total`/`uptake`.
#' @seealso [dynpv()], [futurepv()]
#' @export
summary.dynpv <- function(object, ...) {
structure(
class = "dynpv_summary",
list(
ncoh = ncoh(object),
ntimes = ntimes(object),
uptake = uptake(object),
sum_by_coh = sum_by_coh(object),
total = total(object),
mean = mean(object)
)
)
}
#' @export
print.dynpv_summary <- function(x, ...) {
cat("Summary of Dynamic Pricing and Uptake\n")
cat(" Number of cohorts: ", x$ncoh, "\n")
cat(" Number of times: ", x$ntimes, "\n")
cat(" Total uptake: ", x$uptake, "\n")
# Output depends on whether $ntimes>1
if (x$ntimes>1) {
# Avoid no visible binding note
tzero <- NULL
# Create a tibble
tib <- x$total |>
left_join(x$mean, join_by(tzero)) |>
select(tzero, total, mean)
cat("\n Total and mean present values by timepoint: \n")
print(tib)
}
else {
cat(" Total present value: ", x$total, "\n")
cat(" Mean present value: ", x$mean, "\n")
}
return(invisible(x))
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.