44
44
# ' - pf: RMD in PF state
45
45
# ' - pd: RMD in PD state
46
46
# ' - os: RMD in either alive state
47
+ # ' @importFrom rlang .data
47
48
# ' @seealso [drmd_stm_cf()] [drmd_stm_cr()]
48
49
# ' @noRd
49
50
# Examples
@@ -67,32 +68,72 @@ drmd_psm <- function(ptdata, dpam, psmtype="simple", Ty=10, discrate=0, lifetabl
67
68
# Declare local variables
68
69
Tw <- tvec <- pfprob <- osprob <- adjosprob <- adjfac <- adjprob <- vn <- NULL
69
70
# Time horizon in weeks (ceiling)
70
- Tw <- convert_yrs2wks(Ty )
71
- # Create time vector, with half-cycle addition
72
- tvec <- timestep * (0 : floor(Tw / timestep )) + timestep / 2
71
+ Tw <- ceiling(convert_yrs2wks(Ty )/ timestep )
72
+ # Create dataset, starting with time vector, with half-cycle addition, and unconstrained probs
73
+ ds <- tibble :: tibble(
74
+ tzero = timestep * (0 : Tw ),
75
+ tmid = .data $ tzero + timestep / 2 ,
76
+ pfprob = prob_pf_psm(.data $ tzero , dpam ),
77
+ osprob = prob_os_psm(.data $ tzero , dpam ),
78
+ u_pf = .data $ pfprob ,
79
+ u_pd = .data $ osprob - .data $ pfprob
80
+ )
73
81
# Obtain all the hazards
74
- allh <- calc_haz_psm(timevar = tvec , ptdata = ptdata , dpam = dpam , psmtype = psmtype )$ adj
75
- # PFS and OS probabilties from PSM
76
- pfprob <- prob_pf_psm(tvec , dpam )
77
- osprob <- prob_os_psm(tvec , dpam )
78
- # OS and PFS may be constrained already by definitions of PPD and PPS
79
- maxos <- exp(- cumsum(allh $ os ))
80
- maxpfs <- exp(- cumsum(allh $ pfs ))
81
- # Further constrain OS by lifetable
82
- conos <- constrain_survprob(survprob1 = maxos , survprob2 = osprob , lifetable = lifetable , timevec = tvec )
83
- conpfs <- constrain_survprob(survprob1 = maxpfs , survprob2 = pmin(pfprob , osprob ), lifetable = lifetable , timevec = tvec )
84
- # Discount factor
85
- vn <- (1 + discrate )^ (- convert_wks2yrs(tvec + timestep / 2 ))
82
+ allh <- calc_haz_psm(timevar = ds $ tmid , ptdata = ptdata , dpam = dpam , psmtype = psmtype )$ adj
83
+ # Derive the unconstrained PPD mortality probability
84
+ 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
86
125
# Calculate RMDs
87
- pf <- sum(conpfs * vn ) * timestep
88
- os <- sum(conos * vn ) * timestep
126
+ pf <- sum(ds $ rmd_pf )
127
+ pd <- sum(ds $ rmd_pd )
89
128
# Return values
90
- return (list (pf = pf , pd = os - pf , os = os ))
129
+ return (list (pf = pf , pd = pd , os = pf + pd , calc = ds ))
91
130
}
92
131
132
+
93
133
# ' Discretized Restricted Mean Duration calculation for State Transition Model Clock Forward structure
94
134
# ' Calculate restricted mean duration (RMD) in PF, PD and OS states under a State Transition Model Clock Forward structure.
95
135
# ' @inherit drmd_psm params return
136
+ # ' @importFrom rlang .data
96
137
# ' @seealso [drmd_psm()] [drmd_stm_cr()]
97
138
# ' @noRd
98
139
# Examples
@@ -117,35 +158,63 @@ drmd_stm_cf <- function(dpam, Ty=10, discrate=0, lifetable=NA, timestep=1) {
117
158
Tw <- tvec <- sppd <- sttp <- sos <- NULL
118
159
adjsppd <- adjos <- vn <- pf <- os <- NULL
119
160
# Time horizon in weeks (ceiling)
120
- Tw <- convert_yrs2wks(Ty )
121
- # Create time vector, with half-cycle addition
122
- tvec <- timestep * (0 : floor(Tw / timestep )) + timestep / 2
123
- # Obtain hazard and survival functions
124
- hppd <- tvec | > purrr :: map_dbl(~ calc_haz(.x , survobj = dpam $ ppd ))
125
- http <- tvec | > purrr :: map_dbl(~ calc_haz(.x , survobj = dpam $ ttp ))
126
- hpps <- tvec | > purrr :: map_dbl(~ calc_haz(.x , survobj = dpam $ pps_cf ))
127
- sppd <- tvec | > purrr :: map_dbl(~ calc_surv(.x , survobj = dpam $ ppd ))
128
- sttp <- tvec | > purrr :: map_dbl(~ calc_surv(.x , survobj = dpam $ ttp ))
129
- spps <- tvec | > purrr :: map_dbl(~ calc_surv(.x , survobj = dpam $ pps_cf ))
130
- # Derive the constrained S_PPD and S_PFS
131
- con.sppd <- constrain_survprob(sppd , lifetable = lifetable , timevec = tvec )
132
- con.spps <- constrain_survprob(spps , lifetable = lifetable , timevec = tvec )
133
- # Partial prob PD
134
- con.partprobpd <- sttp * con.sppd * http / con.spps
135
- con.partprobpd [con.spps == 0 ] <- 0
136
- con.probpd <- con.spps * cumsum(con.partprobpd )
137
- # Discount factor
138
- vn <- (1 + discrate )^ (- convert_wks2yrs(tvec + timestep / 2 ))
161
+ Tw <- ceiling(convert_yrs2wks(Ty )/ timestep )
162
+ # Create dataset, starting with time vector, with half-cycle addition, and unconstrained probs
163
+ ds <- tibble :: tibble(
164
+ tzero = timestep * (0 : Tw ),
165
+ tmid = .data $ tzero + timestep / 2 ,
166
+ u_pf = prob_pf_stm(.data $ tzero , dpam ),
167
+ u_pd = prob_pd_stm_cf(.data $ tzero , dpam ),
168
+ # Calculate PPD hazard and probability
169
+ 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
189
+ )
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
139
207
# Calculate RMDs
140
- pf <- sum(sttp * con.sppd * vn ) * timestep
141
- pd <- sum(con.probpd * vn ) * timestep
208
+ pf <- sum(ds $ rmd_pf )
209
+ pd <- sum(ds $ rmd_pd )
142
210
# Return values
143
- return (list (pf = pf , pd = pd , os = pf + pd ))
211
+ return (list (pf = pf , pd = pd , os = pf + pd , calc = ds ))
144
212
}
145
213
146
214
# ' Discretized Restricted Mean Duration calculation for State Transition Model Clock Reset structure
147
215
# ' Calculate restricted mean duration (RMD) in PF, PD and OS states under a State Transition Model Clock Reset structure.
148
216
# ' @inherit drmd_psm params return
217
+ # ' @importFrom rlang .data
149
218
# ' @seealso [drmd_stm_cf()] [drmd_psm()]
150
219
# ' @noRd
151
220
# Examples
@@ -170,42 +239,58 @@ drmd_stm_cr <- function(dpam, Ty=10, discrate=0, lifetable=NA, timestep=1) {
170
239
Tw <- tvec <- sppd <- sttp <- sos <- NULL
171
240
adjsppd <- adjos <- vn <- pf <- os <- NULL
172
241
# Time horizon in weeks (ceiling)
173
- Tw <- convert_yrs2wks(Ty )
174
- # Create time vector, with half-cycle addition
175
- tvec <- timestep * (0 : floor(Tw / timestep )) + timestep / 2
176
- # Obtain unconstrained survival functions
177
- sppd <- tvec | > purrr :: map_dbl(~ calc_surv(.x , survobj = dpam $ ppd ))
178
- sttp <- tvec | > purrr :: map_dbl(~ calc_surv(.x , survobj = dpam $ ttp ))
179
- # Derive the constrained S_PPD
180
- c.sppd <- constrain_survprob(sppd , lifetable = lifetable , timevec = tvec )
181
- # Integrand with constraints on S_PPD and S_PPS
182
- integrand <- function (u , t ) {
183
- i.http <- calc_haz(u , survobj = dpam $ ttp )
184
- i.sttp <- calc_surv(u , survobj = dpam $ ttp )
185
- i.u.sppd <- calc_surv(u , survobj = dpam $ ppd )
186
- i.u.spps <- calc_surv(t - u , survobj = dpam $ pps_cr )
187
- i.slxu <- calc_ltsurv(convert_wks2yrs(u ), lifetable = lifetable )
188
- i.slxt <- calc_ltsurv(convert_wks2yrs(t ), lifetable = lifetable )
189
- i.c.sppd <- pmin(i.u.sppd , i.slxu )
190
- i.c.spps <- pmin(i.u.spps , i.slxt / i.slxu )
191
- i.c.spps [i.slxu == 0 ] <- 0
192
- i.c.sppd * i.sttp * i.http * i.c.spps
242
+ Tw <- ceiling(convert_yrs2wks(Ty )/ timestep )
243
+ # Create dataset, starting with time vector, with half-cycle addition, and unconstrained probs
244
+ ds <- tibble :: tibble(
245
+ tzero = timestep * (0 : Tw ),
246
+ tmid = .data $ tzero + timestep / 2 ,
247
+ 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(
253
+ # Calculate PPD hazard and probability
254
+ 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
273
+ )
274
+ # Derive the constrained PF and PD memberships
275
+ for (t in 2 : (Tw )) {
276
+ ds $ c_pf [t ] = ds $ c_pf [t - 1 ] * (1 - ds $ cqpfs [t - 1 ])
277
+ ds $ c_pd [t ] = ds $ c_pf [t - 1 ] * ds $ q_ttp [t - 1 ] + ds $ c_pd [t - 1 ] * (1 - ds $ cqpps [t - 1 ])
193
278
}
194
- integrand <- Vectorize(integrand , " u" )
195
- # PD membership probability is the integral
196
- probpd <- function (t ) {
197
- stats :: integrate(integrand , lower = 0 , upper = t , t = t )$ value
198
- }
199
- probpd <- Vectorize(probpd , " t" )
200
- # Calculate the PD membership probability for each time
201
- c.probpd <- probpd(tvec )
202
- # Discount factor
203
- vn <- (1 + discrate )^ (- convert_wks2yrs(tvec + timestep / 2 ))
279
+ # The final membership probabilities are zero
280
+ ds $ c_pf [Tw + 1 ] <- ds $ c_pd [Tw + 1 ] <- 0
281
+ # Final calculations on the dataset
282
+ ds <- ds | >
283
+ dplyr :: mutate(
284
+ # Discount factor
285
+ vn = (1 + discrate )^ (- convert_wks2yrs(.data $ tmid )),
286
+ # RMD components in each timestep
287
+ rmd_pf = (.data $ c_pf + dplyr :: lead(.data $ c_pf ))/ 2 * .data $ vn * timestep ,
288
+ rmd_pd = (.data $ c_pd + dplyr :: lead(.data $ c_pd ))/ 2 * .data $ vn * timestep
289
+ )
290
+ ds $ rmd_pf [Tw + 1 ] <- ds $ rmd_pd [Tw + 1 ] <- 0
204
291
# Calculate RMDs
205
- pf <- sum(sttp * c.sppd * vn ) * timestep
206
- pd <- sum(c.probpd * vn ) * timestep
292
+ pf <- sum(ds $ rmd_pf )
293
+ pd <- sum(ds $ rmd_pd )
207
294
# Return values
208
- return (list (pf = pf , pd = pd , os = pf + pd ))
295
+ return (list (pf = pf , pd = pd , os = pf + pd , calc = ds ))
209
296
}
210
-
211
-
0 commit comments