Git Product home page Git Product logo

oncomsm's Introduction

Linux Windows metacran version metacran version Lifecycle:Experimental

Bayesian multi-state models for early oncology

The R package oncomsm implements methods to dynamically predict response and progression of individuals in early oncology trials using parametric multi-state models and Bayesian inference. This allows the dynamic computation of "probability of success" for a wide range of success (or "go") criteria. For instance, the bhmbasket R package can be used to define study success based on Bayesian hierarchical models.

Installation

The development version can be installed from this repository.

install.packages("oncomsm")

The development version can be installed from this repository.

# install.packages("remotes")
remotes::install_github("https://github.com/Boehringer-Ingelheim/oncomsm")

Documentation

The package documentation is hosted here.

Contributing

See the contributing guidelines.

oncomsm's People

Contributors

akktk avatar andrjohns avatar chengxuezhong avatar kkmann avatar lucas234567 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

andrjohns

oncomsm's Issues

Improve performance of forward sampling

The forward sampling is still implemented in R quite naively. We do not want to implement it in stan to keep inference and predictive sampling separate but this should really be implemented in C++ to speed things up a bit.

oncomsm/R/srp_model.R

Lines 65 to 187 in 59dbee9

.impute.srp_model <- function(model, data, nsim, parameter_sample, seed = NULL, ...) {
if (!is.null(seed)) {
set.seed(seed)
}
# TODO: convert data to matrix and process in c++
stopifnot(isa(parameter_sample, "stanfit"))
# extract subject and group id levels for conversion to and back from integer
subject_id_levels <- unique(as.character(data$subject_id))
group_id_levels <- attr(model, "group_id") # important to maintain ordering
# extract parameter matrices
p <- rstan::extract(parameter_sample, "p")[[1]]
scale <- rstan::extract(parameter_sample, "scale")[[1]]
shape <- rstan::extract(parameter_sample, "shape")[[1]]
data <- data %>%
arrange(.data$t_sot, .data$subject_id, (.data$t_min + .data$t_max)/2) %>%
mutate(
subject_id = as.integer(factor(as.character(.data$subject_id), levels = subject_id_levels)),
group_id = as.integer(factor(.data$group_id, levels = group_id_levels))
)
res <- tribble(
~subject_id, ~group_id, ~from, ~to, ~t_min, ~t_max, ~t_sot, ~iter
)
visit_spacing <- attr(model, "visit_spacing")
idx <- sample(1:dim(p)[1], size = nsim, replace = TRUE)
for (i in 1:nrow(data)) {
if (!is.na(data$to[i])) { # observed transition, nothing to sample
res <- bind_rows(res, tidyr::expand_grid(data[i, ], iter = 1:nsim))
} else { # a censored transition
for (j in 1:nsim) {
g <- data$group_id[i]
k <- idx[j]
sshape <- shape[k, g, ]
sscale <- scale[k, g, ]
if (data$from[i] == "stable") {
# first sample response/progression
pr_response_raw <- p[k, g] # use survival information!
pr_survival_response <- 1 - stats::pweibull(data$t_min[i] - data$t_sot[i], sshape[1], sscale[1])
pr_survival_progression <- 1 - stats::pweibull(data$t_min[i] - data$t_sot[i], sshape[2], sscale[2])
pr_response <- pr_response_raw * pr_survival_response / (
pr_response_raw * pr_survival_response +
(1 - pr_response_raw) * pr_survival_progression
)
response <- stats::rbinom(1, 1, pr_response)
if (response) {
# sample exact response time
t_response <- rtruncweibull(
sshape[1], scale = sscale[1], data$t_min[i], Inf # t_min since time of SoT is known
)
# apply visit scheme
n_visits_response <- t_response %/% visit_spacing[g]
tmin_response <- data$t_min[i] + visit_spacing[g] * n_visits_response
tmax_response <- data$t_min[i] + visit_spacing[g] * (n_visits_response + 1)
# sample subsequent progression,
dt_progression <- stats::rweibull(1, sshape[3], sscale[3])
# apply visit scheme
n_visits_progression <- (dt_progression + t_response) %/% visit_spacing[g]
tmin_progression <- data$t_min[i] + visit_spacing[g] * n_visits_progression
tmax_progression <- data$t_min[i] + visit_spacing[g] * (n_visits_progression + 1)
res <- bind_rows(
res, tribble(
~subject_id, ~group_id, ~from, ~to, ~t_min, ~t_max, ~t_sot, ~iter,
data$subject_id[i], g, "stable", "response", tmin_response, tmax_response, data$t_sot[i], j,
data$subject_id[i], g, "response", "progression", tmin_progression, tmax_progression, data$t_sot[i], j
)
)
} else { # sample progression directly
dt_progression <- rtruncweibull(
sshape[2], scale = sscale[2], data$t_min[i], Inf # t_min since time of SoT is known
)
# apply visit scheme
n_visits_progression <- dt_progression %/% visit_spacing[g]
tmin_progression <- data$t_min[i] + visit_spacing[g] * n_visits_progression
tmax_progression <- data$t_min[i] + visit_spacing[g] * (n_visits_progression + 1)
res <- bind_rows(
res, tribble(
~subject_id, ~group_id, ~from, ~to, ~t_min, ~t_max, ~t_sot, ~iter,
data$subject_id[i], g, "stable", "progression", tmin_progression, tmax_progression, data$t_sot[i], j
)
)
} # end stable -> progression
} # end from == 1
if (data$from[i] == "response") {
if (data$from[i - 1] != "stable" || data$subject_id[i - 1] != data$subject_id[i]) {
stop()
}
# sample exact response time
t_response <- rtruncweibull(
sshape[1], scale = sscale[1], data$t_min[i - 1], data$t_max[i - 1]
)
# sample progression time
dt_progression <- rtruncweibull(
sshape[3], scale = sscale[3], data$t_min[i] - t_response, Inf
)
# apply visit scheme
n_visits_progression <- dt_progression %/% visit_spacing[g]
tmin_progression <- data$t_min[i] + visit_spacing[g] * n_visits_progression
tmax_progression <- data$t_min[i] + visit_spacing[g] * (n_visits_progression + 1)
res <- bind_rows(
res, tribble(
~subject_id, ~group_id, ~from, ~to, ~t_min, ~t_max, ~t_sot, ~iter,
data$subject_id[i], g, "response", "progression", tmin_progression, tmax_progression, data$t_sot[i], j
)
)
} # end from == 2
} # end iterate of j
} # end if/else
} # end iteration over i
# convert subject and group id back
res <- res %>%
mutate(
subject_id = as.character(
factor(.data$subject_id, levels = seq_along(subject_id_levels), labels = subject_id_levels)
),
group_id = as.character(
factor(.data$group_id, levels = seq_along(group_id_levels), labels = group_id_levels)
)
)
return(res)
}

