Skip to contents
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 data on the mean off-time reduction in patients given dopamine agonists as adjunct therapy in Parkinson’s disease, in a network of 7 trials of 4 active drugs plus placebo (Dias et al. 2011). The data are available in this package as parkinsons:

head(parkinsons)
#>   studyn trtn     y    se   n  diff se_diff
#> 1      1    1 -1.22 0.504  54    NA   0.504
#> 2      1    3 -1.53 0.439  95 -0.31   0.668
#> 3      2    1 -0.70 0.282 172    NA   0.282
#> 4      2    2 -2.40 0.258 173 -1.70   0.382
#> 5      3    1 -0.30 0.505  76    NA   0.505
#> 6      3    2 -2.60 0.510  71 -2.30   0.718

We consider analysing these data in three separate ways:

  1. Using arm-based data (means y and corresponding standard errors se);
  2. Using contrast-based data (mean differences diff and corresponding standard errors se_diff);
  3. A combination of the two, where some studies contribute arm-based data, and other contribute contrast-based data.

Note: In this case, with Normal likelihoods for both arms and contrasts, we will see that the three analyses give identical results. In general, unless the arm-based likelihood is Normal, results from a model using a contrast-based likelihood will not exactly match those from a model using an arm-based likelihood, since the contrast-based Normal likelihood is only an approximation. Similarity of results depends on the suitability of the Normal approximation, which may not always be appropriate - e.g. with a small number of events or small sample size for a binary outcome. The use of an arm-based likelihood (sometimes called an “exact” likelihood) is therefore preferable where possible in general.

Analysis of arm-based data

We begin with an analysis of the arm-based data - means and standard errors.

Setting up the network

We have arm-level continuous data giving the mean off-time reduction (y) and standard error (se) in each arm. We use the function set_agd_arm() to set up the network.

arm_net <- set_agd_arm(parkinsons, 
                      study = studyn,
                      trt = trtn,
                      y = y, 
                      se = se,
                      sample_size = n)
arm_net
#> A network with 7 AgD studies (arm-based).
#> 
#> ------------------------------------------------------- AgD studies (arm-based) ---- 
#>  Study Treatment arms
#>  1     2: 1 | 3      
#>  2     2: 1 | 2      
#>  3     3: 4 | 1 | 2  
#>  4     2: 4 | 3      
#>  5     2: 4 | 3      
#>  6     2: 4 | 5      
#>  7     2: 4 | 5      
#> 
#>  Outcome type: continuous
#> ------------------------------------------------------------------------------------
#> Total number of treatments: 5
#> Total number of studies: 7
#> Reference treatment is: 4
#> Network is connected

We let treatment 4 be set by default as the network reference treatment, since this results in considerably improved sampling efficiency over choosing treatment 1 as the network reference. The sample_size argument is optional, but enables the nodes to be weighted by sample size in the network plot.

Plot the network structure.

plot(arm_net, weight_edges = TRUE, weight_nodes = TRUE)

Meta-analysis models

We fit both fixed effect (FE) and random effects (RE) models.

Fixed effect meta-analysis

First, we fit a fixed effect model using the nma() function with trt_effects = "fixed". We use \(\mathrm{N}(0, 100^2)\) prior distributions for the treatment effects \(d_k\) and study-specific intercepts \(\mu_j\). 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.

The model is fitted using the nma() function.

arm_fit_FE <- nma(arm_net, 
                  trt_effects = "fixed",
                  prior_intercept = normal(scale = 100),
                  prior_trt = normal(scale = 10))
#> Note: Setting "4" as the network reference treatment.

Basic parameter summaries are given by the print() method:

arm_fit_FE
#> A fixed effects NMA with a normal likelihood (identity link).
#> Inference for Stan model: normal.
#> 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 Rhat
#> d[1]  0.54    0.01 0.48  -0.40  0.21  0.54  0.86  1.50  1400    1
#> d[2] -1.28    0.01 0.53  -2.28 -1.65 -1.27 -0.92 -0.25  1525    1
#> d[3]  0.05    0.01 0.33  -0.61 -0.17  0.05  0.28  0.69  1930    1
#> d[5] -0.30    0.00 0.21  -0.73 -0.44 -0.30 -0.16  0.09  2683    1
#> lp__ -6.73    0.06 2.43 -12.38 -8.12 -6.36 -4.98 -3.12  1570    1
#> 
#> Samples were drawn using NUTS(diag_e) at Wed Mar  6 13:23:17 2024.
#> 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\) are hidden, but could be examined by changing the pars argument:

# Not run
print(arm_fit_FE, pars = c("d", "mu"))

The prior and posterior distributions can be compared visually using the plot_prior_posterior() function:

Random effects meta-analysis

We now fit a random effects model using the nma() function with trt_effects = "random". Again, we use \(\mathrm{N}(0, 100^2)\) prior distributions for the treatment effects \(d_k\) and study-specific intercepts \(\mu_j\), and we additionally use a \(\textrm{half-N}(5^2)\) prior for the 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.

Fitting the RE model

arm_fit_RE <- nma(arm_net, 
                  seed = 379394727,
                  trt_effects = "random",
                  prior_intercept = normal(scale = 100),
                  prior_trt = normal(scale = 100),
                  prior_het = half_normal(scale = 5),
                  adapt_delta = 0.99)
#> Note: Setting "4" as the network reference treatment.
#> Warning: There were 3 divergent transitions after warmup. See
#> https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
#> to find out why this is a problem and how to eliminate them.
#> Warning: Examine the pairs() plot to diagnose sampling problems

We do see a small number of divergent transition errors, which cannot simply be removed by increasing the value of the adapt_delta argument (by default set to 0.95 for RE models). To diagnose, we use the pairs() method to investigate where in the posterior distribution these divergences are happening (indicated by red crosses):

