Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Stein-Fojo Model #265

Merged
merged 32 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
aafc600
wip
gowerc Feb 9, 2024
eac24c9
temporary progress
gowerc Feb 12, 2024
a401a15
first draft
gowerc Feb 14, 2024
4c23dfc
add roxygen pages
gowerc Feb 14, 2024
dec8823
Added unit tests
gowerc Feb 16, 2024
8f03578
[skip actions] Roxygen Man Pages Auto Update
github-actions[bot] Feb 16, 2024
8d5a10e
Address CI/CD findings
gowerc Feb 19, 2024
c45bc49
merged in main
gowerc Feb 20, 2024
4969b01
Fixed S3 method consistency
gowerc Feb 20, 2024
50c4b5e
Delete License (again)
gowerc Feb 20, 2024
9482b1b
Add print methods
gowerc Feb 20, 2024
6a9d602
Added Vignette details
gowerc Feb 20, 2024
cc4b978
Fix spellings
gowerc Feb 20, 2024
337a6d7
update language code
gowerc Feb 20, 2024
f1377e2
Changed to British Spelling
gowerc Feb 20, 2024
6d5168d
Initial implementation
gowerc Feb 20, 2024
a04be9b
added loo examples
gowerc Feb 21, 2024
e367e6c
[skip actions] Roxygen Man Pages Auto Update
github-actions[bot] Feb 21, 2024
b241513
merged in main
gowerc Feb 22, 2024
3e736a3
[skip actions] Roxygen Man Pages Auto Update
github-actions[bot] Feb 22, 2024
b84b0b0
updated namespace
gowerc Feb 22, 2024
778f254
Added survival quantities examples
gowerc Feb 22, 2024
f6d32b2
Initial Progress
gowerc Feb 22, 2024
2298c96
Update Statistical Specification document
gowerc Feb 23, 2024
e95883a
Add Stein-Fojo Model
gowerc Feb 23, 2024
9bb663b
add "full tests" to vscode tasks
gowerc Feb 23, 2024
84d81b1
Fix spellings
gowerc Feb 23, 2024
cbf3941
Fix pkg down docs
gowerc Feb 23, 2024
3518352
Merge branch 'main' into gowerc/issue171
gowerc Feb 23, 2024
1578fc5
Fix unit test
gowerc Feb 23, 2024
113b1dc
add missing test markdown file
gowerc Feb 23, 2024
ac3fc67
Fixed examples
gowerc Feb 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Collate:
'LongitudinalGSF.R'
'LongitudinalQuantities.R'
'LongitudinalRandomSlope.R'
'LongitudinalSteinFojo.R'
'SurvivalExponential.R'
'SurvivalLoglogistic.R'
'SurvivalWeibullPH.R'
Expand All @@ -92,5 +93,6 @@ Collate:
'simulations_gsf.R'
'simulations_os.R'
'simulations_rs.R'
'simulations_sf.R'
'zzz.R'
VignetteBuilder: knitr
9 changes: 9 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,14 @@ S3method(compileStanModel,JointModel)
S3method(dim,Quantities)
S3method(enableLink,LongitudinalGSF)
S3method(enableLink,LongitudinalRandomSlope)
S3method(enableLink,LongitudinalSteinFojo)
S3method(extractVariableNames,DataSubject)
S3method(extractVariableNames,DataSurvival)
S3method(generateQuantities,JointModelSamples)
S3method(getParameters,Link)
S3method(getParameters,LinkComponent)
S3method(getParameters,StanModel)
S3method(getParameters,default)
S3method(initialValues,JointModel)
S3method(initialValues,Link)
S3method(initialValues,LinkComponent)
Expand All @@ -70,11 +73,14 @@ S3method(length,Link)
S3method(linkDSLD,LongitudinalGSF)
S3method(linkDSLD,LongitudinalModel)
S3method(linkDSLD,LongitudinalRandomSlope)
S3method(linkDSLD,LongitudinalSteinFojo)
S3method(linkIdentity,LongitudinalGSF)
S3method(linkIdentity,LongitudinalModel)
S3method(linkIdentity,LongitudinalRandomSlope)
S3method(linkIdentity,LongitudinalSteinFojo)
S3method(linkTTG,LongitudinalGSF)
S3method(linkTTG,LongitudinalModel)
S3method(linkTTG,LongitudinalSteinFojo)
S3method(names,Parameter)
S3method(names,ParameterList)
S3method(sampleStanModel,JointModel)
Expand All @@ -97,6 +103,7 @@ export(LongitudinalGSF)
export(LongitudinalModel)
export(LongitudinalQuantities)
export(LongitudinalRandomSlope)
export(LongitudinalSteinFojo)
export(Parameter)
export(ParameterList)
export(Prior)
Expand Down Expand Up @@ -142,6 +149,7 @@ export(sampleStanModel)
export(show)
export(sim_lm_gsf)
export(sim_lm_random_slope)
export(sim_lm_sf)
export(sim_os_exponential)
export(sim_os_loglogistic)
export(sim_os_weibull)
Expand All @@ -157,6 +165,7 @@ exportClasses(Link)
exportClasses(LongitudinalGSF)
exportClasses(LongitudinalModel)
exportClasses(LongitudinalRandomSlope)
exportClasses(LongitudinalSteinFojo)
exportClasses(Parameter)
exportClasses(ParameterList)
exportClasses(Prior)
Expand Down
136 changes: 136 additions & 0 deletions R/LongitudinalSteinFojo.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#' @include LongitudinalModel.R
#' @include StanModule.R
#' @include generics.R
#' @include ParameterList.R
#' @include Parameter.R
#' @include Link.R
NULL


