library(multinma)
options(mc.cores = parallel::detectCores())
#> For execution on a local, multicore CPU with excess RAM we recommend calling
#> options(mc.cores = parallel::detectCores())
#>
#> Attaching package: 'multinma'
#> The following objects are masked from 'package:stats':
#>
#> dgamma, pgamma, qgamma
This vignette describes the analysis of smoking cessation data (Hasselblad 1998), replicating the analysis in NICE Technical Support Document 4 (Dias et al. 2011). The data are available in this package as smoking
:
head(smoking)
#> studyn trtn trtc r n
#> 1 1 1 No intervention 9 140
#> 2 1 3 Individual counselling 23 140
#> 3 1 4 Group counselling 10 138
#> 4 2 2 Self-help 11 78
#> 5 2 3 Individual counselling 12 85
#> 6 2 4 Group counselling 29 170
We begin by setting up the network. We have arm-level count data giving the number quitting smoking (r
) out of the total (n
) in each arm, so we use the function set_agd_arm()
. Treatment “No intervention” is set as the network reference treatment.
<- set_agd_arm(smoking,
smknet study = studyn,
trt = trtc,
r = r,
n = n,
trt_ref = "No intervention")
smknet#> A network with 24 AgD studies (arm-based).
#>
#> ------------------------------------------------------- AgD studies (arm-based) ----
#> Study Treatments
#> 1 3: No intervention | Individual counselling | Group counselling
#> 2 3: Self-help | Individual counselling | Group counselling
#> 3 2: No intervention | Individual counselling
#> 4 2: No intervention | Individual counselling
#> 5 2: No intervention | Individual counselling
#> 6 2: No intervention | Individual counselling
#> 7 2: No intervention | Individual counselling
#> 8 2: No intervention | Individual counselling
#> 9 2: No intervention | Individual counselling
#> 10 2: No intervention | Self-help
#> ... plus 14 more studies
#>
#> Outcome type: count
#> ------------------------------------------------------------------------------------
#> Total number of treatments: 4
#> Total number of studies: 24
#> Reference treatment is: No intervention
#> Network is connected
Plot the network structure.
plot(smknet, weight_edges = TRUE, weight_nodes = TRUE)
Following TSD 4, we fit a random effects NMA model, using the nma()
function with trt_effects = "random"
. We use \(\mathrm{N}(0, 100^2)\) prior distributions for the treatment effects \(d_k\) and study-specific intercepts \(\mu_j\), and a \(\textrm{half-N}(5^2)\) prior distribution for the between-study heterogeneity standard deviation \(\tau\). We can examine the range of parameter values implied by these prior distributions with the summary()
method:
summary(normal(scale = 100))
#> A Normal prior distribution: location = 0, scale = 100.
#> 50% of the prior density lies between -67.45 and 67.45.
#> 95% of the prior density lies between -196 and 196.
summary(half_normal(scale = 5))
#> A half-Normal prior distribution: location = 0, scale = 5.
#> 50% of the prior density lies between 0 and 3.37.
#> 95% of the prior density lies between 0 and 9.8.
The model is fitted using the nma()
function. By default, this will use a Binomial likelihood and a logit link function, auto-detected from the data.
<- nma(smknet,
smkfit trt_effects = "random",
prior_intercept = normal(scale = 100),
prior_trt = normal(scale = 100),
prior_het = normal(scale = 5))
Basic parameter summaries are given by the print()
method:
smkfit#> A random effects NMA with a binomial likelihood (logit link).
#> Inference for Stan model: binomial_1par.
#> 4 chains, each with iter=2000; warmup=1000; thin=1;
#> post-warmup draws per chain=1000, total post-warmup draws=4000.
#>
#> mean se_mean sd 2.5% 25% 50% 75% 97.5% n_eff
#> d[Group counselling] 1.11 0.01 0.43 0.27 0.82 1.10 1.38 1.97 1828
#> d[Individual counselling] 0.84 0.01 0.23 0.40 0.69 0.84 1.00 1.32 1166
#> d[Self-help] 0.50 0.01 0.40 -0.28 0.24 0.49 0.74 1.34 2048
#> lp__ -5768.02 0.19 6.40 -5781.65 -5772.15 -5767.77 -5763.47 -5756.45 1089
#> tau 0.84 0.01 0.19 0.55 0.71 0.81 0.94 1.28 1166
#> Rhat
#> d[Group counselling] 1.00
#> d[Individual counselling] 1.01
#> d[Self-help] 1.00
#> lp__ 1.00
#> tau 1.00
#>
#> Samples were drawn using NUTS(diag_e) at Tue Jan 18 09:30:10 2022.
#> For each parameter, n_eff is a crude measure of effective sample size,
#> and Rhat is the potential scale reduction factor on split chains (at
#> convergence, Rhat=1).
By default, summaries of the study-specific intercepts \(\mu_j\) and study-specific relative effects \(\delta_{jk}\) are hidden, but could be examined by changing the pars
argument:
# Not run
print(smkfit, pars = c("d", "tau", "mu", "delta"))
The prior and posterior distributions can be compared visually using the plot_prior_posterior()
function:
plot_prior_posterior(smkfit)
By default, this displays all model parameters given prior distributions (in this case \(d_k\), \(\mu_j\), and \(\tau\)), but this may be changed using the prior
argument:
plot_prior_posterior(smkfit, prior = "het")
Model fit can be checked using the dic()
function
<- dic(smkfit))
(dic_consistency #> Residual deviance: 54.1 (on 50 data points)
#> pD: 43.9
#> DIC: 98
and the residual deviance contributions examined with the corresponding plot()
method
plot(dic_consistency)
Overall model fit seems to be adequate, with almost all points showing good fit (mean residual deviance contribution of 1). The only two points with higher residual deviance (i.e. worse fit) correspond to the two zero counts in the data:
$r == 0, ]
smoking[smoking#> studyn trtn trtc r n
#> 13 6 1 No intervention 0 33
#> 31 15 1 No intervention 0 20
Note: The results of the inconsistency models here are slightly different to those of Dias et al. (2010, 2011), although the overall conclusions are the same. This is due to the presence of multi-arm trials and a different ordering of treatments, meaning that inconsistency is parameterised differently within the multi-arm trials. The same results as Dias et al. are obtained if the network is instead set up with
trtn
as the treatment variable.
Another method for assessing inconsistency is node-splitting (Dias et al. 2011, 2010). Whereas the UME model assesses inconsistency globally, node-splitting assesses inconsistency locally for each potentially inconsistent comparison (those with both direct and indirect evidence) in turn.
Node-splitting can be performed using the nma()
function with the argument consistency = "nodesplit"
. By default, all possible comparisons will be split (as determined by the get_nodesplits()
function). Alternatively, a specific comparison or comparisons to split can be provided to the nodesplit
argument.
<- nma(smknet,
smk_nodesplit consistency = "nodesplit",
trt_effects = "random",
prior_intercept = normal(scale = 100),
prior_trt = normal(scale = 100),
prior_het = normal(scale = 5))
#> Fitting model 1 of 7, node-split: Group counselling vs. No intervention
#> Fitting model 2 of 7, node-split: Individual counselling vs. No intervention
#> Fitting model 3 of 7, node-split: Self-help vs. No intervention
#> Fitting model 4 of 7, node-split: Individual counselling vs. Group counselling
#> Fitting model 5 of 7, node-split: Self-help vs. Group counselling
#> Fitting model 6 of 7, node-split: Self-help vs. Individual counselling
#> Fitting model 7 of 7, consistency model
The summary()
method summarises the node-splitting results, displaying the direct and indirect estimates \(d_\mathrm{dir}\) and \(d_\mathrm{ind}\) from each node-split model, the network estimate \(d_\mathrm{net}\) from the consistency model, the inconsistency factor \(\omega = d_\mathrm{dir} - d_\mathrm{ind}\), and a Bayesian \(p\)-value for inconsistency on each comparison. Since random effects models are fitted, the heterogeneity standard deviation \(tau\) under each node-split model and under the consistency model is also displayed. The DIC model fit statistics are also provided.
summary(smk_nodesplit)
#> Node-splitting models fitted for 6 comparisons.
#>
#> ------------------------------ Node-split Group counselling vs. No intervention ----
#>
#> mean sd 2.5% 25% 50% 75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d_net 1.10 0.45 0.26 0.80 1.09 1.39 2.00 1919 2548 1
#> d_dir 1.05 0.75 -0.36 0.56 1.02 1.54 2.63 4077 3242 1
#> d_ind 1.14 0.56 0.07 0.78 1.12 1.48 2.28 1756 1959 1
#> omega -0.08 0.90 -1.81 -0.69 -0.11 0.49 1.78 2555 2536 1
#> tau 0.86 0.19 0.56 0.72 0.84 0.98 1.30 1076 1623 1
#> tau_consistency 0.84 0.19 0.55 0.71 0.82 0.95 1.28 1345 2061 1
#>
#> Residual deviance: 54.2 (on 50 data points)
#> pD: 44.2
#> DIC: 98.4
#>
#> Bayesian p-value: 0.9
#>
#> ------------------------- Node-split Individual counselling vs. No intervention ----
#>
#> mean sd 2.5% 25% 50% 75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d_net 0.84 0.24 0.38 0.68 0.83 0.99 1.34 1388 1931 1
#> d_dir 0.88 0.26 0.39 0.70 0.86 1.04 1.43 2045 2465 1
#> d_ind 0.60 0.69 -0.66 0.13 0.57 1.04 2.02 1590 2107 1
#> omega 0.28 0.71 -1.19 -0.17 0.30 0.74 1.63 1470 2294 1
#> tau 0.87 0.20 0.56 0.73 0.85 0.99 1.35 1390 1779 1
#> tau_consistency 0.84 0.19 0.55 0.71 0.82 0.95 1.28 1345 2061 1
#>
#> Residual deviance: 53.9 (on 50 data points)
#> pD: 44.2
#> DIC: 98.1
#>
#> Bayesian p-value: 0.65
#>
#> -------------------------------------- Node-split Self-help vs. No intervention ----
#>
#> mean sd 2.5% 25% 50% 75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d_net 0.49 0.40 -0.30 0.22 0.50 0.75 1.28 1984 2102 1
#> d_dir 0.34 0.54 -0.71 -0.01 0.34 0.69 1.39 3780 2464 1
#> d_ind 0.71 0.64 -0.52 0.28 0.69 1.11 2.05 1772 2360 1
#> omega -0.37 0.83 -2.06 -0.91 -0.35 0.18 1.23 2129 2235 1
#> tau 0.87 0.19 0.56 0.73 0.84 0.98 1.30 1177 2323 1
#> tau_consistency 0.84 0.19 0.55 0.71 0.82 0.95 1.28 1345 2061 1
#>
#> Residual deviance: 53.9 (on 50 data points)
#> pD: 44.3
#> DIC: 98.2
#>
#> Bayesian p-value: 0.65
#>
#> ----------------------- Node-split Individual counselling vs. Group counselling ----
#>
#> mean sd 2.5% 25% 50% 75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d_net -0.26 0.42 -1.09 -0.54 -0.25 0.01 0.56 2577 2692 1
#> d_dir -0.10 0.48 -1.06 -0.42 -0.10 0.22 0.84 3841 2892 1
#> d_ind -0.57 0.63 -1.84 -0.97 -0.56 -0.16 0.61 1694 2154 1
#> omega 0.46 0.69 -0.85 -0.01 0.46 0.92 1.83 1782 2387 1
#> tau 0.87 0.20 0.55 0.73 0.85 0.98 1.32 1100 1795 1
#> tau_consistency 0.84 0.19 0.55 0.71 0.82 0.95 1.28 1345 2061 1
#>
#> Residual deviance: 54 (on 50 data points)
#> pD: 44.4
#> DIC: 98.3
#>
#> Bayesian p-value: 0.51
#>
#> ------------------------------------ Node-split Self-help vs. Group counselling ----
#>
#> mean sd 2.5% 25% 50% 75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d_net -0.61 0.49 -1.58 -0.91 -0.61 -0.28 0.32 2981 2656 1.00
#> d_dir -0.62 0.65 -1.90 -1.05 -0.61 -0.20 0.63 4189 3062 1.00
#> d_ind -0.65 0.66 -2.02 -1.07 -0.63 -0.21 0.62 2125 2500 1.00
#> omega 0.03 0.86 -1.66 -0.55 0.01 0.57 1.78 2305 2651 1.00
#> tau 0.87 0.20 0.56 0.73 0.85 0.98 1.33 1064 1632 1.01
#> tau_consistency 0.84 0.19 0.55 0.71 0.82 0.95 1.28 1345 2061 1.00
#>
#> Residual deviance: 54.1 (on 50 data points)
#> pD: 44.5
#> DIC: 98.6
#>
#> Bayesian p-value: 0.99
#>
#> ------------------------------- Node-split Self-help vs. Individual counselling ----
#>
#> mean sd 2.5% 25% 50% 75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d_net -0.35 0.41 -1.16 -0.62 -0.34 -0.08 0.44 2648 2672 1.00
#> d_dir 0.07 0.65 -1.20 -0.37 0.07 0.49 1.35 3614 3184 1.00
#> d_ind -0.63 0.52 -1.69 -0.97 -0.63 -0.29 0.36 1809 2087 1.00
#> omega 0.70 0.80 -0.88 0.17 0.70 1.22 2.30 2290 2392 1.00
#> tau 0.86 0.20 0.54 0.72 0.83 0.97 1.33 1233 1555 1.01
#> tau_consistency 0.84 0.19 0.55 0.71 0.82 0.95 1.28 1345 2061 1.00
#>
#> Residual deviance: 53.6 (on 50 data points)
#> pD: 43.9
#> DIC: 97.4
#>
#> Bayesian p-value: 0.37
The DIC of each inconsistency model is unchanged from the consistency model, no node-splits result in reduced heterogeneity standard deviation \(\tau\) compared to the consistency model, and the Bayesian \(p\)-values are all large. There is no evidence of inconsistency.
We can visually compare the posterior distributions of the direct, indirect, and network estimates using the plot()
method. These are all in agreement; the posterior densities of the direct and indirect estimates overlap. Notice that there is not much indirect information for the Individual counselling vs. No intervention comparison, so the network (consistency) estimate is very similar to the direct estimate for this comparison.
plot(smk_nodesplit) +
::theme(legend.position = "bottom", legend.direct = "horizontal") ggplot2
Pairwise relative effects, for all pairwise contrasts with all_contrasts = TRUE
.
<- relative_effects(smkfit, all_contrasts = TRUE))
(smk_releff #> mean sd 2.5% 25% 50% 75% 97.5% Bulk_ESS
#> d[Group counselling vs. No intervention] 1.11 0.43 0.27 0.82 1.10 1.38 1.97 1833
#> d[Individual counselling vs. No intervention] 0.84 0.23 0.40 0.69 0.84 1.00 1.32 1174
#> d[Self-help vs. No intervention] 0.50 0.40 -0.28 0.24 0.49 0.74 1.34 2074
#> d[Individual counselling vs. Group counselling] -0.26 0.40 -1.06 -0.52 -0.26 0.00 0.52 2383
#> d[Self-help vs. Group counselling] -0.61 0.48 -1.57 -0.92 -0.60 -0.30 0.34 2842
#> d[Self-help vs. Individual counselling] -0.35 0.41 -1.18 -0.61 -0.35 -0.07 0.48 2276
#> Tail_ESS Rhat
#> d[Group counselling vs. No intervention] 2481 1.00
#> d[Individual counselling vs. No intervention] 1901 1.01
#> d[Self-help vs. No intervention] 2353 1.00
#> d[Individual counselling vs. Group counselling] 2568 1.00
#> d[Self-help vs. Group counselling] 2833 1.00
#> d[Self-help vs. Individual counselling] 2567 1.00
plot(smk_releff, ref_line = 0)
Treatment rankings, rank probabilities, and cumulative rank probabilities. We set lower_better = FALSE
since a higher log odds of cessation is better (the outcome is positive).
<- posterior_ranks(smkfit, lower_better = FALSE))
(smk_ranks #> mean sd 2.5% 25% 50% 75% 97.5% Bulk_ESS Tail_ESS Rhat
#> rank[No intervention] 3.89 0.32 3 4 4 4 4 2469 NA 1
#> rank[Group counselling] 1.35 0.62 1 1 1 2 3 2742 2769 1
#> rank[Individual counselling] 1.94 0.62 1 2 2 2 3 2548 2585 1
#> rank[Self-help] 2.81 0.68 1 3 3 3 4 2755 NA 1
plot(smk_ranks)
<- posterior_rank_probs(smkfit, lower_better = FALSE))
(smk_rankprobs #> p_rank[1] p_rank[2] p_rank[3] p_rank[4]
#> d[No intervention] 0.00 0.00 0.10 0.9
#> d[Group counselling] 0.72 0.21 0.06 0.0
#> d[Individual counselling] 0.22 0.61 0.16 0.0
#> d[Self-help] 0.06 0.17 0.67 0.1
plot(smk_rankprobs)
<- posterior_rank_probs(smkfit, lower_better = FALSE, cumulative = TRUE))
(smk_cumrankprobs #> p_rank[1] p_rank[2] p_rank[3] p_rank[4]
#> d[No intervention] 0.00 0.00 0.1 1
#> d[Group counselling] 0.72 0.93 1.0 1
#> d[Individual counselling] 0.22 0.84 1.0 1
#> d[Self-help] 0.06 0.23 0.9 1
plot(smk_cumrankprobs)