pairs(arm_fit_RE, pars = c("mu[4]", "d[3]", "delta[4: 3]", "tau"))
#> Warning: New theme missing the following elements: axis.text.theta, axis.text.r,
#> axis.ticks.theta, axis.ticks.r, axis.minor.ticks.x.top, axis.minor.ticks.x.bottom,
#> axis.minor.ticks.y.left, axis.minor.ticks.y.right, axis.minor.ticks.theta, axis.minor.ticks.r,
#> axis.ticks.length.theta, axis.ticks.length.r, axis.minor.ticks.length,
#> axis.minor.ticks.length.x, axis.minor.ticks.length.x.top, axis.minor.ticks.length.x.bottom,
#> axis.minor.ticks.length.y, axis.minor.ticks.length.y.left, axis.minor.ticks.length.y.right,
#> axis.minor.ticks.length.theta, axis.minor.ticks.length.r, axis.line.theta, axis.line.r,
#> legend.key.spacing, legend.key.spacing.x, legend.key.spacing.y, legend.frame, legend.ticks,
#> legend.ticks.length, legend.axis.line, legend.text.position, legend.title.position,
#> legend.position.inside, legend.byrow, legend.justification.top, legend.justification.bottom,
#> legend.justification.left, legend.justification.right, legend.justification.inside,
#> legend.location, plot.tag.location

The divergent transitions occur in the upper tail of the heterogeneity standard deviation. In this case, with only a small number of studies, there is not very much information to estimate the heterogeneity standard deviation and the prior distribution may be too heavy-tailed. We could consider a more informative prior distribution for the heterogeneity variance to aid estimation.

Basic parameter summaries are given by the print() method:

arm_fit_RE
#> A random effects NMA with a normal likelihood (identity link).
#> Inference for Stan model: normal.
#> 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 Rhat
#> d[1]   0.51    0.02 0.63  -0.67   0.11   0.52   0.91  1.70  1684    1
#> d[2]  -1.33    0.02 0.69  -2.72  -1.75  -1.30  -0.89 -0.07  1690    1
#> d[3]   0.02    0.01 0.45  -0.89  -0.25   0.03   0.28  0.88  2148    1
#> d[5]  -0.30    0.01 0.44  -1.21  -0.50  -0.29  -0.09  0.56  1346    1
#> lp__ -12.81    0.10 3.59 -20.80 -15.10 -12.52 -10.20 -6.61  1234    1
#> tau    0.38    0.01 0.37   0.01   0.13   0.27   0.51  1.41   877    1
#> 
#> Samples were drawn using NUTS(diag_e) at Wed Mar  6 13:23:27 2024.
#> 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(arm_fit_RE, pars = c("d", "mu", "delta"))

The prior and posterior distributions can be compared visually using the plot_prior_posterior() function:

Model comparison

Model fit can be checked using the dic() function:

(arm_dic_FE <- dic(arm_fit_FE))
#> Residual deviance: 13.4 (on 15 data points)
#>                pD: 11.1
#>               DIC: 24.6
(arm_dic_RE <- dic(arm_fit_RE))
#> Residual deviance: 13.6 (on 15 data points)
#>                pD: 12.4
#>               DIC: 26.1

Both models fit the data well, having posterior mean residual deviance close to the number of data points. The DIC is similar between models, so we choose the FE model based on parsimony.

We can also examine the residual deviance contributions with the corresponding plot() method.

plot(arm_dic_FE)

plot(arm_dic_RE)

Further results

For comparison with Dias et al. (2011), we can produce relative effects against placebo using the relative_effects() function with trt_ref = 1:

(arm_releff_FE <- relative_effects(arm_fit_FE, trt_ref = 1))
#>       mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d[4] -0.54 0.48 -1.50 -0.86 -0.54 -0.21  0.40     1416     1936    1
#> d[2] -1.82 0.33 -2.48 -2.04 -1.82 -1.60 -1.15     5879     2894    1
#> d[3] -0.49 0.49 -1.44 -0.81 -0.49 -0.17  0.47     2074     2425    1
#> d[5] -0.84 0.53 -1.85 -1.20 -0.85 -0.49  0.18     1574     2462    1
plot(arm_releff_FE, ref_line = 0)

(arm_releff_RE <- relative_effects(arm_fit_RE, trt_ref = 1))
#>       mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d[4] -0.51 0.63 -1.70 -0.91 -0.52 -0.11  0.67     1804     1484    1
#> d[2] -1.84 0.51 -2.86 -2.12 -1.83 -1.54 -0.86     3748     2408    1
#> d[3] -0.49 0.66 -1.80 -0.90 -0.48 -0.08  0.74     2657     1956    1
#> d[5] -0.81 0.78 -2.36 -1.25 -0.82 -0.36  0.66     1737     1588    1
plot(arm_releff_RE, ref_line = 0)

Following Dias et al. (2011), we produce absolute predictions of the mean off-time reduction on each treatment assuming a Normal distribution for the outcomes on treatment 1 (placebo) with mean \(-0.73\) and precision \(21\). We use the predict() method, where the baseline argument takes a distr() distribution object with which we specify the corresponding Normal distribution, and we specify trt_ref = 1 to indicate that the baseline distribution corresponds to treatment 1. (Strictly speaking, type = "response" is unnecessary here, since the identity link function was used.)

arm_pred_FE <- predict(arm_fit_FE, 
                       baseline = distr(qnorm, mean = -0.73, sd = 21^-0.5),
                       type = "response",
                       trt_ref = 1)
arm_pred_FE
#>          mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[4] -1.27 0.53 -2.33 -1.62 -1.27 -0.91 -0.21     1636     2297    1
#> pred[1] -0.73 0.22 -1.16 -0.88 -0.73 -0.58 -0.29     3910     3936    1
#> pred[2] -2.55 0.40 -3.32 -2.82 -2.54 -2.28 -1.79     5087     3341    1
#> pred[3] -1.22 0.54 -2.27 -1.57 -1.22 -0.85 -0.18     2382     2516    1
#> pred[5] -1.57 0.57 -2.67 -1.95 -1.57 -1.18 -0.44     1741     2657    1
plot(arm_pred_FE)

arm_pred_RE <- predict(arm_fit_RE, 
                       baseline = distr(qnorm, mean = -0.73, sd = 21^-0.5),
                       type = "response",
                       trt_ref = 1)
arm_pred_RE
#>          mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[4] -1.24 0.67 -2.51 -1.66 -1.25 -0.81  0.04     1938     1999    1
#> pred[1] -0.73 0.22 -1.16 -0.87 -0.73 -0.58 -0.30     3976     3867    1
#> pred[2] -2.56 0.56 -3.69 -2.90 -2.56 -2.23 -1.50     3593     2853    1
#> pred[3] -1.22 0.70 -2.63 -1.66 -1.22 -0.78  0.09     2838     2008    1
#> pred[5] -1.54 0.82 -3.11 -2.03 -1.55 -1.05 -0.02     1807     1581    1
plot(arm_pred_RE)

