Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into 402-individual-loo
Browse files Browse the repository at this point in the history
  • Loading branch information
gowerc committed Aug 20, 2024
2 parents 8bba385 + 7f3312d commit 1df860b
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 10 deletions.
6 changes: 5 additions & 1 deletion R/LongitudinalSteinFojo.R
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ NULL
#'
#' @param sigma (`Prior`)\cr for the variance of the longitudinal values `sigma`.
#'
#' @param scaled_variance (`logical`)\cr whether the variance should be scaled by the expected value
#' (see the "Statistical Specifications" vignette for more details)
#' @param centred (`logical`)\cr whether to use the centred parameterization.
#'
#' @export
Expand All @@ -51,12 +53,14 @@ LongitudinalSteinFojo <- function(

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

scaled_variance = TRUE,
centred = FALSE
) {

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

# Apply constriants
Expand Down
15 changes: 11 additions & 4 deletions R/SimLongitudinalSteinFojo.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ NULL
#' @param link_identity (`number`)\cr the link coefficient for the SLD Identity contribution.
#' @param link_growth (`number`)\cr the link coefficient for the log-growth parameter contribution.
#' @param link_shrinkage (`number`)\cr the link coefficient for the log-shrinkage parameter contribution.
#' @param scaled_variance (`logical`)\cr whether the variance should be scaled by the expected value
#' (see the "Statistical Specifications" vignette for more details)
#'
#' @slot sigma (`numeric`)\cr See arguments.
#' @slot mu_s (`numeric`)\cr See arguments.
Expand All @@ -31,6 +33,7 @@ NULL
#' @slot link_identity (`numeric`)\cr See arguments.
#' @slot link_growth (`numeric`)\cr See arguments.
#' @slot link_shrinkage (`numeric`)\cr See arguments.
#' @slot scaled_variance (`logical`)\cr See arguments.
#'
#' @family SimLongitudinal
#' @name SimLongitudinalSteinFojo-class
Expand All @@ -50,7 +53,8 @@ NULL
link_ttg = "numeric",
link_identity = "numeric",
link_growth = "numeric",
link_shrinkage = "numeric"
link_shrinkage = "numeric",
scaled_variance = "logical"
)
)

Expand All @@ -69,7 +73,8 @@ SimLongitudinalSteinFojo <- function(
link_ttg = 0,
link_identity = 0,
link_growth = 0,
link_shrinkage = 0
link_shrinkage = 0,
scaled_variance = TRUE
) {

if (length(omega_b) == 1) omega_b <- rep(omega_b, length(mu_b))
Expand All @@ -89,7 +94,8 @@ SimLongitudinalSteinFojo <- function(
link_ttg = link_ttg,
link_identity = link_identity,
link_growth = link_growth,
link_shrinkage = link_shrinkage
link_shrinkage = link_shrinkage,
scaled_variance = scaled_variance
)
}

