-
Notifications
You must be signed in to change notification settings - Fork 3
/
MBMA_stan.R
272 lines (237 loc) · 11.4 KB
/
MBMA_stan.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
#' Fitting a model-based meta-analysis model using Stan
#'
#' `MBMA_stan` fits a model-based meta-analysis model using Stan.
#'
#' @export
#' @param data An object of `create_MBMA_dat`.
#' @param Pred_doses A numerical vector specifying the doses which prediction will be made.
#' @param mu_prior A numerical vector specifying the parameter of the normal prior
#' density for baseline risks, first value is parameter for mean, second is for variance.
#' Default is c(0, 10).
#' @param alpha_prior A numerical vector specifying the parameter of the normal prior
#' density for the alpha parameter, first value is parameter for mean, second is for variance.
#' Default is c(0, 10). Needed for linear and linear log-dose models.
#' @param Emax_prior A numerical vector specifying the parameter of the normal prior
#' density for Emax parameter, first value is parameter for mean, second
#' is for standard deviation. Default is c(0, 10). Needed for emax and sigmoidal models.
#' @param ED50_prior A numerical vector specifying the parameter of the normal prior
#' density for ED50 parameter, first value is parameter for mean, second
#' is for standard deviation. Default is c(0, 10). Needed for emax and sigmoidal models.
#' @param ED50_prior_dist A string specifying the prior density for the ED50 parameter,
#' `functional` is for a functional uniform prior, `half-normal` for uniform prior, `half-cauchy` for
#' half-cauchy prior.
#' @param tau_prior A numerical value specifying the standard dev. of the prior density
#' for heterogenety stdev. Default is 0.5.
#' @param tau_prior_dist A string specifying the prior density for the heterogeneity standard deviation,
#' option is `half-normal` for half-normal prior, `uniform` for uniform prior, `half-cauchy` for
#' half-cauchy prior.
#' @param gamma_prior A numerical vector specifying the parameter of the normal prior
#' density for gamma parameter, first value is parameter for mean, second
#' is for standard deviation. Default is c(1, 2). Needed for sigmoidal model.
#' @param dose_response A string specifying the function defining the dose-response model.
#' Options include "linear", "log-linear", "emax", and "sigmoidal".
#' @param likelihood A string specifying the likelihood of distributions defining the statistical
#' model. Options include "normal", "binomial", and "Poisson".
#' @param re A string specifying whether random-effects are included to the model. When `FALSE`, the
#' model corresponds to a fixed-effects model. The default is `TRUE`.
#' @param ncp A string specifying whether to use a non-centered parametrization.
#' The default is `TRUE`.
#' @param adapt_delta A numerical value specfying the target average proposal acceptance
#' probability for adaptation. See Stan manual for details. Default is 0.95. In general
#' you should not need to change adapt_delta unless you see a warning message about
#' divergent transitions, in which case you can increase adapt_delta from the
#' default to a value closer to 1 (e.g. from 0.95 to 0.99, or from 0.99 to 0.999, etc).
#' @param iter A positive integer specifying the number of iterations for each chain
#' (including warmup). The default is 2000.
#' @param warmup A positive integer specifying the number of warmup (aka burnin)
#' iterations per chain. The default is 1000.
#' @param chains A positive integer specifying the number of Markov chains.
#' The default is 4.
#' @param ... Further arguments passed to or from other methods.
#' @return an object of class `stanfit` returned by `rstan::sampling`
#' @references Boucher M, et al. The many flavors of model-based meta-analysis:
#' Part I-Introduction and landmark data. \emph{CPT: Pharmacometrics and Systems Pharmacology}.
#' 2016;5:54-64.
#' @references Guenhan BK, Roever C, Friede T. MetaStan: An R package for meta-analysis
#' and model-based meta-analysis using Stan. In preparation.
#' @references Mawdsley D, et al. Model-based network meta-analysis: A
#' framework for evidence synthesis of clinical trial data.
#' \emph{CPT: Pharmacometrics and Systems Pharmacology}. 2016;5:393-401.
#' @references Zhang J, et al. (2014). Network meta-analysis of randomized
#' clinical trials: Reporting the proper summaries. \emph{Clinical Trials}.
#' 11(2), 246–262.
#' @references Dias S, et al. Absolute or relative effects?
#' Arm-based synthesis of trial data. \emph{Research Synthesis Methods}.
#' 2016;7:23–28.
#'
#' @examples
#' \dontrun{
#'## Load the dataset
#' data('dat.Eletriptan', package = "MetaStan")
## Fitting a MBMA model
#' datMBMA = create_MetaStan_dat(dat = dat.Eletriptan,
#' armVars = c(dose = "d",
#' responders = "r",
#' sampleSize = "n"),
#' nArmsVar = "nd")
#'
#' MBMA.Emax <- MBMA_stan(data = datMBMA,
#' likelihood = "binomial",
#' dose_response = "emax",
#' Pred_doses = seq(0, 80, length.out = 11),
#' mu_prior = c(0, 100),
#' Emax_prior = c(0, 100),
#' tau_prior_dist = "half-normal",
#' tau_prior = 0.5)
#' plot(MBMA.Emax) + ggplot2::xlab("Doses (mg)") + ggplot2::ylab("response probabilities")
#'
#' }
#'
MBMA_stan = function(data = NULL,
likelihood = NULL,
dose_response = "emax",
mu_prior = c(0, 10),
Emax_prior = c(0, 100),
alpha_prior = c(0, 100),
tau_prior = 0.5,
tau_prior_dist = "half-normal",
ED50_prior = c(-2.5, 1.8),
ED50_prior_dist = "functional",
gamma_prior = c(1, 2),
Pred_doses,
re = TRUE,
ncp = TRUE,
chains = 4,
iter = 2000,
warmup = 1000,
adapt_delta = 0.95,
...) {
################ check likelihood used
if(is.null(likelihood) == TRUE){
stop("Function argument \"likelihood\" must be specified !!!")
}
if (likelihood %in% c("binomial", "normal", "poisson") == FALSE) {
stop("Function argument \"likelihood\" must be equal to \"binomial\" or \"normal\" or \"poisson\"!!!")
}
################ check model used
if (dose_response %in% c("linear", "log-linear", "emax", "sigmoidal") == FALSE) {
stop("Function argument \"dose-response\" must be equal to \"linear\" or \"log-linear\" or \"emax\" or
\"sigmoidal\" !!!")
}
################ check data
data_wide = data$data_wide
data = data$data_long
if (!is.data.frame(data)) { stop("Data MUST be a data frame!!!") }
if (missing(Pred_doses)) {
Pred_doses = seq(from = 0, to = max(as.numeric(as.character(data$dose)),
na.rm = TRUE), length.out = 30)
}
################ check prior for heterogeneity parameter
if(is.null(tau_prior_dist) == TRUE){
stop("Function argument \"half-normal\" or \"uniform\" or \"half-cauchy\" must be specified !!!")
}
################ prior for heterogeneity
if(tau_prior_dist == "half-normal") { tau_prior_dist_num = 1 }
if(tau_prior_dist == "uniform") { tau_prior_dist_num = 2 }
if(tau_prior_dist == "half-cauchy") { tau_prior_dist_num = 3 }
if(ED50_prior_dist == "functional") { ED50_prior_dist_num = 1 }
if(ED50_prior_dist == "half-normal") { ED50_prior_dist_num = 2 }
if(likelihood == "normal") { link = 1 }
if(likelihood == "binomial") { link = 2 }
if(likelihood == "poisson") { link = 3 }
if(dose_response == "linear") { dose_response = 1 }
if(dose_response == "log-linear") { dose_response = 2 }
if(dose_response == "emax") { dose_response = 3 }
if(dose_response == "sigmoidal") { dose_response = 4 }
################ Create a list to be used with Stan
Nobs = nrow(data)
y <- array(rep(0,Nobs))
y_se <- array(rep(0,Nobs))
r <- array(rep(0,Nobs))
n <- array(rep(1,Nobs))
count <- array(rep(0, Nobs))
exposure <- array(rep(0, Nobs))
if(likelihood == "binomial") {
r = data$responders
n = data$sampleSize
}
if(likelihood == "normal") {
y = data$mean
y_se = data$std.err
}
if(likelihood == "poisson") {
count = data$count
exposure = data$exposure
}
if(re == TRUE) {re = 1}
if(re == FALSE) {re = 0}
if(ncp == TRUE) {ncp = 1}
if(ncp == FALSE) {ncp = 0}
## Create a list to be used with Stan
data = cbind(ID = 1:nrow(data), data)
b_ndx = data[data$dose == 0, ]$ID
t_ndx = data[data$dose != 0, ]$ID
if(dose_response == 1 || dose_response == 2) {alpha_Emax_prior = alpha_prior}
if(dose_response == 3 || dose_response == 4) {alpha_Emax_prior = Emax_prior}
stan_dat_long <- list(Nobs = Nobs,
link = link,
st = data$study,
Nst = max(data$study),
dose = as.numeric(as.character(data$dose)),
ndose = data_wide$nd,
Npred = length(Pred_doses),
Pred_doses = Pred_doses,
y = y,
y_se = y_se,
r = r,
n = n,
count = count,
exposure = exposure,
maxdose = max(as.numeric(data$dose)),
mu_prior = mu_prior,
alpha_prior = alpha_Emax_prior,
ED50_prior = ED50_prior,
gamma_prior = gamma_prior,
ED50_prior_dist = ED50_prior_dist_num,
tau_prior = tau_prior,
tau_prior_dist = tau_prior_dist_num,
dose_response = dose_response,
re = re,
ncp = ncp,
b_ndx = b_ndx,
t_ndx = t_ndx)
## Fitting the model
fit = rstan::sampling(stanmodels$MBMA,
data = stan_dat_long,
chains = chains,
iter = iter,
warmup = warmup,
control = list(adapt_delta = adapt_delta),
...)
## MODEL FINISHED
fit_sum <- rstan::summary(fit)$summary
Rhat.max <- max(fit_sum[,"Rhat"], na.rm=TRUE)
N_EFF.min <- min(fit_sum[,"n_eff"], na.rm=TRUE)
if(Rhat.max > 1.1)
warning("Maximal Rhat > 1.1. Consider increasing meta_stan MCMC parameters.")
## finally include a check if the Stan NuTS sample had any
## divergence in the sampling phase, these are not supposed to
## happen and can often be avoided by increasing adapt_delta
sampler_params <- rstan::get_sampler_params(fit, inc_warmup=FALSE)
n_divergent <- sum(sapply(sampler_params, function(x) sum(x[,'divergent__'])) )
if(n_divergent > 0) {
warning(paste("In total", n_divergent, "divergent transitions occured during the sampling
phase.\nPlease consider increasing adapt_delta closer to 1."))
}
out = list(fit = fit,
fit_sum = fit_sum,
data = stan_dat_long,
data_wide = data_wide,
data_long = data,
Rhat.max = Rhat.max,
N_EFF.min = N_EFF.min,
tau_prior_dist = tau_prior_dist,
ED50_prior_dist = ED50_prior_dist)
class(out) <- "MBMA_stan"
return(out)
}