If the baseline argument is omitted, predictions of mean off-time reduction will be produced for every study in the network based on their estimated baseline response \(\mu_j\):

arm_pred_FE_studies <- predict(arm_fit_FE, type = "response")
arm_pred_FE_studies
#> ---------------------------------------------------------------------- Study: 1 ---- 
#> 
#>             mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[1: 4] -1.65 0.46 -2.57 -1.97 -1.65 -1.34 -0.76     1967     2359    1
#> pred[1: 1] -1.12 0.43 -1.96 -1.41 -1.12 -0.82 -0.29     3358     3179    1
#> pred[1: 2] -2.93 0.52 -3.98 -3.28 -2.93 -2.58 -1.94     3558     3007    1
#> pred[1: 3] -1.60 0.39 -2.36 -1.87 -1.60 -1.35 -0.85     3563     3476    1
#> pred[1: 5] -1.95 0.50 -2.95 -2.30 -1.95 -1.61 -0.97     2112     2518    1
#> 
#> ---------------------------------------------------------------------- Study: 2 ---- 
#> 
#>             mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[2: 4] -1.18 0.52 -2.19 -1.51 -1.18 -0.83 -0.19     1403     2042    1
#> pred[2: 1] -0.64 0.26 -1.16 -0.82 -0.64 -0.46 -0.11     5099     3542    1
#> pred[2: 2] -2.45 0.24 -2.93 -2.62 -2.45 -2.29 -1.99     4938     3308    1
#> pred[2: 3] -1.13 0.53 -2.19 -1.48 -1.13 -0.76 -0.10     2020     2243    1
#> pred[2: 5] -1.48 0.56 -2.56 -1.85 -1.48 -1.10 -0.38     1549     2294    1
#> 
#> ---------------------------------------------------------------------- Study: 3 ---- 
#> 
#>             mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[3: 4] -1.12 0.43 -1.96 -1.40 -1.13 -0.84 -0.30     1896     2287    1
#> pred[3: 1] -0.59 0.36 -1.30 -0.83 -0.58 -0.34  0.10     4295     2625    1
#> pred[3: 2] -2.40 0.39 -3.18 -2.66 -2.41 -2.14 -1.66     4336     2898    1
#> pred[3: 3] -1.07 0.48 -2.00 -1.39 -1.08 -0.75 -0.14     2678     2607    1
#> pred[3: 5] -1.42 0.48 -2.36 -1.74 -1.42 -1.12 -0.49     2069     2750    1
#> 
#> ---------------------------------------------------------------------- Study: 4 ---- 
#> 
#>             mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[4: 4] -0.40 0.30 -0.97 -0.61 -0.40 -0.20  0.18     2383     2650    1
#> pred[4: 1]  0.14 0.51 -0.84 -0.20  0.13  0.48  1.12     1909     2492    1
#> pred[4: 2] -1.68 0.56 -2.78 -2.05 -1.68 -1.31 -0.59     2009     2493    1
#> pred[4: 3] -0.35 0.24 -0.83 -0.51 -0.35 -0.18  0.11     5241     3305    1
#> pred[4: 5] -0.70 0.37 -1.43 -0.94 -0.71 -0.45  0.01     2615     2737    1
#> 
#> ---------------------------------------------------------------------- Study: 5 ---- 
#> 
#>             mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[5: 4] -0.57 0.35 -1.25 -0.80 -0.56 -0.33  0.12     2455     2826    1
#> pred[5: 1] -0.03 0.53 -1.05 -0.38 -0.04  0.33  1.04     2141     2659    1
#> pred[5: 2] -1.84 0.58 -2.95 -2.24 -1.85 -1.45 -0.70     2219     2457    1
#> pred[5: 3] -0.52 0.30 -1.11 -0.71 -0.52 -0.32  0.09     5196     3264    1
#> pred[5: 5] -0.87 0.41 -1.68 -1.14 -0.86 -0.59 -0.09     2708     3075    1
#> 
#> ---------------------------------------------------------------------- Study: 6 ---- 
#> 
#>             mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[6: 4] -2.20 0.18 -2.54 -2.32 -2.20 -2.08 -1.85     2978     3062    1
#> pred[6: 1] -1.66 0.52 -2.66 -2.01 -1.66 -1.31 -0.66     1536     2365    1
#> pred[6: 2] -3.48 0.56 -4.55 -3.86 -3.47 -3.09 -2.43     1652     2716    1
#> pred[6: 3] -2.15 0.37 -2.89 -2.40 -2.15 -1.90 -1.43     2192     2486    1
#> pred[6: 5] -2.50 0.17 -2.84 -2.62 -2.50 -2.38 -2.17     5107     2797    1
#> 
#> ---------------------------------------------------------------------- Study: 7 ---- 
#> 
#>             mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[7: 4] -1.80 0.18 -2.15 -1.92 -1.80 -1.68 -1.46     3338     2985    1
#> pred[7: 1] -1.26 0.51 -2.25 -1.60 -1.26 -0.92 -0.25     1542     2379    1
#> pred[7: 2] -3.08 0.55 -4.14 -3.46 -3.07 -2.69 -2.03     1657     2473    1
#> pred[7: 3] -1.75 0.37 -2.47 -2.00 -1.75 -1.49 -1.02     2156     2604    1
#> pred[7: 5] -2.10 0.20 -2.49 -2.23 -2.10 -1.96 -1.71     4689     3469    1
plot(arm_pred_FE_studies)

We can also produce treatment rankings, rank probabilities, and cumulative rank probabilities.

(arm_ranks <- posterior_ranks(arm_fit_FE))
#>         mean   sd 2.5% 25% 50% 75% 97.5% Bulk_ESS Tail_ESS Rhat
#> rank[4] 3.49 0.70    2   3   3   4     5     2073       NA    1
#> rank[1] 4.65 0.76    2   5   5   5     5     2172       NA    1
#> rank[2] 1.05 0.28    1   1   1   1     2     2948     3006    1
#> rank[3] 3.53 0.92    2   3   4   4     5     2867       NA    1
#> rank[5] 2.27 0.66    1   2   2   2     4     2558     2626    1
plot(arm_ranks)