To work with rcpp we need to create the columns separately and then put them together as data.frame at the very end.

Catch cornercase of EOF after terminal state

When the data contains EOF after the terminal state (progression) the posterior fit gets a hiccup.

Need to filter this or throw an informative error.

  • Define a check_data(data, model) function; treat EOF > terminal as warning
  • call it in sample_posterior() and impute()
  • ignore EOF if EOF > terminal

add baseline covariates

Time-dependent covariates are a bit tricky to incorporate since they would require a joint longitudinal / multi-state model. Sex, treatment line etc. are fixed at baseline though and can be incorporated relatively simply. Since the model is not hazard-based, covariates would affect logodds of transition probabilities and the location parameter of the transition times.

data input checking

raise an error when using a dataset that does not conform with model (eg rsponse -> stable).

switch to beta prior for orr

Logodds might be more convenient to implement hierarchical borrowing; for now beta priros might make it more convenient to elicit priors though.

Release oncomsm 0.1.2

First release:

Prepare for release:

  • git pull
  • urlchecker::url_check()
  • devtools::check(remote = TRUE, manual = TRUE)
  • devtools::check_win_devel()
  • rhub::check_for_cran()
  • rhub::check(platform = 'ubuntu-rchk')
  • rhub::check_with_sanitizers()
  • git push

Submit to CRAN:

  • usethis::use_version('patch')
  • devtools::submit_cran()
  • Approve email

Wait for CRAN...

  • Accepted ๐ŸŽ‰
  • git push
  • usethis::use_github_release()
  • usethis::use_dev_version()
  • usethis::use_news_md()
  • git push

Make states agnostic

There is no reason to be opinioted about how to call the three states in the model.

achieve 100% unit-test coverage

We should gradually increase the unit-test coverage up to 100%. Structural testing is done in vignettes for now, maybe additional pkgdown articles are required as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.