#' `LongitudinalSteinFojo`
#'
#' This class extends the general [`LongitudinalModel`] class for using the
#' Stein-Fojo model for the longitudinal outcome.
#'
#' @exportClass LongitudinalSteinFojo
.LongitudinalSteinFojo <- setClass(
Class = "LongitudinalSteinFojo",
contains = "LongitudinalModel"
)


#' @rdname LongitudinalSteinFojo-class
#'
#' @param mu_bsld (`Prior`)\cr for the mean baseline value `mu_bsld`.
#' @param mu_ks (`Prior`)\cr for the mean shrinkage rate `mu_ks`.
#' @param mu_kg (`Prior`)\cr for the mean growth rate `mu_kg`.
#'
#' @param omega_bsld (`Prior`)\cr for the baseline value standard deviation `omega_bsld`.
#' @param omega_ks (`Prior`)\cr for the shrinkage rate standard deviation `omega_ks`.
#' @param omega_kg (`Prior`)\cr for the growth rate standard deviation `omega_kg`.
#'
#' @param sigma (`Prior`)\cr for the variance of the longitudinal values `sigma`.
#'
#' @param centred (`logical`)\cr whether to use the centred parameterization.
#'
#' @export
LongitudinalSteinFojo <- function(

mu_bsld = prior_normal(log(60), 1),
mu_ks = prior_normal(log(0.5), 1),
mu_kg = prior_normal(log(0.3), 1),

omega_bsld = prior_lognormal(log(0.2), 1),
omega_ks = prior_lognormal(log(0.2), 1),
omega_kg = prior_lognormal(log(0.2), 1),

sigma = prior_lognormal(log(0.1), 1),

centred = FALSE
) {

sf_model <- StanModule(decorated_render(
.x = paste0(read_stan("lm-stein-fojo/model.stan"), collapse = "\n"),
centred = centred
))

parameters <- list(
Parameter(name = "lm_sf_mu_bsld", prior = mu_bsld, size = "n_studies"),
Parameter(name = "lm_sf_mu_ks", prior = mu_ks, size = "n_arms"),
Parameter(name = "lm_sf_mu_kg", prior = mu_kg, size = "n_arms"),

Parameter(name = "lm_sf_omega_bsld", prior = omega_bsld, size = 1),
Parameter(name = "lm_sf_omega_ks", prior = omega_ks, size = 1),
Parameter(name = "lm_sf_omega_kg", prior = omega_kg, size = 1),

Parameter(name = "lm_sf_sigma", prior = sigma, size = 1)
)

assert_flag(centred)
parameters_extra <- if (centred) {
list(
Parameter(
name = "lm_sf_psi_bsld",
prior = prior_init_only(prior_lognormal(mu_bsld@init, omega_bsld@init)),
size = "Nind"
),
Parameter(
name = "lm_sf_psi_ks",
prior = prior_init_only(prior_lognormal(mu_ks@init, omega_ks@init)),
size = "Nind"
),
Parameter(
name = "lm_sf_psi_kg",
prior = prior_init_only(prior_lognormal(mu_kg@init, omega_kg@init)),
size = "Nind"
)
)
} else {
list(
Parameter(name = "lm_sf_eta_tilde_bsld", prior = prior_std_normal(), size = "Nind"),
Parameter(name = "lm_sf_eta_tilde_ks", prior = prior_std_normal(), size = "Nind"),
Parameter(name = "lm_sf_eta_tilde_kg", prior = prior_std_normal(), size = "Nind")
)
}
parameters <- append(parameters, parameters_extra)

x <- LongitudinalModel(
name = "Stein-Fojo",
stan = merge(
sf_model,
StanModule("lm-stein-fojo/functions.stan")
),
parameters = do.call(ParameterList, parameters)
)
.LongitudinalSteinFojo(x)
}