(arm_rankprobs <- posterior_rank_probs(arm_fit_FE))
#>      p_rank[1] p_rank[2] p_rank[3] p_rank[4] p_rank[5]
#> d[4]      0.00      0.04      0.51      0.37      0.08
#> d[1]      0.00      0.04      0.06      0.12      0.79
#> d[2]      0.96      0.04      0.01      0.00      0.00
#> d[3]      0.00      0.16      0.25      0.46      0.12
#> d[5]      0.04      0.72      0.18      0.05      0.01
plot(arm_rankprobs)

(arm_cumrankprobs <- posterior_rank_probs(arm_fit_FE, cumulative = TRUE))
#>      p_rank[1] p_rank[2] p_rank[3] p_rank[4] p_rank[5]
#> d[4]      0.00      0.04      0.55      0.92         1
#> d[1]      0.00      0.04      0.10      0.21         1
#> d[2]      0.96      0.99      1.00      1.00         1
#> d[3]      0.00      0.17      0.42      0.88         1
#> d[5]      0.04      0.76      0.94      0.99         1
plot(arm_cumrankprobs)

Analysis of contrast-based data

We now perform an analysis using the contrast-based data (mean differences and standard errors).

Setting up the network

With contrast-level data giving the mean difference in off-time reduction (diff) and standard error (se_diff), we use the function set_agd_contrast() to set up the network.

contr_net <- set_agd_contrast(parkinsons, 
                              study = studyn,
                              trt = trtn,
                              y = diff, 
                              se = se_diff,
                              sample_size = n)
contr_net
#> A network with 7 AgD studies (contrast-based).
#> 
#> -------------------------------------------------- AgD studies (contrast-based) ---- 
#>  Study Treatment arms
#>  1     2: 1 | 3      
#>  2     2: 1 | 2      
#>  3     3: 4 | 1 | 2  
#>  4     2: 4 | 3      
#>  5     2: 4 | 3      
#>  6     2: 4 | 5      
#>  7     2: 4 | 5      
#> 
#>  Outcome type: continuous
#> ------------------------------------------------------------------------------------
#> Total number of treatments: 5
#> Total number of studies: 7
#> Reference treatment is: 4
#> Network is connected

The sample_size argument is optional, but enables the nodes to be weighted by sample size in the network plot.

Plot the network structure.

plot(contr_net, weight_edges = TRUE, weight_nodes = TRUE)

Meta-analysis models

We fit both fixed effect (FE) and random effects (RE) models.

Fixed effect meta-analysis

First, we fit a fixed effect model using the nma() function with trt_effects = "fixed". We use \(\mathrm{N}(0, 100^2)\) prior distributions for the treatment effects \(d_k\). 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.

The model is fitted using the nma() function.

contr_fit_FE <- nma(contr_net, 
                    trt_effects = "fixed",
                    prior_trt = normal(scale = 100))
#> Note: Setting "4" as the network reference treatment.

Basic parameter summaries are given by the print() method:

contr_fit_FE
#> A fixed effects NMA with a normal likelihood (identity link).
#> Inference for Stan model: normal.
#> 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 Rhat
#> d[1]  0.54    0.01 0.48 -0.40  0.21  0.54  0.85  1.48  2216    1
#> d[2] -1.27    0.01 0.52 -2.30 -1.61 -1.27 -0.93 -0.25  2325    1
#> d[3]  0.07    0.01 0.33 -0.58 -0.16  0.06  0.29  0.74  3070    1
#> d[5] -0.30    0.00 0.21 -0.71 -0.44 -0.30 -0.16  0.12  3359    1
#> lp__ -3.16    0.03 1.46 -6.87 -3.86 -2.82 -2.09 -1.38  1762    1
#> 
#> Samples were drawn using NUTS(diag_e) at Wed Mar  6 13:23:51 2024.
#> 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).

The prior and posterior distributions can be compared visually using the plot_prior_posterior() function:

plot_prior_posterior(contr_fit_FE)

Random effects meta-analysis

We now fit a random effects model using the nma() function with trt_effects = "random". Again, we use \(\mathrm{N}(0, 100^2)\) prior distributions for the treatment effects \(d_k\), and we additionally use a \(\textrm{half-N}(5^2)\) prior for the 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.

Fitting the RE model

contr_fit_RE <- nma(contr_net, 
                    seed = 1150676438,
                    trt_effects = "random",
                    prior_trt = normal(scale = 100),
                    prior_het = half_normal(scale = 5),
                    adapt_delta = 0.99)
#> Note: Setting "4" as the network reference treatment.
#> Warning: There were 1 divergent transitions after warmup. See
#> https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
#> to find out why this is a problem and how to eliminate them.
#> Warning: Examine the pairs() plot to diagnose sampling problems

We do see a small number of divergent transition errors, which cannot simply be removed by increasing the value of the adapt_delta argument (by default set to 0.95 for RE models). To diagnose, we use the pairs() method to investigate where in the posterior distribution these divergences are happening (indicated by red crosses):

pairs(contr_fit_RE, pars = c("d[3]", "delta[4: 4 vs. 3]", "tau"))
#> Warning: New theme missing the following elements: axis.text.theta, axis.text.r,
#> axis.ticks.theta, axis.ticks.r, axis.minor.ticks.x.top, axis.minor.ticks.x.bottom,
#> axis.minor.ticks.y.left, axis.minor.ticks.y.right, axis.minor.ticks.theta, axis.minor.ticks.r,
#> axis.ticks.length.theta, axis.ticks.length.r, axis.minor.ticks.length,
#> axis.minor.ticks.length.x, axis.minor.ticks.length.x.top, axis.minor.ticks.length.x.bottom,
#> axis.minor.ticks.length.y, axis.minor.ticks.length.y.left, axis.minor.ticks.length.y.right,
#> axis.minor.ticks.length.theta, axis.minor.ticks.length.r, axis.line.theta, axis.line.r,
#> legend.key.spacing, legend.key.spacing.x, legend.key.spacing.y, legend.frame, legend.ticks,
#> legend.ticks.length, legend.axis.line, legend.text.position, legend.title.position,
#> legend.position.inside, legend.byrow, legend.justification.top, legend.justification.bottom,
#> legend.justification.left, legend.justification.right, legend.justification.inside,
#> legend.location, plot.tag.location

The divergent transitions occur in the upper tail of the heterogeneity standard deviation. In this case, with only a small number of studies, there is not very much information to estimate the heterogeneity standard deviation and the prior distribution may be too heavy-tailed. We could consider a more informative prior distribution for the heterogeneity variance to aid estimation.

Basic parameter summaries are given by the print() method:

