-
-
Notifications
You must be signed in to change notification settings - Fork 176
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
Mixture cure models (Weibull and log-normal) #1453
base: master
Are you sure you want to change the base?
Changes from 21 commits
f3337e3
77753bf
78e8ed7
174fda4
82eb565
981ccc5
122ddd7
e8c4a99
f903828
5fec70e
aa1701c
25dce93
9143e88
110beef
ba43f65
ffa6807
1803a24
b8bcaa0
977d30d
9f9870c
6c45b0f
e84df01
068263a
986d5d0
4bd37c2
9dfa184
3eedf4e
cf8ceef
a749f4a
0907128
f992dc6
9bfb9ce
008d921
f29c11f
7b8d234
32aa4b4
309392a
bbe730b
0a09dc8
5f54717
cb37642
93905fa
36ce202
c7e128f
de8bcba
d744e02
cd317a1
5196e48
423edd8
7286a86
eabb8a9
0be151c
4a8e5bc
8dbaa32
ecbc9de
a6fa42c
9e692c4
3e0123f
7ab3f68
5541e7e
3f54932
9c8e44b
8e83ec4
e62c387
ca9673b
d4e50eb
67b4aaa
9073341
bf1b448
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2052,6 +2052,107 @@ phurdle_lognormal <- function(q, mu, sigma, hu, lower.tail = TRUE, | |
out | ||
} | ||
|
||
#' @rdname Mixcure | ||
#' @export | ||
dmixcure_lognormal <- function(x, mu, sigma, inc, log = FALSE) { | ||
pars <- list(meanlog = mu, sdlog = sigma) | ||
.dmixcure(x, "lnorm", inc, pars, log) | ||
} | ||
|
||
#' @rdname Mixcure | ||
#' @export | ||
pmixcure_lognormal <- function(q, mu, sigma, inc, lower.tail = TRUE, log.p = FALSE) { | ||
pars <- list(meanlog = mu, sdlog = sigma) | ||
.pmixcure(q, "lnorm", inc, pars, lower.tail, log.p) | ||
} | ||
|
||
#' @rdname Mixcure | ||
#' @export | ||
dmixcure_weibull <- function(x, shape, scale, inc, log = FALSE) { | ||
pars <- list(shape = shape, scale = scale) | ||
.dmixcure(x, "weibull", inc, pars, log) | ||
} | ||
|
||
#' @rdname Mixcure | ||
#' @export | ||
pmixcure_weibull <- function(q, shape, scale, inc, lower.tail = TRUE, log.p = FALSE) { | ||
pars <- list(shape = shape, scale = scale) | ||
.pmixcure(q, "weibull", inc, pars, lower.tail, log.p) | ||
} | ||
|
||
# density of a mixcure distribution | ||
# @param dist name of the distribution | ||
# @param inc bernoulli incidence parameter | ||
# @param pars list of parameters passed to pdf | ||
.dmixcure <- function(x, dist, inc, pars, log) { | ||
stopifnot(is.list(pars)) | ||
dist <- as_one_character(dist) | ||
log <- as_one_logical(log) | ||
args <- expand(dots = c(nlist(x, inc), pars)) | ||
x <- args$x | ||
inc <- args$inc | ||
pars <- args[names(pars)] | ||
pdf <- paste0("d", dist) | ||
# incidence part (not censored): pi(z) * f(t | x) | ||
out <- log(inc) + do_call(pdf, c(list(x), pars, log = TRUE)) | ||
if (!log) { | ||
out <- exp(out) | ||
} | ||
out | ||
} | ||
|
||
# CDF of a mixcure distribution | ||
# @param dist name of the distribution | ||
# @param inc bernoulli incidence parameter | ||
# @param pars list of parameters passed to pdf | ||
# @param lb lower bound of the conditional distribution | ||
# @param ub upper bound of the conditional distribution | ||
.pmixcure <- function(q, dist, inc, pars, lower.tail, log.p, lb = 0, ub = Inf) { | ||
stopifnot(is.list(pars)) | ||
dist <- as_one_character(dist) | ||
lower.tail <- as_one_logical(lower.tail) | ||
log.p <- as_one_logical(log.p) | ||
args <- expand(dots = c(nlist(q, inc), pars)) | ||
q <- args$q | ||
inc <- args$inc | ||
pars <- args[names(pars)] | ||
cdf <- paste0("p", dist) | ||
# compute log CCDF values | ||
# latency part (right-censored): [1 - pi(z)] + pi(z) * S(t | x) | ||
out <- matrixStats::logSumExp(c( | ||
1, -inc, | ||
inc + do_call( | ||
cdf, | ||
c(list(q), pars, lower.tail = FALSE, log.p = TRUE) | ||
) | ||
)) | ||
# take the limits of the distribution into account | ||
out <- ifelse(q < lb, 0, out) | ||
out <- ifelse(q > ub, -Inf, out) | ||
if (lower.tail) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. some strange indentation going on here. Please fix. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The convention is to use 2 spaces for indentation, correct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes but what about the if statements afterwards? |
||
out <- 1 - exp(out) | ||
if (log.p) { | ||
out <- log(out) | ||
} | ||
} else { | ||
if (!log.p) { | ||
out <- exp(out) | ||
} | ||
} | ||
out | ||
if (lower.tail) { | ||
out <- 1 - exp(out) | ||
if (log.p) { | ||
out <- log(out) | ||
} | ||
} else { | ||
if (!log.p) { | ||
out <- exp(out) | ||
} | ||
} | ||
out | ||
} | ||
|
||
# density of the categorical distribution with the softmax transform | ||
# @param x positive integers not greater than ncat | ||
# @param eta the linear predictor (of length or ncol ncat) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -485,6 +485,16 @@ posterior_epred_hurdle_lognormal <- function(prep) { | |
with(prep$dpars, exp(mu + sigma^2 / 2) * (1 - hu)) | ||
} | ||
|
||
posterior_epred_mixcure_lognormal <- function(prep) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it not possible to compute the expected value for these families? |
||
stop2("Cannot compute expected values of the posterior predictive ", | ||
"distribution for family 'micure_lognormal'.") | ||
} | ||
|
||
posterior_epred_mixcure_weibull <- function(prep) { | ||
stop2("Cannot compute expected values of the posterior predictive ", | ||
"distribution for family 'micure_weibull'.") | ||
} | ||
|
||
posterior_epred_hurdle_cumulative <- function(prep) { | ||
adjust <- ifelse(prep$family$link == "identity", 0, 1) | ||
ncat_max <- max(prep$data$nthres) + adjust | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -283,9 +283,9 @@ stan_log_lik_simple_lpdf <- function(lpdf, link, bterms, sep = "_") { | |
} | ||
|
||
# prepare _logit suffix for distributional parameters | ||
# used in zero-inflated and hurdle models | ||
# used in zero-inflated, hurdle and mixcure models | ||
stan_log_lik_dpar_usc_logit <- function(dpar, bterms) { | ||
stopifnot(dpar %in% c("zi", "hu")) | ||
stopifnot(dpar %in% c("zi", "hu", "inc")) | ||
stopifnot(is.brmsterms(bterms)) | ||
cens_or_trunc <- stan_log_lik_adj(bterms, c("cens", "trunc")) | ||
usc_logit <- isTRUE(bterms$dpars[[dpar]]$family$link == "logit") | ||
|
@@ -889,6 +889,25 @@ stan_log_lik_hurdle_cumulative <- function(bterms, resp = "", mix = "", | |
sdist(lpdf, p$mu, p$hu, p$disc, p$thres, p$Jthres) | ||
} | ||
|
||
stan_log_lik_mixcure_lognormal <- function(bterms, resp = "", mix = "", ...) { | ||
p <- stan_log_lik_dpars(bterms, TRUE, resp, mix) | ||
usc_logit <- stan_log_lik_dpar_usc_logit("inc", bterms) | ||
lpdf <- paste0("mixcure_lognormal", usc_logit) | ||
sdist(lpdf, p$mu, p$sigma, p$inc) | ||
} | ||
|
||
stan_log_lik_mixcure_weibull <- function(bterms, resp = "", mix = "", ...) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like |
||
reqn <- stan_log_lik_adj(bterms) || nzchar(mix) | ||
p <- stan_log_lik_dpars(bterms, TRUE, resp, mix) | ||
usc_logit <- stan_log_lik_dpar_usc_logit("inc", bterms) | ||
lpdf <- paste0("mixcure_weibull", usc_logit) | ||
# Stan uses shape-scale parameterization for weibull | ||
need_dot_div <- !reqn && paste0("shape", mix) %in% names(bterms$dpars) | ||
div_op <- str_if(need_dot_div, " ./ ", " / ") | ||
p$scale <- paste0(p$mu, div_op, "tgamma(1 + 1", div_op, p$shape, ")") | ||
sdist(lpdf, p$scale, p$shape, p$inc) | ||
} | ||
|
||
stan_log_lik_zero_inflated_poisson <- function(bterms, resp = "", mix = "", | ||
...) { | ||
p <- stan_log_lik_dpars(bterms, TRUE, resp, mix) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* mixcure lognormal (AFT) log-PDF of a single response | ||
* identity parameterization of the incidence part | ||
*/ | ||
real mixcure_lognormal_lpdf(real y, real mu, real sigma, real inc) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add some empty lines between function definitions in both stan files? And add a little more doc at the top? |
||
return bernoulli_lpmf(1 | inc) + lognormal_lpdf(y | mu, sigma); | ||
} | ||
real mixcure_lognormal_lccdf(real y, real mu, real sigma, real inc) { | ||
return log_sum_exp( | ||
bernoulli_lpmf(0 | inc), | ||
bernoulli_lpmf(1 | inc) + lognormal_lccdf(y | mu, sigma) | ||
); | ||
} | ||
real mixcure_lognormal_lcdf(real y, real mu, real sigma, real inc) { | ||
return log1m_exp(mixcure_lognormal_lccdf(y | mu, sigma, inc)); | ||
} | ||
/* mixcure lognormal (AFT) log-PDF of a single response | ||
* logit parameterization of the incidence part | ||
*/ | ||
real mixcure_lognormal_logit_lpdf(real y, real mu, real sigma, real inc) { | ||
return bernoulli_logit_lpmf(1 | inc) + lognormal_lpdf(y | mu, sigma); | ||
} | ||
real mixcure_lognormal_logit_lccdf(real y, real mu, real sigma, real inc) { | ||
return log_sum_exp( | ||
bernoulli_logit_lpmf(0 | inc), | ||
bernoulli_logit_lpmf(1 | inc) + lognormal_lccdf(y | mu, sigma) | ||
); | ||
} | ||
real mixcure_lognormal_logit_lcdf(real y, real mu, real sigma, real inc) { | ||
return log1m_exp(mixcure_lognormal_logit_lccdf(y | mu, sigma, inc)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like an unusal gitignore for an R package. Do we really need it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry about that, I use jupyter lab for editing so I added that line to avoid adding the checkpoints files. Will fix this.