Expand Down Expand Up @@ -144,7 +150,8 @@ sampleObservations.SimLongitudinalSteinFojo <- function(object, times_df) {
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 * object@sigma)) |>
dplyr::mutate(sld_sd = ifelse(object@scaled_variance, .data$mu_sld * object@sigma, object@sigma)) |>
dplyr::mutate(sld = stats::rnorm(dplyr::n(), .data$mu_sld, .data$sld_sd)) |>
dplyr::mutate(
log_haz_link =
(object@link_dsld * .data$dsld) +
Expand Down
12 changes: 10 additions & 2 deletions inst/stan/lm-stein-fojo/model.stan
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,21 @@ transformed parameters{
long_obvs_log_lik[subject_tumour_index_obs] = vect_normal_log_dens(
tumour_value[subject_tumour_index_obs],
Ypred[subject_tumour_index_obs],
Ypred[subject_tumour_index_obs] * lm_sf_sigma
{%- if scaled_variance -%}
Ypred[subject_tumour_index_obs] * lm_sf_sigma
{% else %}
rep_vector(lm_sf_sigma, n_tumour_obs)
{%- endif -%}
);
if (n_tumour_cens > 0 ) {
long_obvs_log_lik[subject_tumour_index_cens] = vect_normal_log_cum(
tumour_value_lloq,
Ypred[subject_tumour_index_cens],
Ypred[subject_tumour_index_cens] * lm_sf_sigma
{%- if scaled_variance -%}
Ypred[subject_tumour_index_cens] * lm_sf_sigma
{% else %}
rep_vector(lm_sf_sigma, n_tumour_cens)
{%- endif -%}
);
}
}
Expand Down
4 changes: 4 additions & 0 deletions man/LongitudinalSteinFojo-class.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion man/SimLongitudinalSteinFojo-class.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

173 changes: 173 additions & 0 deletions tests/testthat/test-LongitudinalSteinFojo.R
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ test_that("Can recover known distributional parameters from a SF joint model", {
expect_true(all(dat$ess_bulk > 100))
})






test_that("Can recover known distributional parameters from a SF joint model with growth link", {

skip_if_not(is_full_test())
Expand Down Expand Up @@ -431,6 +436,9 @@ test_that("Can recover known distributional parameters from a SF joint model wit






test_that("Quantity models pass the parser", {
mock_samples <- .JointModelSamples(
model = JointModel(longitudinal = LongitudinalSteinFojo(centred = TRUE)),
Expand All @@ -454,6 +462,11 @@ test_that("Quantity models pass the parser", {








test_that("Can generate valid initial values", {

pars <- c(
Expand Down Expand Up @@ -500,3 +513,163 @@ test_that("Can generate valid initial values", {
expect_true(all(vals > 0))

})






test_that("Unscaled variance SF mode pass the parser", {
jm <- JointModel(
longitudinal = LongitudinalSteinFojo(centred = FALSE, scaled_variance = FALSE),
survival = SurvivalLogLogistic(),
link = linkDSLD()
)
x <- as.StanModule(jm)
expect_stan_syntax(x)
})






test_that("Can recover known distributional parameters from unscaled variance SF model", {

skip_if_not(is_full_test())

sim_params <- list(
sigma = 0.4,
mu_s = log(c(0.15, 0.3)),
mu_g = log(c(0.4, 0.25)),
mu_b = log(60),
omega_b = 0.1,
omega_s = c(0.1, 0.1),
omega_g = c(0.2, 0.2),
link_ttg = 0,
link_dsld = 0,
link_growth = 0,
lambda = 2,
lambda_cen = 1 / 9000,
beta_cat_b = -0.1,
beta_cat_c = 0.5,
beta_cont = 0.3
)

set.seed(2338)
## Generate Test data with known parameters
jlist <- SimJointData(
design = list(
SimGroup(200, "Arm-A", "Study-X"),
SimGroup(200, "Arm-B", "Study-X")
),
longitudinal = SimLongitudinalSteinFojo(
times = c(1, 50, 100, 150, 200, 300, 400, 500, 600, 700) / 365,
sigma = sim_params$sigma,
mu_s = sim_params$mu_s,
mu_g = sim_params$mu_g,
mu_b = sim_params$mu_b,
omega_b = sim_params$omega_b,
omega_s = sim_params$omega_s,
omega_g = sim_params$omega_g,
link_ttg = sim_params$link_ttg,
link_dsld = sim_params$link_dsld,
link_growth = sim_params$link_growth,
scaled_variance = FALSE
),
survival = SimSurvivalExponential(
time_max = 4,
time_step = 1 / 365,
lambda = sim_params$lambda,
lambda_cen = 1 / 9000,
beta_cat = c(
"A" = 0,
"B" = sim_params$beta_cat_b,
"C" = sim_params$beta_cat_c
),
beta_cont = sim_params$beta_cont
),
.silent = TRUE
)

# nolint start⁠
### Diagnostics helpers
# plot(survival::survfit(Surv(time, event) ~ 1, data = jlist@survival))
# median(jlist@survival$time)
# nolint end


jm <- JointModel(
longitudinal = LongitudinalSteinFojo(
mu_bsld = prior_normal(log(60), 0.5),
mu_ks = prior_normal(log(0.2), 0.5),
mu_kg = prior_normal(log(0.3), 0.5),
omega_bsld = prior_lognormal(log(0.1), 0.5),
omega_ks = prior_lognormal(log(0.1), 0.5),
omega_kg = prior_lognormal(log(0.1), 0.5),
sigma = prior_lognormal(log(0.5), 0.5),
centred = TRUE,
scaled_variance = FALSE
)
)

jdat <- DataJoint(
subject = DataSubject(
data = jlist@survival,
subject = "subject",
arm = "arm",
study = "study"
),
longitudinal = DataLongitudinal(
data = jlist@longitudinal,
formula = sld ~ time,
threshold = 5
)
)


set.seed(2213)
mp <- run_quietly({
sampleStanModel(
jm,
data = jdat,
iter_warmup = 750,
iter_sampling = 750,
chains = 2,
parallel_chains = 2
)
})

summary_post <- function(model, vars, exp = FALSE) {
dat <- model$summary(
vars,
mean = mean,
q01 = \(x) purrr::set_names(quantile(x, 0.01), ""),
q99 = \(x) purrr::set_names(quantile(x, 0.99), ""),
rhat = posterior::rhat,
ess_bulk = posterior::ess_bulk,
ess_tail = posterior::ess_tail
)
if (exp) {
dat$q01 <- dat$q01 |> exp()
dat$q99 <- dat$q99 |> exp()
dat$mean <- dat$mean |> exp()
}
dat
}

dat <- summary_post(
as.CmdStanMCMC(mp),
c(
"lm_sf_mu_bsld", "lm_sf_mu_ks", "lm_sf_mu_kg",
"lm_sf_sigma", "lm_sf_omega_bsld", "lm_sf_omega_kg", "lm_sf_omega_ks"
)
)
true_values <- c(
sim_params$mu_b, sim_params$mu_s, sim_params$mu_g,
sim_params$sigma, sim_params$omega_b, sim_params$omega_g, sim_params$omega_s
)
expect_true(all(dat$q01 <= true_values))
expect_true(all(dat$q99 >= true_values))
expect_true(all(dat$ess_bulk > 100))
})
2 changes: 1 addition & 1 deletion tests/testthat/test-SimLongitudinalSteinFojo.R
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ test_that("SimLongitudinalSteinFojo works as expected", {
names(res_obvs),
c(
"subject", "arm", "study", "psi_b", "psi_s", "psi_g", "time",
"mu_sld", "dsld", "ttg", "sld", "log_haz_link"
"mu_sld", "dsld", "ttg", "sld_sd", "sld", "log_haz_link"
)
)
})
Expand Down
5 changes: 4 additions & 1 deletion vignettes/statistical-specification.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ Where:

* $\eta_{\theta i}$ is a random effects offset on parameter $\theta$ for subject $i$


If using the unscaled variance parameterisation then the following alternative formulation is used:
$$
y_{ij} &\sim N(SLD_{ij},\ \sigma^2)
$$

### Derivative of the SLD Trajectory Link

Expand Down

0 comments on commit 1df860b

Please sign in to comment.