contr_fit_RE
#> A random effects NMA with a normal likelihood (identity link).
#> Inference for Stan model: normal.
#> 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 Rhat
#> d[1]  0.51    0.01 0.61  -0.69   0.13  0.50  0.89  1.71  2022    1
#> d[2] -1.33    0.01 0.68  -2.74  -1.74 -1.34 -0.90 -0.04  2369    1
#> d[3]  0.04    0.01 0.45  -0.86  -0.24  0.04  0.31  0.90  2730    1
#> d[5] -0.31    0.01 0.39  -1.09  -0.51 -0.31 -0.10  0.51  1861    1
#> lp__ -8.38    0.08 2.83 -14.87 -10.07 -8.13 -6.32 -3.71  1338    1
#> tau   0.37    0.01 0.38   0.01   0.12  0.27  0.50  1.29   982    1
#> 
#> Samples were drawn using NUTS(diag_e) at Wed Mar  6 13:23:57 2024.
#> 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 relative effects \(\delta_{jk}\) are hidden, but could be examined by changing the pars argument:

# Not run
print(contr_fit_RE, pars = c("d", "delta"))

The prior and posterior distributions can be compared visually using the plot_prior_posterior() function:

plot_prior_posterior(contr_fit_RE)

Model comparison

Model fit can be checked using the dic() function:

(contr_dic_FE <- dic(contr_fit_FE))
#> Residual deviance: 6.3 (on 8 data points)
#>                pD: 4
#>               DIC: 10.4
(contr_dic_RE <- dic(contr_fit_RE))
#> Residual deviance: 6.7 (on 8 data points)
#>                pD: 5.5
#>               DIC: 12.1

Both models fit the data well, having posterior mean residual deviance close to the number of data points. The DIC is similar between models, so we choose the FE model based on parsimony.

We can also examine the residual deviance contributions with the corresponding plot() method.

plot(contr_dic_FE)

plot(contr_dic_RE)

Further results

For comparison with Dias et al. (2011), we can produce relative effects against placebo using the relative_effects() function with trt_ref = 1:

(contr_releff_FE <- relative_effects(contr_fit_FE, trt_ref = 1))
#>       mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d[4] -0.54 0.48 -1.48 -0.85 -0.54 -0.21  0.40     2233     2368    1
#> d[2] -1.80 0.33 -2.45 -2.03 -1.80 -1.59 -1.16     5522     3140    1
#> d[3] -0.47 0.47 -1.42 -0.78 -0.47 -0.16  0.45     3080     3086    1
#> d[5] -0.84 0.52 -1.88 -1.18 -0.84 -0.49  0.17     2381     2347    1
plot(contr_releff_FE, ref_line = 0)

(contr_releff_RE <- relative_effects(contr_fit_RE, trt_ref = 1))
#>       mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d[4] -0.51 0.61 -1.71 -0.89 -0.50 -0.13  0.69     2200     1943    1
#> d[2] -1.84 0.49 -2.81 -2.12 -1.83 -1.55 -0.90     3668     2552    1
#> d[3] -0.47 0.64 -1.73 -0.86 -0.48 -0.08  0.80     3062     2332    1
#> d[5] -0.82 0.74 -2.25 -1.27 -0.82 -0.38  0.63     1967     1595    1
plot(contr_releff_RE, ref_line = 0)

Following Dias et al. (2011), we produce absolute predictions of the mean off-time reduction on each treatment assuming a Normal distribution for the outcomes on treatment 1 (placebo) with mean \(-0.73\) and precision \(21\). We use the predict() method, where the baseline argument takes a distr() distribution object with which we specify the corresponding Normal distribution, and we specify trt_ref = 1 to indicate that the baseline distribution corresponds to treatment 1. (Strictly speaking, type = "response" is unnecessary here, since the identity link function was used.)

contr_pred_FE <- predict(contr_fit_FE, 
                       baseline = distr(qnorm, mean = -0.73, sd = 21^-0.5),
                       type = "response",
                       trt_ref = 1)
contr_pred_FE
#>          mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[4] -1.27 0.52 -2.31 -1.61 -1.27 -0.92 -0.24     2393     2512    1
#> pred[1] -0.73 0.22 -1.16 -0.88 -0.73 -0.58 -0.30     3988     3629    1
#> pred[2] -2.53 0.39 -3.33 -2.80 -2.53 -2.27 -1.77     4851     3791    1
#> pred[3] -1.20 0.52 -2.23 -1.56 -1.20 -0.84 -0.18     3333     3328    1
#> pred[5] -1.57 0.56 -2.69 -1.94 -1.57 -1.19 -0.51     2506     2533    1
plot(contr_pred_FE)

contr_pred_RE <- predict(contr_fit_RE, 
                       baseline = distr(qnorm, mean = -0.73, sd = 21^-0.5),
                       type = "response",
                       trt_ref = 1)
contr_pred_RE
#>          mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[4] -1.24 0.65 -2.49 -1.65 -1.23 -0.85  0.06     2175     1866    1
#> pred[1] -0.73 0.22 -1.16 -0.88 -0.73 -0.59 -0.31     3715     3592    1
#> pred[2] -2.57 0.54 -3.67 -2.89 -2.57 -2.24 -1.52     3848     2953    1
#> pred[3] -1.21 0.68 -2.48 -1.63 -1.21 -0.79  0.10     2974     2324    1
#> pred[5] -1.55 0.77 -3.00 -2.03 -1.55 -1.08 -0.05     1929     1677    1
plot(contr_pred_RE)

If the baseline argument is omitted an error will be raised, as there are no study baselines estimated in this network.

# Not run
predict(contr_fit_FE, type = "response")

We can also produce treatment rankings, rank probabilities, and cumulative rank probabilities.

(contr_ranks <- posterior_ranks(contr_fit_FE))
#>         mean   sd 2.5% 25% 50% 75% 97.5% Bulk_ESS Tail_ESS Rhat
#> rank[4] 3.47 0.72    2   3   3   4     5     2606       NA    1
#> rank[1] 4.65 0.75    2   5   5   5     5     2701       NA    1
#> rank[2] 1.06 0.30    1   1   1   1     2     2204     2244    1
#> rank[3] 3.55 0.91    2   3   4   4     5     3781       NA    1
#> rank[5] 2.27 0.66    1   2   2   2     4     2718     2809    1
plot(contr_ranks)