#' @rdname standard-link-methods
#' @export
enableLink.LongitudinalSteinFojo <- function(object, ...) {
object@stan <- merge(
object@stan,
StanModule("lm-stein-fojo/link.stan")
)
object
}

#' @rdname standard-link-methods
#' @export
linkDSLD.LongitudinalSteinFojo <- function(object, ...) {
StanModule("lm-stein-fojo/link_dsld.stan")
}

#' @rdname standard-link-methods
#' @export
linkTTG.LongitudinalSteinFojo <- function(object, ...) {
StanModule("lm-stein-fojo/link_ttg.stan")
}

#' @rdname standard-link-methods
#' @export
linkIdentity.LongitudinalSteinFojo <- function(object, ...) {
StanModule("lm-stein-fojo/link_identity.stan")
}
1 change: 1 addition & 0 deletions R/StanModel.R
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ as.list.StanModel <- function(x, ...) {
# getParameters-StanModel ----

#' @rdname getParameters
#' @export
getParameters.StanModel <- function(object) object@parameters


Expand Down
1 change: 1 addition & 0 deletions R/defaults.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ NULL


#' @rdname getParameters
#' @export
getParameters.default <- function(object) {
if (missing(object) || is.null(object)) {
return(NULL)
Expand Down
6 changes: 6 additions & 0 deletions R/simulations.R
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,18 @@ simulate_joint_data <- function(
msg = "The longitudinal dataset must be sorted by pt, time"
)


os_dat_chaz <- time_dat_baseline |>
dplyr::mutate(log_haz_link = lm_dat$log_haz_link) |> # only works if lm_dat is sorted pt, time
dplyr::mutate(log_bl_haz = os_fun(.data$evalp)) |>
# Fix to avoid issue with log(0) = NaN values
dplyr::mutate(log_bl_haz = dplyr::if_else(.data$evalp == 0, -999, .data$log_bl_haz)) |>
dplyr::mutate(hazard_instant = exp(.data$log_bl_haz + .data$log_haz_cov + .data$log_haz_link)) |>
# Reset Inf values to large number to avoid NaN issues downstream
# This is suitable as Hazard limits tend to be in the range of -10 to 10 so large numbers
# are essentially equivalent to infinity for simulation purposes
dplyr::mutate(hazard_instant = dplyr::if_else(.data$hazard_instant == Inf, 999, .data$hazard_instant)) |>
dplyr::mutate(hazard_instant = dplyr::if_else(.data$hazard_instant == -Inf, -999, .data$hazard_instant)) |>
dplyr::mutate(hazard_interval = .data$hazard_instant * .data$width) |>
dplyr::group_by(.data$pt) |>
dplyr::mutate(chazard = cumsum(.data$hazard_interval)) |>
Expand Down
106 changes: 106 additions & 0 deletions R/simulations_sf.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@



#' Stein-Fojo Functionals
#'
#' @param time (`numeric`)\cr time grid.
#' @param b (`number`)\cr baseline.
#' @param s (`number`)\cr shrinkage.
#' @param g (`number`)\cr growth.
#'
#' @returns The function results.
#' @keywords internal
#'
#' @examples
#' sf_sld(1:10, 20, 0.3, 0.6)
sf_sld <- function(time, b, s, g) {
b * (exp(-s * time) + exp(g * time) - 1)
}


#' @rdname sf_sld
#' @examples
#' sf_ttg(1:10, 20, 0.3, 0.6)
sf_ttg <- function(time, b, s, g) {
t1 <- (log(s) - log(g)) / (g + s)
t1[t1 <= 0] <- 0
return(t1)
}


#' @rdname sf_sld
#' @examples
#' sf_dsld(1:10, 20, 0.3, 0.6)
sf_dsld <- function(time, b, s, g) {
t1 <- g * exp(g * time)
t2 <- s * exp(-s * time)
return(b * (t1 - t2))
}



#' Construct a Simulation Function for Longitudinal Data from Stein-Fojo Model
#'
#' @param sigma (`number`)\cr the variance of the longitudinal values.
#' @param mu_s (`numeric`)\cr the mean shrinkage rates for the two treatment arms.
#' @param mu_g (`numeric`)\cr the mean growth rates for the two treatment arms.
#' @param mu_b (`numeric`)\cr the mean baseline values for the two treatment arms.
#' @param omega_b (`number`)\cr the baseline value standard deviation.
#' @param omega_s (`number`)\cr the shrinkage rate standard deviation.
#' @param omega_g (`number`)\cr the growth rate standard deviation.
#' @param link_dsld (`number`)\cr the link coefficient for the derivative contribution.
#' @param link_ttg (`number`)\cr the link coefficient for the time-to-growth contribution.
#' @param link_identity (`number`)\cr the link coefficient for the SLD Identity contribution.
#'
#' @returns A function with argument `lm_base` that can be used to simulate
#' longitudinal data from the corresponding Stein-Fojo model.
#'
#' @export
sim_lm_sf <- function(
sigma = 0.01,
mu_s = c(0.6, 0.4),
mu_g = c(0.25, 0.35),
mu_b = 60,
omega_b = 0.2,
omega_s = 0.2,
omega_g = 0.2,
link_dsld = 0,
link_ttg = 0,
link_identity = 0
) {
function(lm_base) {

assert_that(
length(unique(lm_base$study)) == length(mu_b),
length(mu_b) == 1,
length(sigma) == 1,
length(mu_s) == length(unique(lm_base$arm)),
length(mu_s) == length(mu_g),
length(c(omega_b, omega_s, omega_g)) == 3
)

baseline_covs <- lm_base |>
dplyr::distinct(.data$pt, .data$arm, .data$study) |>
dplyr::mutate(study_idx = as.numeric(factor(as.character(.data$study)))) |>
dplyr::mutate(arm_idx = as.numeric(factor(as.character(.data$arm)))) |>
dplyr::mutate(psi_b = stats::rlnorm(dplyr::n(), log(mu_b[.data$study_idx]), omega_b)) |>
dplyr::mutate(psi_s = stats::rlnorm(dplyr::n(), log(mu_s[.data$arm_idx]), omega_s)) |>
dplyr::mutate(psi_g = stats::rlnorm(dplyr::n(), log(mu_g[.data$arm_idx]), omega_g))


lm_dat <- lm_base |>
dplyr::select(!dplyr::all_of(c("study", "arm"))) |>
dplyr::left_join(baseline_covs, by = "pt") |>
dplyr::mutate(mu_sld = sf_sld(.data$time, .data$psi_b, .data$psi_s, .data$psi_g)) |>
dplyr::mutate(dsld = sf_dsld(.data$time, .data$psi_b, .data$psi_s, .data$psi_g)) |>
dplyr::mutate(ttg = sf_ttg(.data$time, .data$psi_b, .data$psi_s, .data$psi_g)) |>
dplyr::mutate(sld = stats::rnorm(dplyr::n(), .data$mu_sld, .data$mu_sld * sigma)) |>
dplyr::mutate(
log_haz_link =
(link_dsld * .data$dsld) +
(link_ttg * .data$ttg) +
(link_identity * .data$mu_sld)
)
return(lm_dat)
}
}
Loading
Loading