Skip to content

Commit a19f1f8

Browse files
authored
Merge pull request #50 from Merck/feature_drmd
Simplify constrained mortality calculations
2 parents 3241077 + 6728250 commit a19f1f8

File tree

1 file changed

+69
-133
lines changed

1 file changed

+69
-133
lines changed

R/discrmd.R

+69-133
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,20 @@
5353
# fits <- fit_ends_mods_spl(bosonc)
5454
# # Pick out best distribution according to min AIC
5555
# params <- list(
56-
# ppd = find_bestfit_spl(fits$ppd, "aic")$fit,
57-
# ttp = find_bestfit_spl(fits$ttp, "aic")$fit,
58-
# pfs = find_bestfit_spl(fits$pfs, "aic")$fit,
59-
# os = find_bestfit_spl(fits$os, "aic")$fit,
60-
# pps_cf = find_bestfit_spl(fits$pps_cf, "aic")$fit,
61-
# pps_cr = find_bestfit_spl(fits$pps_cr, "aic")$fit
56+
# ppd = find_bestfit(fits$ppd, "aic")$fit,
57+
# ttp = find_bestfit(fits$ttp, "aic")$fit,
58+
# pfs = find_bestfit(fits$pfs, "aic")$fit,
59+
# os = find_bestfit(fits$os, "aic")$fit,
60+
# pps_cf = find_bestfit(fits$pps_cf, "aic")$fit,
61+
# pps_cr = find_bestfit(fits$pps_cr, "aic")$fit
6262
# )
6363
# drmd_psm(ptdata=bosonc, dpam=params)
6464
# # Add a lifetable constraint
6565
# ltable <- tibble::tibble(lttime=0:20, lx=1-lttime*0.05)
6666
# drmd_psm(ptdata=bosonc, dpam=params, lifetable=ltable)
6767
drmd_psm <- function(ptdata, dpam, psmtype="simple", Ty=10, discrate=0, lifetable=NA, timestep=1) {
6868
# Declare local variables
69-
Tw <- tvec <- pfprob <- osprob <- adjosprob <- adjfac <- adjprob <- vn <- NULL
69+
Tw <- NULL
7070
# Time horizon in weeks (ceiling)
7171
Tw <- ceiling(convert_yrs2wks(Ty)/timestep)
7272
# Create dataset, starting with time vector, with half-cycle addition, and unconstrained probs
@@ -82,51 +82,8 @@ drmd_psm <- function(ptdata, dpam, psmtype="simple", Ty=10, discrate=0, lifetabl
8282
allh <- calc_haz_psm(timevar=ds$tmid, ptdata=ptdata, dpam=dpam, psmtype=psmtype)$adj
8383
# Derive the unconstrained PPD mortality probability
8484
ds$q_ppd <- 1-exp(-allh$ppd)
85-
# Derive the constrained life table
86-
ds$clx <- calc_ltsurv(convert_wks2yrs(ds$tzero), lifetable)
87-
# Other calculations on the dataset
88-
ds <- ds |>
89-
dplyr::mutate(
90-
# Derive the background mortality for this timepoint
91-
cqx = 1 - dplyr::lead(.data$clx)/.data$clx,
92-
# Derive the TTP probability (balancing item)
93-
q_pfs = 1 - dplyr::lead(.data$u_pf)/.data$u_pf,
94-
q_ttp = .data$q_pfs - .data$q_ppd,
95-
d_pf = .data$u_pf * .data$q_ppd,
96-
c_qpfs = .data$q_ttp + pmax(.data$q_ppd, .data$cqx),
97-
# Derive the PPS mortality probability
98-
d_pfpd = .data$u_pf + .data$u_pd - dplyr::lead(.data$u_pf) - dplyr::lead(.data$u_pd),
99-
d_pps = .data$d_pfpd - .data$d_pf,
100-
q_pps = dplyr::if_else(.data$u_pd==0, 0, .data$d_pps / .data$u_pd),
101-
# Constrained probabilities
102-
cqpfs = .data$q_ttp + pmax(.data$q_ppd, .data$cqx),
103-
cqpps = pmax(.data$q_pps, .data$cqx),
104-
# Derive the constrained PF and PD memberships
105-
c_pf = .data$u_pf,
106-
c_pd = .data$u_pd,
107-
)
108-
# Derive the constrained PF and PD memberships
109-
for (t in 2:(Tw)) {
110-
ds$c_pf[t] = ds$c_pf[t-1] * (1-ds$cqpfs[t-1])
111-
ds$c_pd[t] = ds$c_pf[t-1] * ds$q_ttp[t-1] + ds$c_pd[t-1] * (1 - ds$cqpps[t-1])
112-
}
113-
# The final membership probabilities are zero
114-
ds$c_pf[Tw+1] <- ds$c_pd[Tw+1] <- 0
115-
# Final calculations on the dataset
116-
ds <- ds |>
117-
dplyr::mutate(
118-
# Discount factor
119-
vn = (1+discrate)^(-convert_wks2yrs(.data$tmid)),
120-
# RMD components in each timestep
121-
rmd_pf = (.data$c_pf + dplyr::lead(.data$c_pf))/2 * .data$vn * timestep,
122-
rmd_pd = (.data$c_pd + dplyr::lead(.data$c_pd))/2 * .data$vn * timestep
123-
)
124-
ds$rmd_pf[Tw+1] <- ds$rmd_pd[Tw+1] <- 0
125-
# Calculate RMDs
126-
pf <- sum(ds$rmd_pf)
127-
pd <- sum(ds$rmd_pd)
128-
# Return values
129-
return(list(pf=pf, pd=pd, os=pf+pd, calc=ds))
85+
# Call routine to run calculations
86+
calc_drmd(ds, Tw, discrate, lifetable, timestep)
13087
}
13188

13289

@@ -142,73 +99,35 @@ drmd_psm <- function(ptdata, dpam, psmtype="simple", Ty=10, discrate=0, lifetabl
14299
# fits <- fit_ends_mods_spl(bosonc)
143100
# # Pick out best distribution according to min AIC
144101
# params <- list(
145-
# ppd = find_bestfit_spl(fits$ppd, "aic")$fit,
146-
# ttp = find_bestfit_spl(fits$ttp, "aic")$fit,
147-
# pfs = find_bestfit_spl(fits$pfs, "aic")$fit,
148-
# os = find_bestfit_spl(fits$os, "aic")$fit,
149-
# pps_cf = find_bestfit_spl(fits$pps_cf, "aic")$fit,
150-
# pps_cr = find_bestfit_spl(fits$pps_cr, "aic")$fit
102+
# ppd = find_bestfit(fits$ppd, "aic")$fit,
103+
# ttp = find_bestfit(fits$ttp, "aic")$fit,
104+
# pfs = find_bestfit(fits$pfs, "aic")$fit,
105+
# os = find_bestfit(fits$os, "aic")$fit,
106+
# pps_cf = find_bestfit(fits$pps_cf, "aic")$fit,
107+
# pps_cr = find_bestfit(fits$pps_cr, "aic")$fit
151108
# )
152109
# drmd_stm_cf(dpam=params)
153110
# # Add a lifetable constraint
154111
# ltable <- tibble::tibble(lttime=0:20, lx=1-lttime*0.05)
155112
# drmd_stm_cf(dpam=params, lifetable=ltable)
156113
drmd_stm_cf <- function(dpam, Ty=10, discrate=0, lifetable=NA, timestep=1) {
157114
# Declare local variables
158-
Tw <- tvec <- sppd <- sttp <- sos <- NULL
159-
adjsppd <- adjos <- vn <- pf <- os <- NULL
115+
Tw <- NULL
160116
# Time horizon in weeks (ceiling)
161117
Tw <- ceiling(convert_yrs2wks(Ty)/timestep)
162118
# Create dataset, starting with time vector, with half-cycle addition, and unconstrained probs
163119
ds <- tibble::tibble(
164120
tzero = timestep*(0:Tw),
165121
tmid = .data$tzero + timestep/2,
166122
u_pf = prob_pf_stm(.data$tzero, dpam),
123+
# Must be CF in next line
167124
u_pd = prob_pd_stm_cf(.data$tzero, dpam),
168125
# Calculate PPD hazard and probability
169126
h_ppd = calc_haz(.data$tmid, survobj=dpam$ppd),
170-
q_ppd = 1-exp(-.data$h_ppd),
171-
# Derive the constrained life table
172-
clx = calc_ltsurv(convert_wks2yrs(.data$tzero), lifetable),
173-
# Derive the background mortality for this timepoint
174-
cqx = 1 - dplyr::lead(.data$clx)/.data$clx,
175-
# Derive the TTP probability (balancing item for PFS)
176-
q_pfs = 1 - dplyr::lead(.data$u_pf)/.data$u_pf,
177-
q_ttp = .data$q_pfs-.data$q_ppd,
178-
d_pf = .data$u_pf * .data$q_ppd,
179-
# Derive the PPS mortality probability
180-
d_pfpd = .data$u_pf + .data$u_pd - dplyr::lead(.data$u_pf) - dplyr::lead(.data$u_pd),
181-
d_pps = .data$d_pfpd - .data$d_pf,
182-
q_pps = dplyr::if_else(.data$u_pd==0, 0, .data$d_pps / .data$u_pd),
183-
# Constrained probabilities
184-
cqpfs = .data$q_ttp + pmax(.data$q_ppd, .data$cqx),
185-
cqpps = pmax(.data$q_pps, .data$cqx),
186-
# Initial constrained PF and PD
187-
c_pf = .data$u_pf,
188-
c_pd = .data$u_pd
127+
q_ppd = 1-exp(-.data$h_ppd)
189128
)
190-
# Derive the constrained PF and PD memberships
191-
for (t in 2:(Tw)) {
192-
ds$c_pf[t] = ds$c_pf[t-1] * (1-ds$cqpfs[t-1])
193-
ds$c_pd[t] = ds$c_pf[t-1] * ds$q_ttp[t-1] + ds$c_pd[t-1] * (1 - ds$cqpps[t-1])
194-
}
195-
# The final membership probabilities are zero
196-
ds$c_pf[Tw+1] <- ds$c_pd[Tw+1] <- 0
197-
# Final calculations on the dataset
198-
ds <- ds |>
199-
dplyr::mutate(
200-
# Discount factor
201-
vn = (1+discrate)^(-convert_wks2yrs(tmid)),
202-
# RMD components in each timestep
203-
rmd_pf = (.data$c_pf + dplyr::lead(.data$c_pf))/2 * .data$vn * timestep,
204-
rmd_pd = (.data$c_pd + dplyr::lead(.data$c_pd))/2 * .data$vn * timestep
205-
)
206-
ds$rmd_pf[Tw+1] <- ds$rmd_pd[Tw+1] <- 0
207-
# Calculate RMDs
208-
pf <- sum(ds$rmd_pf)
209-
pd <- sum(ds$rmd_pd)
210-
# Return values
211-
return(list(pf=pf, pd=pd, os=pf+pd, calc=ds))
129+
# Call routine to run calculations
130+
calc_drmd(ds, Tw, discrate, lifetable, timestep)
212131
}
213132

214133
#' Discretized Restricted Mean Duration calculation for State Transition Model Clock Reset structure
@@ -223,54 +142,71 @@ drmd_stm_cf <- function(dpam, Ty=10, discrate=0, lifetable=NA, timestep=1) {
223142
# fits <- fit_ends_mods_spl(bosonc)
224143
# # Pick out best distribution according to min AIC
225144
# params <- list(
226-
# ppd = find_bestfit_spl(fits$ppd, "aic")$fit,
227-
# ttp = find_bestfit_spl(fits$ttp, "aic")$fit,
228-
# pfs = find_bestfit_spl(fits$pfs, "aic")$fit,
229-
# os = find_bestfit_spl(fits$os, "aic")$fit,
230-
# pps_cf = find_bestfit_spl(fits$pps_cf, "aic")$fit,
231-
# pps_cr = find_bestfit_spl(fits$pps_cr, "aic")$fit
145+
# ppd = find_bestfit(fits$ppd, "aic")$fit,
146+
# ttp = find_bestfit(fits$ttp, "aic")$fit,
147+
# pfs = find_bestfit(fits$pfs, "aic")$fit,
148+
# os = find_bestfit(fits$os, "aic")$fit,
149+
# pps_cf = find_bestfit(fits$pps_cf, "aic")$fit,
150+
# pps_cr = find_bestfit(fits$pps_cr, "aic")$fit
232151
# )
233152
# drmd_stm_cr(dpam=params)
234153
# # Add a lifetable constraint
235154
# ltable <- tibble::tibble(lttime=0:20, lx=1-lttime*0.05)
236155
# drmd_stm_cr(dpam=params, lifetable=ltable)
237156
drmd_stm_cr <- function(dpam, Ty=10, discrate=0, lifetable=NA, timestep=1) {
238157
# Declare local variables
239-
Tw <- tvec <- sppd <- sttp <- sos <- NULL
240-
adjsppd <- adjos <- vn <- pf <- os <- NULL
158+
Tw <- NULL
241159
# Time horizon in weeks (ceiling)
242160
Tw <- ceiling(convert_yrs2wks(Ty)/timestep)
243161
# Create dataset, starting with time vector, with half-cycle addition, and unconstrained probs
244162
ds <- tibble::tibble(
245163
tzero = timestep*(0:Tw),
246164
tmid = .data$tzero + timestep/2,
247165
u_pf = prob_pf_stm(.data$tzero, dpam),
248-
u_pd = prob_pd_stm_cf(.data$tzero, dpam)
249-
)
250-
# Keep calculating membership probabilities
251-
ds <- ds |>
252-
dplyr::mutate(
166+
# Must be CR in next line
167+
u_pd = prob_pd_stm_cr(.data$tzero, dpam),
253168
# Calculate PPD hazard and probability
254169
h_ppd = calc_haz(.data$tmid, survobj=dpam$ppd),
255-
q_ppd = 1-exp(-h_ppd),
256-
# Derive the constrained life table
257-
clx = calc_ltsurv(convert_wks2yrs(.data$tzero), lifetable),
258-
cqx = 1 - dplyr::lead(.data$clx)/.data$clx,
259-
# Derive the TTP probability (balancing item for PF)
260-
q_pfs = 1 - dplyr::lead(.data$u_pf)/.data$u_pf,
261-
q_ttp = .data$q_pfs - .data$q_ppd,
262-
d_pf = .data$u_pf * .data$q_ppd,
263-
# Derive the PPS mortality probability
264-
d_pfpd = .data$u_pf + .data$u_pd - dplyr::lead(.data$u_pf) - dplyr::lead(.data$u_pd),
265-
d_pps = .data$d_pfpd - .data$d_pf,
266-
q_pps = dplyr::if_else(.data$u_pd==0, 0, .data$d_pps / .data$u_pd),
267-
# Constrained probabilities
268-
cqpfs = .data$q_ttp + pmax(.data$q_ppd, .data$cqx),
269-
cqpps = pmax(.data$q_pps, .data$cqx),
270-
# Initial constrained PF and PD
271-
c_pf = .data$u_pf,
272-
c_pd = .data$u_pd
170+
q_ppd = 1-exp(-.data$h_ppd)
273171
)
172+
# Call routine to run calculations
173+
calc_drmd(ds, Tw, discrate, lifetable, timestep)
174+
}
175+
176+
#' Calculate the constrained and discounted RMDs
177+
#'
178+
#' @inheritParams drmd_psm
179+
#' @param ds Dataset required for this calculation must be a tibble including: tzero, tmid, q_ppd, u_pf and u_pd
180+
#' @importFrom rlang .data
181+
#' @return List containing:
182+
#' - pf: RMD in PF state
183+
#' - pd: RMD in PD state
184+
#' - os: RMD in either alive state
185+
#' @noRd
186+
calc_drmd <- function(ds, Tw, discrate, lifetable, timestep) {
187+
# Derive the constrained life table
188+
ds$clx <- calc_ltsurv(convert_wks2yrs(ds$tzero), lifetable)
189+
# Other calculations on the dataset
190+
ds <- ds |>
191+
dplyr::mutate(
192+
# Derive the background mortality for this timepoint
193+
cqx = 1 - dplyr::lead(.data$clx)/.data$clx,
194+
# Derive the TTP probability (balancing item)
195+
q_pfs = 1 - dplyr::lead(.data$u_pf)/.data$u_pf,
196+
q_ttp = .data$q_pfs - .data$q_ppd,
197+
d_pf = .data$u_pf * .data$q_ppd,
198+
c_qpfs = .data$q_ttp + pmax(.data$q_ppd, .data$cqx),
199+
# Derive the PPS mortality probability
200+
d_pfpd = .data$u_pf + .data$u_pd - dplyr::lead(.data$u_pf) - dplyr::lead(.data$u_pd),
201+
d_pps = .data$d_pfpd - .data$d_pf,
202+
q_pps = dplyr::if_else(.data$u_pd==0, 0, .data$d_pps / .data$u_pd),
203+
# Constrained probabilities
204+
cqpfs = .data$q_ttp + pmax(.data$q_ppd, .data$cqx),
205+
cqpps = pmax(.data$q_pps, .data$cqx),
206+
# Derive the constrained PF and PD memberships
207+
c_pf = .data$u_pf,
208+
c_pd = .data$u_pd,
209+
)
274210
# Derive the constrained PF and PD memberships
275211
for (t in 2:(Tw)) {
276212
ds$c_pf[t] = ds$c_pf[t-1] * (1-ds$cqpfs[t-1])
@@ -293,4 +229,4 @@ drmd_stm_cr <- function(dpam, Ty=10, discrate=0, lifetable=NA, timestep=1) {
293229
pd <- sum(ds$rmd_pd)
294230
# Return values
295231
return(list(pf=pf, pd=pd, os=pf+pd, calc=ds))
296-
}
232+
}

0 commit comments

Comments
 (0)