(contr_rankprobs <- posterior_rank_probs(contr_fit_FE))
#>      p_rank[1] p_rank[2] p_rank[3] p_rank[4] p_rank[5]
#> d[4]      0.00      0.05      0.51      0.36      0.08
#> d[1]      0.00      0.04      0.06      0.12      0.79
#> d[2]      0.96      0.03      0.01      0.00      0.00
#> d[3]      0.00      0.16      0.25      0.47      0.12
#> d[5]      0.04      0.72      0.17      0.06      0.01
plot(contr_rankprobs)

(contr_cumrankprobs <- posterior_rank_probs(contr_fit_FE, cumulative = TRUE))
#>      p_rank[1] p_rank[2] p_rank[3] p_rank[4] p_rank[5]
#> d[4]      0.00      0.05      0.56      0.92         1
#> d[1]      0.00      0.04      0.10      0.21         1
#> d[2]      0.96      0.99      1.00      1.00         1
#> d[3]      0.00      0.16      0.41      0.88         1
#> d[5]      0.04      0.76      0.94      0.99         1
plot(contr_cumrankprobs)

Analysis of mixed arm-based and contrast-based data

We now perform an analysis where some studies contribute arm-based data, and other contribute contrast-based data. Replicating Dias et al. (2011), we consider arm-based data from studies 1-3, and contrast-based data from studies 4-7.

studies <- parkinsons$studyn
(parkinsons_arm <- parkinsons[studies %in% 1:3, ])
#>   studyn trtn     y    se   n  diff se_diff
#> 1      1    1 -1.22 0.504  54    NA   0.504
#> 2      1    3 -1.53 0.439  95 -0.31   0.668
#> 3      2    1 -0.70 0.282 172    NA   0.282
#> 4      2    2 -2.40 0.258 173 -1.70   0.382
#> 5      3    1 -0.30 0.505  76    NA   0.505
#> 6      3    2 -2.60 0.510  71 -2.30   0.718
#> 7      3    4 -1.20 0.478  81 -0.90   0.695
(parkinsons_contr <- parkinsons[studies %in% 4:7, ])
#>    studyn trtn     y    se   n  diff se_diff
#> 8       4    3 -0.24 0.265 128    NA   0.265
#> 9       4    4 -0.59 0.354  72 -0.35   0.442
#> 10      5    3 -0.73 0.335  80    NA   0.335
#> 11      5    4 -0.18 0.442  46  0.55   0.555
#> 12      6    4 -2.20 0.197 137    NA   0.197
#> 13      6    5 -2.50 0.190 131 -0.30   0.274
#> 14      7    4 -1.80 0.200 154    NA   0.200
#> 15      7    5 -2.10 0.250 143 -0.30   0.320

Setting up the network

We use the functions set_agd_arm() and set_agd_contrast() to set up the respective data sources within the network, and then combine together with combine_network().

mix_arm_net <- set_agd_arm(parkinsons_arm, 
                           study = studyn,
                           trt = trtn,
                           y = y, 
                           se = se,
                           sample_size = n)

mix_contr_net <- set_agd_contrast(parkinsons_contr, 
                                  study = studyn,
                                  trt = trtn,
                                  y = diff, 
                                  se = se_diff,
                                  sample_size = n)

mix_net <- combine_network(mix_arm_net, mix_contr_net)
mix_net
#> A network with 3 AgD studies (arm-based), and 4 AgD studies (contrast-based).
#> 
#> ------------------------------------------------------- AgD studies (arm-based) ---- 
#>  Study Treatment arms
#>  1     2: 1 | 3      
#>  2     2: 1 | 2      
#>  3     3: 4 | 1 | 2  
#> 
#>  Outcome type: continuous
#> -------------------------------------------------- AgD studies (contrast-based) ---- 
#>  Study Treatment arms
#>  4     2: 4 | 3      
#>  5     2: 4 | 3      
#>  6     2: 4 | 5      
#>  7     2: 4 | 5      
#> 
#>  Outcome type: continuous
#> ------------------------------------------------------------------------------------
#> Total number of treatments: 5
#> Total number of studies: 7
#> Reference treatment is: 4
#> Network is connected

The sample_size argument is optional, but enables the nodes to be weighted by sample size in the network plot.

Plot the network structure.

plot(mix_net, weight_edges = TRUE, weight_nodes = TRUE)

Meta-analysis models

We fit both fixed effect (FE) and random effects (RE) models.

Fixed effect meta-analysis

First, we fit a fixed effect model using the nma() function with trt_effects = "fixed". We use \(\mathrm{N}(0, 100^2)\) prior distributions for the treatment effects \(d_k\) and study-specific intercepts \(\mu_j\). 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.

The model is fitted using the nma() function.

mix_fit_FE <- nma(mix_net, 
                  trt_effects = "fixed",
                  prior_intercept = normal(scale = 100),
                  prior_trt = normal(scale = 100))
#> Note: Setting "4" as the network reference treatment.

Basic parameter summaries are given by the print() method:

mix_fit_FE
#> A fixed effects NMA with a normal likelihood (identity link).
#> Inference for Stan model: normal.
#> 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 Rhat
#> d[1]  0.52    0.01 0.47 -0.40  0.20  0.52  0.84  1.43  1517    1
#> d[2] -1.29    0.01 0.51 -2.32 -1.62 -1.29 -0.96 -0.29  1641    1
#> d[3]  0.05    0.01 0.32 -0.58 -0.16  0.04  0.26  0.67  2753    1
#> d[5] -0.30    0.00 0.20 -0.71 -0.44 -0.30 -0.16  0.10  2836    1
#> lp__ -4.61    0.04 1.87 -9.22 -5.57 -4.28 -3.26 -2.00  1833    1
#> 
#> Samples were drawn using NUTS(diag_e) at Wed Mar  6 13:24:12 2024.
#> 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\) are hidden, but could be examined by changing the pars argument:

# Not run
print(mix_fit_FE, pars = c("d", "mu"))

The prior and posterior distributions can be compared visually using the plot_prior_posterior() function:

Random effects meta-analysis

We now fit a random effects model using the nma() function with trt_effects = "random". Again, we use \(\mathrm{N}(0, 100^2)\) prior distributions for the treatment effects \(d_k\) and study-specific intercepts \(\mu_j\), and we additionally use a \(\textrm{half-N}(5^2)\) prior for the 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.

Fitting the RE model

mix_fit_RE <- nma(mix_net, 
                  seed = 437219664,
                  trt_effects = "random",
                  prior_intercept = normal(scale = 100),
                  prior_trt = normal(scale = 100),
                  prior_het = half_normal(scale = 5),
                  adapt_delta = 0.99)
#> Note: Setting "4" as the network reference treatment.
#> Warning: There were 2 divergent transitions after warmup. See
#> https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
#> to find out why this is a problem and how to eliminate them.
#> Warning: There were 3 transitions after warmup that exceeded the maximum treedepth. Increase max_treedepth above 10. See
#> https://mc-stan.org/misc/warnings.html#maximum-treedepth-exceeded
#> Warning: Examine the pairs() plot to diagnose sampling problems

We do see a small number of divergent transition errors, which cannot simply be removed by increasing the value of the adapt_delta argument (by default set to 0.95 for RE models). To diagnose, we use the pairs() method to investigate where in the posterior distribution these divergences are happening (indicated by red crosses):

pairs(mix_fit_RE, pars = c("d[3]", "delta[4: 4 vs. 3]", "tau"))
#> Warning: New theme missing the following elements: axis.text.theta, axis.text.r,
#> axis.ticks.theta, axis.ticks.r, axis.minor.ticks.x.top, axis.minor.ticks.x.bottom,
#> axis.minor.ticks.y.left, axis.minor.ticks.y.right, axis.minor.ticks.theta, axis.minor.ticks.r,
#> axis.ticks.length.theta, axis.ticks.length.r, axis.minor.ticks.length,
#> axis.minor.ticks.length.x, axis.minor.ticks.length.x.top, axis.minor.ticks.length.x.bottom,
#> axis.minor.ticks.length.y, axis.minor.ticks.length.y.left, axis.minor.ticks.length.y.right,
#> axis.minor.ticks.length.theta, axis.minor.ticks.length.r, axis.line.theta, axis.line.r,
#> legend.key.spacing, legend.key.spacing.x, legend.key.spacing.y, legend.frame, legend.ticks,
#> legend.ticks.length, legend.axis.line, legend.text.position, legend.title.position,
#> legend.position.inside, legend.byrow, legend.justification.top, legend.justification.bottom,
#> legend.justification.left, legend.justification.right, legend.justification.inside,
#> legend.location, plot.tag.location

The divergent transitions occur in the upper tail of the heterogeneity standard deviation. In this case, with only a small number of studies, there is not very much information to estimate the heterogeneity standard deviation and the prior distribution may be too heavy-tailed. We could consider a more informative prior distribution for the heterogeneity variance to aid estimation.

Basic parameter summaries are given by the print() method:

mix_fit_RE
#> A random effects NMA with a normal likelihood (identity link).
#> Inference for Stan model: normal.
#> 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 Rhat
#> d[1]   0.51    0.02 0.66  -0.86   0.15   0.52  0.89  1.74  1629 1.00
#> d[2]  -1.34    0.02 0.75  -2.90  -1.75  -1.32 -0.91  0.04  1537 1.00
#> d[3]   0.02    0.01 0.54  -1.05  -0.26   0.02  0.32  0.99  2249 1.00
#> d[5]  -0.30    0.01 0.43  -1.21  -0.51  -0.30 -0.09  0.61  2295 1.00
#> lp__ -10.72    0.08 3.23 -17.78 -12.71 -10.44 -8.45 -5.15  1444 1.00
#> tau    0.42    0.02 0.46   0.01   0.13   0.28  0.54  1.67   533 1.01
#> 
#> Samples were drawn using NUTS(diag_e) at Wed Mar  6 13:24:25 2024.
#> 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(mix_fit_RE, pars = c("d", "mu", "delta"))

The prior and posterior distributions can be compared visually using the plot_prior_posterior() function:

Model comparison

Model fit can be checked using the dic() function:

(mix_dic_FE <- dic(mix_fit_FE))
#> Residual deviance: 9.2 (on 11 data points)
#>                pD: 6.9
#>               DIC: 16.2
(mix_dic_RE <- dic(mix_fit_RE))
#> Residual deviance: 9.6 (on 11 data points)
#>                pD: 8.5
#>               DIC: 18.1

Both models fit the data well, having posterior mean residual deviance close to the number of data points. The DIC is similar between models, so we choose the FE model based on parsimony.

We can also examine the residual deviance contributions with the corresponding plot() method.

plot(mix_dic_FE)

plot(mix_dic_RE)

Further results

For comparison with Dias et al. (2011), we can produce relative effects against placebo using the relative_effects() function with trt_ref = 1:

(mix_releff_FE <- relative_effects(mix_fit_FE, trt_ref = 1))
#>       mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d[4] -0.52 0.47 -1.43 -0.84 -0.52 -0.20  0.40     1542     2165    1
#> d[2] -1.81 0.33 -2.47 -2.03 -1.81 -1.59 -1.17     6740     3205    1
#> d[3] -0.47 0.49 -1.41 -0.81 -0.48 -0.14  0.48     2302     2692    1
#> d[5] -0.82 0.52 -1.81 -1.17 -0.82 -0.48  0.19     1755     2427    1
plot(mix_releff_FE, ref_line = 0)

(mix_releff_RE <- relative_effects(mix_fit_RE, trt_ref = 1))
#>       mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> d[4] -0.51 0.66 -1.74 -0.89 -0.52 -0.15  0.86     1814     1594    1
#> d[2] -1.85 0.58 -2.96 -2.13 -1.85 -1.56 -0.84     4001     2109    1
#> d[3] -0.50 0.69 -1.82 -0.89 -0.50 -0.12  0.89     2965     2206    1
#> d[5] -0.81 0.79 -2.32 -1.27 -0.82 -0.38  0.82     1866     1679    1
plot(mix_releff_RE, ref_line = 0)

Following Dias et al. (2011), we produce absolute predictions of the mean off-time reduction on each treatment assuming a Normal distribution for the outcomes on treatment 1 (placebo) with mean \(-0.73\) and precision \(21\). We use the predict() method, where the baseline argument takes a distr() distribution object with which we specify the corresponding Normal distribution, and we specify trt_ref = 1 to indicate that the baseline distribution corresponds to treatment 1. (Strictly speaking, type = "response" is unnecessary here, since the identity link function was used.)

mix_pred_FE <- predict(mix_fit_FE, 
                       baseline = distr(qnorm, mean = -0.73, sd = 21^-0.5),
                       type = "response",
                       trt_ref = 1)
mix_pred_FE
#>          mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[4] -1.24 0.52 -2.24 -1.61 -1.25 -0.89 -0.22     1674     2447    1
#> pred[1] -0.72 0.22 -1.14 -0.87 -0.72 -0.58 -0.29     3556     3638    1
#> pred[2] -2.53 0.39 -3.31 -2.80 -2.53 -2.27 -1.73     4609     3417    1
#> pred[3] -1.19 0.53 -2.21 -1.57 -1.20 -0.83 -0.15     2345     2871    1
#> pred[5] -1.54 0.56 -2.65 -1.92 -1.57 -1.15 -0.46     1866     2434    1
plot(mix_pred_FE)

mix_pred_RE <- predict(mix_fit_RE, 
                       baseline = distr(qnorm, mean = -0.73, sd = 21^-0.5),
                       type = "response",
                       trt_ref = 1)
mix_pred_RE
#>          mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[4] -1.24 0.69 -2.54 -1.66 -1.25 -0.85  0.17     1920     1707    1
#> pred[1] -0.73 0.22 -1.17 -0.88 -0.73 -0.58 -0.31     4051     3817    1
#> pred[2] -2.58 0.62 -3.75 -2.91 -2.57 -2.24 -1.52     3816     2164    1
#> pred[3] -1.22 0.73 -2.60 -1.65 -1.22 -0.82  0.18     3082     2578    1
#> pred[5] -1.54 0.82 -3.08 -2.03 -1.56 -1.07  0.09     1963     1709    1
plot(mix_pred_RE)

If the baseline argument is omitted, predictions of mean off-time reduction will be produced for every arm-based study in the network based on their estimated baseline response \(\mu_j\):

mix_pred_FE_studies <- predict(mix_fit_FE, type = "response")
mix_pred_FE_studies
#> ---------------------------------------------------------------------- Study: 1 ---- 
#> 
#>             mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[1: 4] -1.64 0.45 -2.53 -1.95 -1.64 -1.34 -0.75     2293     2590    1
#> pred[1: 1] -1.12 0.43 -1.98 -1.41 -1.12 -0.84 -0.27     3653     3131    1
#> pred[1: 2] -2.93 0.52 -3.94 -3.29 -2.93 -2.58 -1.91     3566     2944    1
#> pred[1: 3] -1.60 0.40 -2.38 -1.87 -1.59 -1.34 -0.81     3534     2565    1
#> pred[1: 5] -1.94 0.49 -2.91 -2.27 -1.95 -1.61 -0.96     2424     2445    1
#> 
#> ---------------------------------------------------------------------- Study: 2 ---- 
#> 
#>             mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[2: 4] -1.16 0.50 -2.13 -1.50 -1.17 -0.83 -0.15     1495     2084    1
#> pred[2: 1] -0.64 0.26 -1.14 -0.82 -0.64 -0.47 -0.11     5710     3476    1
#> pred[2: 2] -2.45 0.24 -2.91 -2.61 -2.45 -2.29 -1.97     5095     3528    1
#> pred[2: 3] -1.11 0.53 -2.13 -1.47 -1.12 -0.76 -0.08     2107     2349    1
#> pred[2: 5] -1.46 0.54 -2.53 -1.83 -1.46 -1.09 -0.39     1693     2292    1
#> 
#> ---------------------------------------------------------------------- Study: 3 ---- 
#> 
#>             mean   sd  2.5%   25%   50%   75% 97.5% Bulk_ESS Tail_ESS Rhat
#> pred[3: 4] -1.11 0.41 -1.89 -1.38 -1.12 -0.85 -0.27     1920     2608    1
#> pred[3: 1] -0.59 0.37 -1.32 -0.84 -0.58 -0.34  0.11     4354     3105    1
#> pred[3: 2] -2.40 0.38 -3.16 -2.65 -2.41 -2.15 -1.63     4350     3240    1
#> pred[3: 3] -1.07 0.47 -1.98 -1.38 -1.07 -0.75 -0.15     2796     2796    1
#> pred[3: 5] -1.41 0.46 -2.29 -1.72 -1.42 -1.11 -0.50     2130     2646    1
plot(mix_pred_FE_studies)

We can also produce treatment rankings, rank probabilities, and cumulative rank probabilities.

(mix_ranks <- posterior_ranks(mix_fit_FE))
#>         mean   sd 2.5% 25% 50% 75% 97.5% Bulk_ESS Tail_ESS Rhat
#> rank[4] 3.50 0.71    2   3   3   4     5     2062       NA    1
#> rank[1] 4.63 0.78    2   5   5   5     5     1963       NA    1
#> rank[2] 1.05 0.26    1   1   1   1     2     2608     2767    1
#> rank[3] 3.54 0.92    2   3   4   4     5     3151       NA    1
#> rank[5] 2.28 0.66    1   2   2   2     4     2615     2603    1
plot(mix_ranks)

(mix_rankprobs <- posterior_rank_probs(mix_fit_FE))
#>      p_rank[1] p_rank[2] p_rank[3] p_rank[4] p_rank[5]
#> d[4]      0.00      0.04      0.49      0.38      0.08
#> d[1]      0.00      0.04      0.06      0.12      0.78
#> d[2]      0.96      0.03      0.01      0.00      0.00
#> d[3]      0.00      0.16      0.25      0.45      0.13
#> d[5]      0.04      0.72      0.19      0.05      0.01
plot(mix_rankprobs)

(mix_cumrankprobs <- posterior_rank_probs(mix_fit_FE, cumulative = TRUE))
#>      p_rank[1] p_rank[2] p_rank[3] p_rank[4] p_rank[5]
#> d[4]      0.00      0.04      0.54      0.92         1
#> d[1]      0.00      0.04      0.10      0.22         1
#> d[2]      0.96      0.99      1.00      1.00         1
#> d[3]      0.00      0.17      0.42      0.87         1
#> d[5]      0.04      0.75      0.94      0.99         1
plot(mix_cumrankprobs)

References

Dias, S., N. J. Welton, A. J. Sutton, and A. E. Ades. 2011. NICE DSU Technical Support Document 2: A Generalised Linear Modelling Framework for Pair-Wise and Network Meta-Analysis of Randomised Controlled Trials.” National Institute for Health and Care Excellence. https://www.sheffield.ac.uk/nice-dsu.