This vignette demonstrates how to use `MBNMAtime`

to perform meta-analysis of studies with multiple follow-up measurements in order to account for time-course relationships within single or multiple treatment comparisons. This can be performed by conducting Model-Based (Network) Meta-Analysis (MBNMA) to pool relative treatment effects.

Including all available follow-up measurements within a study makes use of all the available evidence in a way that maintains connectivity between treatments and explains how the response of the treatment changes over time, thus accounting for heterogeneity and inconsistency that may be present from “lumping” together different time points in a standard Network Meta-Analysis (NMA). All models and analyses are implemented in a Bayesian framework, following an extension of the standard NMA methodology presented by (Lu and Ades 2004) and are run in JAGS *(version 4.3.0 or later is required)* (JAGS Computer Program 2017). For full details of time-course MBNMA methodology see Pedder et al. (2019), and a simulation study exploring the statistical properties of the method is reported in Pedder et al. (2020).

This package has been developed alongside `MBNMAdose`

, a package that allows users to perform dose-response MBNMA to allow for modelling of dose-response relationships between different agents within a network. However, *they should not be loaded into R at the same time* as there are a number of functions with shared names that perform similar tasks yet are specific to dealing with either time-course *or* dose-response data.

Within the vignette, some models have not been evaluated, or have been run with fewer iterations than would be necessary to achieve convergence and produce valid results in practice. This has been done to speed up computation and rendering of the vignette.

Functions within `MBNMAtime`

follow a clear pattern of use:

- Load your data into the correct format using
`mb.network()`

- Specify a suitable time-course function and analyse your data using
`mb.run()`

- Test for consistency using functions like
`mb.nodesplit()`

- Examine model results using forest plots and treatment rankings
- Use your model to predict responses using
`predict()`

At each of these stages there are a number of informative graphs that can be generated to help understand the data and make decisions regarding model fitting.

`osteopain`

is from a systematic review of treatments for pain in osteoarthritis, used previously in Pedder et al. (2019). The outcome is pain measured on a continuous scale, and aggregate data responses correspond to the mean WOMAC pain score at different follow-up times. The dataset includes 30 Randomised-Controlled Trials (RCTs), comparing 29 different treatments (including placebo). `osteopain`

is a data frame in long format (one row per time point, arm and study), with the variables `studyID`

, `time`

, `y`

, `se`

, `treatment`

and `arm`

.

studyID | time | y | se | treatment | arm | treatname |
---|---|---|---|---|---|---|

Baerwald 2010 | 0 | 6.55 | 0.09 | Pl_0 | 1 | Placebo_0 |

Baerwald 2010 | 2 | 5.40 | 0.09 | Pl_0 | 1 | Placebo_0 |

Baerwald 2010 | 6 | 4.97 | 0.10 | Pl_0 | 1 | Placebo_0 |

Baerwald 2010 | 13 | 4.75 | 0.11 | Pl_0 | 1 | Placebo_0 |

Baerwald 2010 | 0 | 6.40 | 0.13 | Na_1000 | 2 | Naproxen_1000 |

Baerwald 2010 | 2 | 4.03 | 0.13 | Na_1000 | 2 | Naproxen_1000 |

`alog_pcfb`

is from a systematic review of Randomised-Controlled Trials (RCTs) comparing different doses of alogliptin with placebo (Langford et al. 2016). The systematic review was simply performed and was intended to provide data to illustrate a statistical methodology rather than for clinical inference. Alogliptin is a treatment aimed at reducing blood glucose concentration in type II diabetes. The outcome is continuous, and aggregate data responses correspond to the mean change in HbA1c from baseline to follow-up in studies of at least 12 weeks follow-up. The dataset includes 14 Randomised-Controlled Trials (RCTs), comparing 5 different doses of alogliptin with placebo (6 different treatments in total). `alog_pcfb`

is a data frame in long format (one row per time point, arm and study), with the variables `studyID`

, `clinicaltrialGov_ID`

, `agent`

, `dose`

, `treatment`

, `time`

, `y`

, `se`

, and `N`

.

studyID | clinicaltrialGov_ID | agent | dose | treatment | time | y | se | N |
---|---|---|---|---|---|---|---|---|

1 | NCT01263470 | alogliptin | 0.00 | placebo | 2 | 0.00 | 0.02 | 75 |

1 | NCT01263470 | alogliptin | 6.25 | alog_6.25 | 2 | -0.16 | 0.02 | 79 |

1 | NCT01263470 | alogliptin | 12.50 | alog_12.5 | 2 | -0.17 | 0.02 | 84 |

1 | NCT01263470 | alogliptin | 25.00 | alog_25 | 2 | -0.16 | 0.02 | 79 |

1 | NCT01263470 | alogliptin | 50.00 | alog_50 | 2 | -0.15 | 0.02 | 79 |

1 | NCT01263470 | alogliptin | 0.00 | placebo | 4 | -0.01 | 0.04 | 75 |

A dataset from a systematic review of Randomised-Controlled Trials (RCTs) for maintenance treatment of moderate to severe chronic obstructive pulmonary disease (COPD) (Karabis et al. 2013). Data are extracted from (Tallarita, De lorio, and Baio 2019). SEs were imputed for three studies, and number of patients randomised were imputed for one study (LAS 39) in which they were missing, using the median standard deviation calculated from other studies in the dataset. The outcome is trough Forced Expiratory Volume in 1 second (FEV1), measured in litres and reported in each study arm as mean change from baseline to follow-up. The dataset includes 13 RCTs, comparing 2 treatments (Tiotropium and Aclidinium) and placebo. `copd`

is a data frame in long format (one row per observation, arm and study), with the variables `studyID`

, `time`

, `y`

, `se`

, `treatment`

, and `n`

.

studyID | time | y | se | treatment | n |
---|---|---|---|---|---|

ACCORD I | 1 | -0.01 | 0.01 | Placebo | 187 |

ACCORD I | 4 | -0.01 | 0.01 | Placebo | 187 |

ACCORD I | 8 | -0.01 | 0.01 | Placebo | 187 |

ACCORD I | 12 | -0.02 | 0.01 | Placebo | 187 |

ACCORD I | 1 | 0.10 | 0.01 | Aclidinium | 190 |

ACCORD I | 4 | 0.11 | 0.01 | Aclidinium | 190 |

`obesityBW_CFB`

is from a systematic review of pharmacological treatments for obesity. The outcome measured is change from baseline in body weight (kg) at different follow-up times. 35 RCTs are included that investigate 26 different treatments (16 agents/agent combinations compared at different doses). `obesityBW_CFB`

is a dataset in long format (one row per time point, arm and study), with the variables `studyID`

, `time`

, `y`

, `se`

, `N`

, `treatment`

, `arm`

, `treatname`

, `agent`

and `class`

.

`class`

is the class of a particular `agent`

(e.g. Lipase inhibitor)

studyID | time | y | se | N | treatment | treatname | agent | class | |
---|---|---|---|---|---|---|---|---|---|

27 | Apfelbaum 1999 | 4.35 | -1.00 | 0.39 | 78 | plac | placebo | placebo | Placebo |

28 | Apfelbaum 1999 | 4.35 | -1.59 | 0.38 | 81 | sibu_10MG | sibutramine 10MG | sibutramine | SNRI |

29 | Apfelbaum 1999 | 8.70 | -1.59 | 0.40 | 78 | plac | placebo | placebo | Placebo |

30 | Apfelbaum 1999 | 8.70 | -3.01 | 0.39 | 81 | sibu_10MG | sibutramine 10MG | sibutramine | SNRI |

31 | Apfelbaum 1999 | 13.04 | -2.25 | 0.41 | 78 | plac | placebo | placebo | Placebo |

32 | Apfelbaum 1999 | 13.04 | -4.76 | 0.40 | 81 | sibu_10MG | sibutramine 10MG | sibutramine | SNRI |

`goutSUA_CFB`

is from a systematic review of interventions for lowering Serum Uric Acid (SUA) concentration in patients with gout *[not published previously]*. The outcome is continuous, and aggregate data responses correspond to the mean change from baseline in SUA in mg/dL at different follow-up times. The dataset includes 28 RCTs, comparing 41 treatments (8 agents compared at different doses). `goutSUA_CFB`

is a data frame in long format (one row per arm and study), with the variables `studyID`

, `time`

, `y`

, `se`

, `treatment`

, `arm`

, `class`

and `treatname`

.

studyID | time | y | se | treatment | treatname | class |
---|---|---|---|---|---|---|

1102 | 1 | 0.07 | 0.25 | RDEA_100 | RDEA594_100 | RDEA |

1102 | 1 | 0.02 | 0.18 | RDEA_200 | RDEA594_200 | RDEA |

1102 | 1 | 0.06 | 0.25 | RDEA_400 | RDEA594_400 | RDEA |

1102 | 2 | -0.53 | 0.25 | RDEA_100 | RDEA594_100 | RDEA |

1102 | 2 | -1.37 | 0.18 | RDEA_200 | RDEA594_200 | RDEA |

1102 | 2 | -1.73 | 0.25 | RDEA_400 | RDEA594_400 | RDEA |

Before embarking on an analysis, the first step is to have a look at the raw data. Two features (network connectivity and time-course relationship) are particularly important for MBNMA. To investigate these we must first get our dataset into the right format for the package. We can do this using `mb.network()`

. This requires specifying the desired treatment to use for the network reference treatment, though one will automatically be specified if not given.

```
# Using the pain dataset
network.pain <- mb.network(osteopain, reference = "Pl_0")
print(network.pain)
#> description :
#> [1] "Network"
#>
#> data.ab :
#> # A tibble: 417 x 10
#> # Groups: studyID, time [116]
#> studyID time y se treatment arm treatname fupcount fups narm
#> <fct> <dbl> <dbl> <dbl> <dbl> <int> <fct> <int> <int> <int>
#> 1 Baerwald~ 0 6.55 0.0861 1 1 Placebo_0 1 4 3
#> 2 Baerwald~ 0 6.40 0.127 15 2 Naproxen_1~ 1 4 3
#> 3 Baerwald~ 0 6.62 0.0900 16 3 Naproxcino~ 1 4 3
#> 4 Baerwald~ 2 5.40 0.0932 1 1 Placebo_0 2 4 3
#> 5 Baerwald~ 2 4.03 0.133 15 2 Naproxen_1~ 2 4 3
#> 6 Baerwald~ 2 4.43 0.0926 16 3 Naproxcino~ 2 4 3
#> 7 Baerwald~ 6 4.97 0.0997 1 1 Placebo_0 3 4 3
#> 8 Baerwald~ 6 3.72 0.139 15 2 Naproxen_1~ 3 4 3
#> 9 Baerwald~ 6 4.08 0.0965 16 3 Naproxcino~ 3 4 3
#> 10 Baerwald~ 13 4.75 0.109 1 1 Placebo_0 4 4 3
#> # ... with 407 more rows
#>
#> treatments :
#> [1] "Pl_0" "Ce_100" "Ce_200" "Ce_400" "Du_90" "Et_10" "Et_30"
#> [8] "Et_5" "Et_60" "Et_90" "Lu_100" "Lu_200" "Lu_400" "Lu_NA"
#> [15] "Na_1000" "Na_1500" "Na_250" "Na_750" "Ox_44" "Ro_12" "Ro_125"
#> [22] "Ro_25" "Tr_100" "Tr_200" "Tr_300" "Tr_400" "Va_10" "Va_20"
#> [29] "Va_5"
```

This takes a dataset with the columns:

`studyID`

Study identifiers`time`

Numeric data indicating follow-up times`y`

Numeric data indicating the mean response for a given observation`se`

Numeric data indicating the standard error for a given observation`treatment`

Treatment identifiers (can be numeric, factor or character)`class`

An optional column indicating a particular class that may be shared by several treatments.`N`

An optional column indicating the number of participants used to calculate the response at a given observation.

Additional columns can be included in the dataset. These will simply be added to the `mb.network`

object, though will not affect the classification of the data.

`mb.network`

then performs the following checks on the data:

- The dataset has the required column names
- There are no missing values
- All standard errors (SE) are positive
- Observations are made at the same time points in all arms of a study (i.e. the data are balanced)
- Class labels are consistent within each treatment
- Studies have at least two arms

And finally it converts the data into an object of `class("mb.network")`

, which contains indices for study arms and follow-up measurements, and generates numeric values for treatments and classes. By convention, treatments are numbered alphabetically, though if the original data for treatments is provided as a factor then the factor codes will be used. This then contains all the necessary information for subsequent `MBNMAtime`

functions.

Looking at how the evidence in the network is connected and identifying which studies compare which treatments helps to understand which treatment effects can be estimated and what information will be helping to inform those estimates. A network plot can be generated which shows which treatments have been compared in head-to-head trials. Typically the thickness of connecting lines (“edges”) is proportional to the number of studies that make a particular comparison and the size of treatment nodes (“vertices”) is proportional to the total number of patients in the network who were randomised to a given treatment (provided `N`

is included as a variable in the original dataset for `mb.network()`

).

In `MBNMAtime`

these plots are generated using `igraph`

, and can be plotted by calling `plot()`

. The generated plots are objects of `class("igraph")`

meaning that, in addition to the options specified in `plot()`

, various `igraph`

functions can be used to make more detailed edits to them.

```
# Prepare data using the alogliptin dataset
network.alog <- mb.network(alog_pcfb, reference = "placebo")
# Plot network
plot(network.alog)
```

Within these network plots, treatments are automatically aligned in a circle (as the default) and can be tidied by shifting the label distance away from the nodes.

```
# Draw network plot in a star layout using the gout dataset
network.gout <- mb.network(goutSUA_CFB, reference = "Plac")
plot(network.gout, layout=igraph::as_star(), label.distance = 5)
```

This command returns a warning stating that some treatments are not connected to the network reference treatment through any pathway of head-to-head evidence. The nodes that are coloured white represent these treatments. This means that it will not be possible to estimate relative effects for these treatments versus the network reference treatment (or any treatments connected to it). Several options exist to allow for inclusion of these treatments in an analysis which we will discuss in more detail later, but one approach is to assume a shared effect among treatments within the same class/agent. We can generate a network plot at the class level to examine this more closely, and can see that the network is connected at the class level.

It is also possible to plot a network at the treatment level but to colour the treatments by the class that they belong to.

In order to consider which functional forms may be appropriate for modelling the time-course relationship, it is important to look at the responses in each arm plotted over time. This can easily be done using the `timeplot()`

function on an object of `class("mb.network")`

```
# Prepare data using the pain dataset
network.pain <- mb.network(osteopain, reference="Pl_0")
# Draw plot of raw study responses over time
timeplot(network.pain)
```

As the mean response for all treatments shows a rapid reduction in pain score followed by a levelling out after 2-5 weeks, an exponential decay time-course function might be a reasonable fit for this dataset. More complex alternatives could be Emax models (with or without a Hill parameter), fractional polynomials or a spline function.

Responses can also be plotted grouped by class rather than by treatment, and the relative effects between each treatment/class can be plotted instead of the absolute treatment responses. Since the MBNMA framework models the time-course on relative effects (Pedder et al. 2019) this can in fact make interpretation of the plots easier with regards to identifying a best-fitting time-course function.

```
# Draw plot of within-study relative effects over time grouped by class
network.gout <- mb.network(goutSUA_CFBcomb)
timeplot(network.gout, level="class", plotby="rel")
```

Many of the profiles here appear to be quite different within the same class, which would suggest modelling class effects may be inappropriate for this dataset.

`mb.run()`

MBNMA models are fitted using `mb.run()`

. This can just as easily be performed on datasets with many different treatments (network meta-analysis) as it can on datasets comparing only two treatments (pairwise meta-analysis) - the syntax is the same.

An object or `class("mb.network")`

must be provided as the data for `mb.run()`

. The key arguments within `mb.run()`

involve specifying the functional form used to model the time-course, and the time-course parameters that comprise that functional form.

A number of different time-course functions can be fitted within `MBNMAtime`

and the specific forms of the time-course parameters are defined by arguments within these functions, and this allows for a wide variety of parameterizations and time-course shapes. For further details check the help files for each function (e.g. `?tloglin()`

). These functions, are then used as inputs for the `fun`

argument in `mb.run()`

.

`tloglin()`

- Log-linear function`texp()`

- Exponential function`temax()`

- Emax function`tpoly()`

- Polynomial function (e.g. linear, quadratic)`tfpoly()`

- Fractional polynomial function, as proposed previously for time-course NMA by Jansen (2015).`tspline()`

- Spline functions (includes B-splines, restricted cubic splines, natural splines and piecewise linear splines)`tuser()`

- A time-course function that can be explicitly defined by the user

Time-course parameters within time-course functions are each defined by two arguments:

`pool`

is used to define the approach used for the pooling of a given time-course parameter and can either of:

`"rel"`

indicates that relative effects should be pooled for this time-course parameter. This preserves randomisation within included studies, are likely to vary less between studies (only due to effect modification), and allow for testing of consistency between direct and indirect evidence. Pooling follows the general approach for Network Meta-Analysis proposed by Lu and Ades (2004).`"abs"`

indicates that study arms should be pooled across the whole network for this time-course parameter**independently of assigned treatment**. This implies using a single absolute value across the network for this time-course parameter, and may therefore be making strong assumptions of similarity.

`method`

is used to define the model used for meta-analysis for a given time-course parameter and can take either of:

`"common"`

implies that all studies estimate the same true effect (sometimes called a “fixed effect” meta-analysis)`"random"`

implies that all studies estimate a separate true effect, but that each of these true effects vary randomly around a true mean effect. This approach allows for modelling of between-study heterogeneity.

Specifying pooling relative effects on all time-course parameters would imply performing a contrast-based synthesis, whereas specifying pooling absolute effects on all of them would imply performing an arm-based synthesis. There has been substantial discussion in the literature regarding the strengths and limitations of both these approaches (Dias and Ades 2016; Hong et al. 2016; Karahalios et al. 2017).

Additional arguments within the function may also be used to specify the degree (e.g. for polynomials) or the number of knots or knot placement for splines.

`mb.run()`

returns an object of `class(c("mbnma", "rjags"))`

. `summary()`

provides summary estimates of posterior densities for different parameters in the model, with some explanation regarding the way in which the model has been defined. Estimates are automatically reported for parameters of interest depending on the model specification (unless otherwise specified in `parameters.to.save`

). Nodes that are automatically monitored (if present in the model) have the following interpretation:

If pooling is relative (e.g. `pool.1="rel"`

) for a given parameter then the named parameter (e.g. `emax`

) or a numbered `d`

parameter (e.g. `d.1`

) corresponds to the pooled relative effect for a given treatment compared to the network reference treatment for this time-course parameter.

`sd.`

followed by a named (e.g. `emax`

, `beta.1`

) is the between-study SD (heterogeneity) for relative effects, reported if pooling for a time-course parameter is relative (e.g. `pool.1="rel"`

) *and* the method for synthesis is random (e.g. `method.1="random`

).

If class effects are modelled, parameters for classes are represented by the upper case name of the time-course parameter they correspond to. For example if `class.effect=list(emax="random")`

, relative class effects will be represented by `EMAX`

. The SD of the class effect (e.g. `sd.EMAX`

, `sd.BETA.1`

) is the SD of treatments within a class for the time-course parameter they correspond to.

If pooling is absolute (e.g. `pool.1="abs"`

) for a given parameter then the named parameter (e.g. `emax`

) or a numbered `beta`

parameter (e.g. `beta.1`

) corresponds to the estimated absolute effect for this time-course parameter.

For an absolute time-course parameter if the corresponding method is common (e.g. `method.1="common"`

) the parameter corresponds to a single common parameter estimated across all studies and treatments. If the corresponding method is random (e.g. `method.1="random"`

) then parameter is a mean effect around which the study-level absolute effects vary with SD corresponding to `sd.`

followed by the named parameter (e.g. `sd.emax`

, `sd.beta.1`

).

`rho`

is the correlation coefficient for correlation between time-points. Its interpretation will differ depending on the covariance structure specified in `covar`

.

`totresdev`

is residual deviance of the model and `deviance`

is the deviance of the model. Model fit statistics for `pD`

(effective number of parameters) and `DIC`

(Deviance Information Criterion) are also reported, with an explanation as to how they have been calculated.

An example MBNMA of the alogliptin dataset using a linear time-course function and common treatment effects that pool relative effects and assumes consistency between direct and indirect evidence can be performed as follows:

```
# Run a linear time-course MBNMA
mbnma <- mb.run(network.alog, fun=tpoly(degree=1, pool.1="rel", method.1="common"))
#> module glm loaded
```

```
summary(mbnma)
#> ========================================
#> Time-course MBNMA
#> ========================================
#>
#> Time-course function: poly (degree = 1)
#> Data modelled with intercept
#>
#> beta.1 parameter
#> Pooling: relative effects
#> Method: common treatment effects
#>
#> |Treatment |Parameter | Median| 2.5%| 97.5%|
#> |:---------|:---------|-------:|-------:|-------:|
#> |placebo |d.1[1] | 0.0000| 0.0000| 0.0000|
#> |alog_6.25 |d.1[2] | -0.0365| -0.0394| -0.0337|
#> |alog_12.5 |d.1[3] | -0.0418| -0.0436| -0.0400|
#> |alog_25 |d.1[4] | -0.0449| -0.0467| -0.0431|
#> |alog_50 |d.1[5] | -0.0507| -0.0535| -0.0480|
#> |alog_100 |d.1[6] | -0.0488| -0.0663| -0.0301|
#>
#>
#>
#> Correlation between time points
#> Covariance structure: varadj
#> Rho assigned a numeric value: 0
#>
#> #### Model Fit Statistics ####
#>
#> Effective number of parameters:
#> pD (pV) calculated using the rule, pD = var(deviance)/2 = 34
#> Deviance = 403
#> Residual deviance = 1350
#> Deviance Information Criterion (DIC) = 437
```

For this model, the `d.1`

parameters correspond to the 1st polynomial coefficient, and therefore are the linear gradient of the response over time for each treatment versus `placebo`

. However, note that the residual deviance of the model is very high, suggesting (as we might expect) that this linear time-course function is a poor fit.

We may want to fit a more complex time-course function with two time-course parameters, such as an Emax function, yet limitations in the data might require that we make an assumption that one of the parameters does not vary by treatment. We can specify this by setting `pool`

to be equal to `"abs"`

for any parameters we choose.

```
# Run an Emax time-course MBNMA with two parameters
mbnma <- mb.run(network.alog, fun=temax(
pool.emax = "rel", method.emax="common",
pool.et50 = "abs", method.et50="common"
))
#> 'et50' parameters are on exponential scale to ensure they take positive values on the natural scale
```

```
summary(mbnma)
#> ========================================
#> Time-course MBNMA
#> ========================================
#>
#> Time-course function: emax
#> Data modelled with intercept
#>
#> emax parameter
#> Pooling: relative effects
#> Method: common treatment effects
#>
#> |Treatment |Parameter | Median| 2.5%| 97.5%|
#> |:---------|:---------|-------:|-------:|-------:|
#> |placebo |emax[1] | 0.0000| 0.0000| 0.0000|
#> |alog_6.25 |emax[2] | -0.6477| -0.7280| -0.5723|
#> |alog_12.5 |emax[3] | -0.8052| -0.8723| -0.7458|
#> |alog_25 |emax[4] | -0.8802| -0.9515| -0.8146|
#> |alog_50 |emax[5] | -0.9988| -1.0970| -0.9103|
#> |alog_100 |emax[6] | -0.8685| -1.1453| -0.5879|
#>
#>
#> et50 parameter
#> Pooling: absolute effects
#> Method: common treatment effects
#> Parameter modelled on exponential scale to ensure it takes positive values on the natural scale
#>
#> |Treatment |Parameter | Median| 2.5%| 97.5%|
#> |:---------|:---------|------:|------:|------:|
#> |placebo |et50 | 1.7227| 1.5664| 1.8826|
#> |alog_6.25 |et50 | 1.7227| 1.5664| 1.8826|
#> |alog_12.5 |et50 | 1.7227| 1.5664| 1.8826|
#> |alog_25 |et50 | 1.7227| 1.5664| 1.8826|
#> |alog_50 |et50 | 1.7227| 1.5664| 1.8826|
#> |alog_100 |et50 | 1.7227| 1.5664| 1.8826|
#>
#>
#>
#> Correlation between time points
#> Covariance structure: varadj
#> Rho assigned a numeric value: 0
#>
#> #### Model Fit Statistics ####
#>
#> Effective number of parameters:
#> pD (pV) calculated using the rule, pD = var(deviance)/2 = 34
#> Deviance = -439
#> Residual deviance = 508
#> Deviance Information Criterion (DIC) = -404
```

In this case, the parameters are named following the Emax function specification. `emax`

corresponds to the maximum relative effect for each treatment versus `place`

, whereas `et50`

is the log of the time at which 50% of the maximum response is achieved, **across all treatments in the network**. This assumes conditional constancy of absolute effects for this time-course parameter, which is typically a strong assumption. However, if there were limited data with which to inform this parameter (e.g. at earlier time-points) then such an assumption might be necessary, with the caveat that interpolation of response at time-points informed by this parameter may be more susceptible to bias. Further exploration of the degree of data required for reliable estimation of time-course parameters is given in Pedder et al. (2020).

`mb.run()`

Within-study correlation between time points can easily be modelled using `mb.run()`

, though this requires some additional considerations. The simplest approach is to incorporate correlation by using a variance adjustment (Jansen, Vieira, and Cope 2015). This avoids the need to use a multivariate normal likelihood (which is slow to run), and it assumes a common correlation between neighbouring time-points. This is achieved by using the argument `covar="varadj"`

, which is the default in `mb.run()`

.

There are two alternative covariance structures can be modelled, though these require fitting a multivariate normal likelihood and therefore take longer to run. `covar="CS"`

specifies fitting a Compound Symmetry covariance structure, whilst `covar="AR1"`

specifies fitting an autoregressive AR1 covariance structure to the multivariate normal likelihood used for modelling the correlation between multiple time points within a study (PennState Eberly College of Science, n.d.).

However, in addition to this, it’s also necessary to specify a value for `rho`

, and this can be assigned in one of two ways:

- Given as string representing a JAGS prior distribution (Plummer 2017), which indicates that the correlation should be estimated from the data. For example, to specify a prior that the correlation between time-points will be between 0 and 1 with equal probability you could set
`rho="dunif(0,1)"`

. - Given as a single numeric value, which indicates that the correlation should be fixed to that value. For example, this value could be estimated externally from another study using Individual Participant Data. This could also be used to run a deterministic sensitivity analysis using different fixed values of
`rho`

.

```
# Using the COPD dataset
network.copd <- mb.network(copd)
# Run an log-linear time-course MBNMA
# that accounts for correlation between time points using variance adjustment
mbnma <- mb.run(network.copd,
fun=tloglin(pool.rate="rel", method.rate="random"),
rho="dunif(0,1)", covar="varadj")
```

It is important to note that the covariance matrix must be positive semi-definite. This may mean that in order to satisfy this requirement for particular covariance matrix structures, the values that `rho`

can take are limited. `rho`

must always be bounded by -1 and 1, but even within this range some negative values for `rho`

can result in a non positive matrix, which can lead to an error in the evaluation of the multivariate likelihood. If so, it may be necessary to further restrict the prior distribution.

Shared effects between treatments within the network can be modelled using class effects. This requires assuming that different treatments have some sort of shared class effect, perhaps due to different (yet clinically similar) doses of the same agent or different treatments with a similar mechanism of action. One advantage of this is that class effects can be used to connect relative effects between treatments in a network that would be disconnected at the treatment level, but can be connected via classes at the class level. However, it is important to ensure that such an effect is clinically justifiable, as making these assumptions risks introducing heterogeneity/inconsistency.

Class effects can only be applied to time-course parameters which vary by treatment (`pool="rel"`

), and class effects are modelled separately for each time-course parameter.

In `mb.run()`

class effects are specified as a list, in which each element is named by the time-course parameter on which it should be modelled. The class effect for each time-course parameter can be either `"common"`

, in which the effects for each treatment within the same class are constrained to a common class effect, or `"random"`

, in which the effects for each treatment within the same class are assumed to be randomly distributed around a shared class mean.

```
# Run a B-spline time-course MBNMA with a knot at 0.2 times the max follow-up
# Common class effect on beta.2, the 2nd spline coefficient
mbnma <- mb.run(network.gout,
fun=tspline(type="bs", knots=c(0.2),
pool.1 = "rel", method.1="common",
pool.2="rel", method.2="random"),
class.effect = list(beta.2="common"))
```

```
summary(mbnma)
#> ========================================
#> Time-course MBNMA
#> ========================================
#>
#> Time-course function: B-spline (knots = 0.2; degree = 1)
#> Data modelled with intercept
#>
#> beta.1 parameter
#> Pooling: relative effects
#> Method: common treatment effects
#>
#> |Treatment |Parameter | Median| 2.5%| 97.5%|
#> |:---------|:---------|--------:|--------:|--------:|
#> |Plac |d.1[1] | 0.0000| 0.0000| 0.0000|
#> |Allo_100 |d.1[2] | -1.5932| -3.0459| -0.0849|
#> |Allo_200 |d.1[3] | -3.1365| -3.3959| -2.8857|
#> |Allo_289 |d.1[4] | -4.4078| -4.5717| -4.2367|
#> |Allo_400 |d.1[5] | -12.4440| -14.0334| -10.9217|
#> |Arha_NA |d.1[6] | -6.8517| -7.5575| -6.1069|
#> |BCX4_140 |d.1[7] | -4.6051| -4.9666| -4.2345|
#> |BCX4_18.5 |d.1[8] | -2.4991| -2.9553| -2.0596|
#> |BCX4_240 |d.1[9] | -5.9009| -6.3620| -5.4527|
#> |BCX4_80 |d.1[10] | -3.6243| -4.0995| -3.1752|
#> |Benz_NA |d.1[11] | -15.0427| -16.2538| -13.9201|
#> |Febu_140 |d.1[12] | -6.9461| -7.1171| -6.7678|
#> |Febu_210 |d.1[13] | -9.0091| -9.1153| -8.9007|
#> |Febu_25 |d.1[14] | -4.0053| -4.1695| -3.8418|
#> |Febu_72.5 |d.1[15] | -5.9425| -6.1132| -5.7669|
#> |RDEA_100 |d.1[16] | -2.2728| -2.8326| -1.7272|
#> |RDEA_200 |d.1[17] | -4.0424| -4.3907| -3.6734|
#> |RDEA_400 |d.1[18] | -5.1579| -5.5053| -4.8083|
#> |RDEA_600 |d.1[19] | -8.0515| -8.4898| -7.6161|
#>
#>
#> beta.2 parameter
#> Pooling: relative effects
#> Method: random treatment effects
#> Class effects modelled for this parameter
#>
#> |Treatment |Parameter | Median| 2.5%| 97.5%|
#> |:---------|:---------|-------:|--------:|-------:|
#> |Plac |d.2[1] | 0.0000| 0.0000| 0.0000|
#> |Allo_100 |d.2[2] | 15.0165| -0.9267| 29.5792|
#> |Allo_200 |d.2[3] | 15.0165| -0.9267| 29.5792|
#> |Allo_289 |d.2[4] | 15.0165| -0.9267| 29.5792|
#> |Allo_400 |d.2[5] | 15.0165| -0.9267| 29.5792|
#> |Arha_NA |d.2[6] | -0.4155| -62.4791| 61.8483|
#> |BCX4_140 |d.2[7] | 10.7825| -3.1657| 24.9763|
#> |BCX4_18.5 |d.2[8] | 10.7825| -3.1657| 24.9763|
#> |BCX4_240 |d.2[9] | 10.7825| -3.1657| 24.9763|
#> |BCX4_80 |d.2[10] | 10.7825| -3.1657| 24.9763|
#> |Benz_NA |d.2[11] | 15.5054| -14.6098| 43.7460|
#> |Febu_140 |d.2[12] | 15.9188| 2.8667| 27.7989|
#> |Febu_210 |d.2[13] | 15.9188| 2.8667| 27.7989|
#> |Febu_25 |d.2[14] | 15.9188| 2.8667| 27.7989|
#> |Febu_72.5 |d.2[15] | 15.9188| 2.8667| 27.7989|
#> |RDEA_100 |d.2[16] | 20.1915| -0.2510| 38.5101|
#> |RDEA_200 |d.2[17] | 20.1915| -0.2510| 38.5101|
#> |RDEA_400 |d.2[18] | 20.1915| -0.2510| 38.5101|
#> |RDEA_600 |d.2[19] | 20.1915| -0.2510| 38.5101|
#>
#> Between-study SD modelled for this parameter:
#>
#> |Parameter | Median| 2.5%| 97.5%|
#> |:---------|-------:|-------:|-------:|
#> |sd.beta.2 | 13.8466| 10.1003| 19.9032|
#>
#>
#> Class Effects
#>
#> Class effects for beta.2
#> Common (fixed) class effects
#>
#> |Class |Parameter | Median| 2.5%| 97.5%|
#> |:-----|:---------|-------:|--------:|-------:|
#> |Plac |D.2[1] | 0.0000| 0.0000| 0.0000|
#> |Allo |D.2[2] | 15.0165| -0.9267| 29.5792|
#> |Arha |D.2[3] | -0.4155| -62.4791| 61.8483|
#> |BCX4 |D.2[4] | 10.7825| -3.1657| 24.9763|
#> |Benz |D.2[5] | 15.5054| -14.6098| 43.7460|
#> |Febu |D.2[6] | 15.9188| 2.8667| 27.7989|
#> |RDEA |D.2[7] | 20.1915| -0.2510| 38.5101|
#>
#>
#> Correlation between time points
#> Covariance structure: varadj
#> Rho assigned a numeric value: 0
#>
#> #### Model Fit Statistics ####
#>
#> Effective number of parameters:
#> pD (pV) calculated using the rule, pD = var(deviance)/2 = 99
#> Deviance = 13311
#> Residual deviance = 14005
#> Deviance Information Criterion (DIC) = 13410
```

Mean class effects are given in the output as `D.2`

parameters. These can be interpreted as the relative effect of each class versus the `Plac`

(Placebo), for the 2nd spline coefficient (`beta.2`

). Note the number of `D.2`

parameters is therefore equal to the number of classes defined in the dataset.

Several additional arguments can be given to `mb.run()`

that require further explanation.

Default vague priors for the model are as follows:

\[ \begin{aligned} &\alpha_{i} \sim N(0,10000)\\ &\boldsymbol{\mu}_{i} \sim MVN(0,M_{i})\\ &\boldsymbol{d}_{t} \sim MVN(0,\Sigma_{t})\\ &beta_{\phi} \sim N(0,10000)\\ &D_{\phi,c} \sim N(0,1000)\\ &\tau_{\phi} \sim N(0,400) \text{ limited to } x \in [0,\infty]\\ &\tau^D_{\phi} \sim N(0,400) \text{ limited to } x \in [0,\infty]\\ &M_{i} \sim Wish^{-1}(\Omega,k)\\ &\Sigma_{t} \sim Wish^{-1}(\Omega,k)\\ \end{aligned} \]

- \(\alpha_i\) is the response at time=0 in study \(i\)
- \(\mu_i\) is a vector of study reference effects for each time-course parameter in study \(i\). Where only a single time-course parameter is modelled using relative effects the prior is defined as \(\mu_{i} \sim N(0,10000)\).
- \(\boldsymbol{d}_{t}\) is a vector of pooled relative effects for treatment \(t\) whose length is the number of time-course parameters in the model. Where only a single time-course parameter is modelled using relative effects the prior is defined as \(d_{t} \sim N(0,10000)\).
- \(\beta_{\phi}\) is the absolute effect for time-course parameter \(\phi\) modelled independently of treatment
- \(D_{\phi,c}\) is the class relative effect for time-course parameter \(\phi\) in class \(c\)
- \(\tau_{\phi}\) is the between-study SD for time-course parameter \(\phi\)
- \(\tau^D_{\phi}\) is the within-class SD for time-course parameter \(\phi\)
- \(\Omega\) is a diagonal scale matrix specified in
`var.scale`

- \(k\) is the degrees of freedom for the inverse-Wishart distribution, equal to the number of time-course parameters

Users may wish to change these, perhaps in order to use more/less informative priors, but also because the default prior distributions in some models may lead to errors when compiling/updating models.

This can be more likely for certain types of models. For example some prior distributions may generate results that are too extreme for JAGS to compute, such as for time-course parameters that are powers (e.g. Emax functions with a Hill parameter or power parameters in fractional polynomials).

If the model fails during compilation/updating (i.e. due to a problem in JAGS), `mb.run()`

will generate an error and return a list of arguments that `mb.run()`

used to generate the model. Within this (as within a model that has run successfully), the priors used by the model (in JAGS syntax) are stored within `"model.arg"`

.

In this way a model can first be run with vague priors and then rerun with different priors, perhaps to allow successful computation, perhaps to provide more informative priors, or perhaps to run a sensitivity analysis with different priors.

To change priors within a model, a list of replacements can be provided to `priors`

in `mb.run()`

. The name of each element is the name of the parameter to change (without indices) and the value of the element is the JAGS distribution to use for the prior. See the JAGS Manual (2017) for syntax details regarding specifying distributions. This can include censoring or truncation if desired. Only the priors to be changed need to be specified - priors for parameters that aren’t specified will take default values. Note that in JAGS, normal distributions are specified using precision (1/variance) rather than SD.

For example, we may wish to specify a tighter prior for the between-study SD:

The default value for `pd`

in `mb.run()`

is `pd="pv"`

, which uses the rapid approach automatically calculated in the `R2jags`

package as `pv = var(deviance)/2`

. Whilst this is easy to calculate, it is only an approximation to the effective number of parameters, and may be numerically unstable (Gelman et al. 2003). However, it has been shown to be reliable for model comparison in time-course MBNMA models in a simulation study (Pedder et al. 2020).

A more reliable method for estimating `pd`

is `pd="pd.kl"`

, which uses the Kullback-Leibler divergence (Plummer 2008). This is more reliable than the default method used in `R2jags`

for calculating the effective number of parameters from non-linear models. The disadvantage of this approach is that it requires running additional MCMC iterations, so can be slightly slower to calculate.

A commonly-used approach in Bayesian models for calculating pD is the plug-in method (`pd="plugin"`

) (Spiegelhalter et al. 2002). However, this can sometimes result in negative non-sensical values due to skewed posterior distributions for deviance contributions that can arise when fitting non-linear models.

Finally, pD can also be calculated using an optimism adjustment (`pd="popt"`

) which allows for calculation of the penalized expected deviance (Plummer 2008). This adjustment allows for the fact that data used to estimate the model is the same as that used to assess its parsimony. As for `pd="pd.kl"`

, it also requires running additional MCMC iterations.

`mb.run()`

automatically models correlation between time-course parameters modelled using relative effects. Time-course parameters are typically correlated and this allows information on each parameter to help inform the other(s). The correlation is modelled using a multivariate normal distribution with a vague inverse-Wishart prior on the covariance matrix \(\Sigma_t\). This can be made more informative by indicating the scale matrix for the parameters that are modelled using relative effects, and by increasing the degrees of freedom of the inverse-Wishart prior \(\Sigma_t\).

`omega`

can be used as an argument in `mb.run()`

to represent this scale matrix. If specified, it must be a symmetric positive definite matrix with dimensions equal to the number of time-course parameters modelled using relative effects (`pool="rel"`

). If left as `NULL`

(the default) a diagonal matrix with elements equal to 1 is used. The degrees of freedom can then be changed within the `priors`

argument of `mb.run()`

to make the prior more informative.

For example, with the osteoarthritis dataset we might expect that for a piecewise linear time-course function, the parameter values (in this model the relative different in gradient vs placebo) for the first coefficient might be 10 times larger than for the second coefficient:

In addition to the arguments specific to `mb.run()`

it is also possible to use any arguments to be sent to `R2jags::jags()`

. Most of these relate to improving the performance of MCMC simulations in JAGS. Some of the key arguments that may be of interest are:

`n.chains`

The number of Markov chains to run (default is 3)`n.iter`

The total number of iterations per MCMC chain`n.burnin`

The number of iterations that are discarded to ensure iterations are only saved once chains have converged`n.thin`

The thinning rate which ensures that results are only saved for 1 in every`n.thin`

iterations per chain. This can be increased to reduce autocorrelation in MCMC samples

`MBNMAtime`

is run using JAGS, which performs Bayesian inference using Gibbs sampling (JAGS Computer Program 2017). This samples from the posterior distribution to obtain posterior densities for monitored parameters of interest. However, convergence of the MCMC algorithm on the posterior density is therefore critical for obtaining robust results.

Multiple MCMC chains should always be run (default in `MBNMAtime`

is 3) as this allows for traces of each chain to be examined. Consistent overlap between the traces in different chains, as well as low autocorrelation in MCMC samples suggests that convergence is likely to have been successful.

A simple output for assessing convergence is the Gelman-Rubin \(\hat{R}\) statistic, which is the ratio of between- and within-chain standard deviations (Gelman et al. 2003). Values of <1.05 are widely accepted as implying convergence for practical values, though some functions in `MBNMAtime`

alerts users if \(\hat{R}>1.02\).

`Rhat`

values for each parameter are calculated automatically in `R2jags`

and can be shown if `print()`

is called on an object of `class("mbnma")`

:

```
print(mbnma)
#> Inference for Bugs model at "C:\Users\hp17602\AppData\Local\Temp\RtmpUR7Fjg\file4c5c4a54e72", fit using jags,
#> 3 chains, each with 20000 iterations (first 10000 discarded), n.thin = 10
#> n.sims = 3000 iterations saved
#> mu.vect sd.vect 2.5% 25% 50% 75% 97.5%
#> D.2[1] 0.000 0.000 0.000 0.000 0.000 0.000 0.000
#> D.2[2] 14.782 7.706 -0.927 9.978 15.016 19.844 29.579
#> D.2[3] -0.064 31.874 -62.479 -21.943 -0.415 22.150 61.848
#> D.2[4] 10.833 7.248 -3.166 5.832 10.782 15.484 24.976
#> D.2[5] 15.111 14.549 -14.610 5.781 15.505 24.534 43.746
#> D.2[6] 15.729 6.410 2.867 11.537 15.919 20.040 27.799
#> D.2[7] 19.929 9.760 -0.251 13.763 20.191 26.341 38.510
#> d.1[1] 0.000 0.000 0.000 0.000 0.000 0.000 0.000
#> d.1[2] -1.595 0.744 -3.046 -2.097 -1.593 -1.096 -0.085
#> d.1[3] -3.137 0.127 -3.396 -3.221 -3.136 -3.050 -2.886
#> d.1[4] -4.408 0.088 -4.572 -4.467 -4.408 -4.350 -4.237
#> d.1[5] -12.454 0.780 -14.033 -12.970 -12.444 -11.926 -10.922
#> d.1[6] -6.843 0.372 -7.558 -7.096 -6.852 -6.587 -6.107
#> d.1[7] -4.603 0.188 -4.967 -4.736 -4.605 -4.475 -4.234
#> d.1[8] -2.502 0.227 -2.955 -2.654 -2.499 -2.348 -2.060
#> d.1[9] -5.901 0.235 -6.362 -6.058 -5.901 -5.742 -5.453
#> d.1[10] -3.625 0.242 -4.100 -3.787 -3.624 -3.452 -3.175
#> d.1[11] -15.054 0.585 -16.254 -15.444 -15.043 -14.669 -13.920
#> d.1[12] -6.944 0.091 -7.117 -7.005 -6.946 -6.883 -6.768
#> d.1[13] -9.008 0.053 -9.115 -9.044 -9.009 -8.972 -8.901
#> d.1[14] -4.005 0.086 -4.170 -4.064 -4.005 -3.947 -3.842
#> d.1[15] -5.942 0.089 -6.113 -6.002 -5.942 -5.884 -5.767
#> d.1[16] -2.276 0.281 -2.833 -2.468 -2.273 -2.084 -1.727
#> d.1[17] -4.040 0.182 -4.391 -4.162 -4.042 -3.920 -3.673
#> d.1[18] -5.160 0.173 -5.505 -5.271 -5.158 -5.046 -4.808
#> d.1[19] -8.055 0.223 -8.490 -8.206 -8.052 -7.903 -7.616
#> d.2[1] 0.000 0.000 0.000 0.000 0.000 0.000 0.000
#> d.2[2] 14.782 7.706 -0.927 9.978 15.016 19.844 29.579
#> d.2[3] 14.782 7.706 -0.927 9.978 15.016 19.844 29.579
#> d.2[4] 14.782 7.706 -0.927 9.978 15.016 19.844 29.579
#> d.2[5] 14.782 7.706 -0.927 9.978 15.016 19.844 29.579
#> d.2[6] -0.064 31.874 -62.479 -21.943 -0.415 22.150 61.848
#> d.2[7] 10.833 7.248 -3.166 5.832 10.782 15.484 24.976
#> d.2[8] 10.833 7.248 -3.166 5.832 10.782 15.484 24.976
#> d.2[9] 10.833 7.248 -3.166 5.832 10.782 15.484 24.976
#> d.2[10] 10.833 7.248 -3.166 5.832 10.782 15.484 24.976
#> d.2[11] 15.111 14.549 -14.610 5.781 15.505 24.534 43.746
#> d.2[12] 15.729 6.410 2.867 11.537 15.919 20.040 27.799
#> d.2[13] 15.729 6.410 2.867 11.537 15.919 20.040 27.799
#> d.2[14] 15.729 6.410 2.867 11.537 15.919 20.040 27.799
#> d.2[15] 15.729 6.410 2.867 11.537 15.919 20.040 27.799
#> d.2[16] 19.929 9.760 -0.251 13.763 20.191 26.341 38.510
#> d.2[17] 19.929 9.760 -0.251 13.763 20.191 26.341 38.510
#> d.2[18] 19.929 9.760 -0.251 13.763 20.191 26.341 38.510
#> d.2[19] 19.929 9.760 -0.251 13.763 20.191 26.341 38.510
#> rho 0.000 0.000 0.000 0.000 0.000 0.000 0.000
#> sd.beta.2 14.150 2.472 10.100 12.404 13.847 15.555 19.903
#> totresdev 14005.435 14.059 13979.653 13995.610 14004.881 14014.440 14034.654
#> deviance 13311.247 14.059 13285.464 13301.422 13310.692 13320.251 13340.465
#> Rhat n.eff
#> D.2[1] 1.000 1
#> D.2[2] 1.001 3000
#> D.2[3] 1.001 3000
#> D.2[4] 1.001 3000
#> D.2[5] 1.001 3000
#> D.2[6] 1.001 3000
#> D.2[7] 1.002 1800
#> d.1[1] 1.000 1
#> d.1[2] 1.001 3000
#> d.1[3] 1.002 2200
#> d.1[4] 1.001 3000
#> d.1[5] 1.003 1400
#> d.1[6] 1.001 3000
#> d.1[7] 1.001 3000
#> d.1[8] 1.001 3000
#> d.1[9] 1.001 3000
#> d.1[10] 1.001 3000
#> d.1[11] 1.005 1000
#> d.1[12] 1.001 3000
#> d.1[13] 1.003 800
#> d.1[14] 1.001 3000
#> d.1[15] 1.001 3000
#> d.1[16] 1.001 3000
#> d.1[17] 1.002 2000
#> d.1[18] 1.001 3000
#> d.1[19] 1.001 3000
#> d.2[1] 1.000 1
#> d.2[2] 1.001 3000
#> d.2[3] 1.001 3000
#> d.2[4] 1.001 3000
#> d.2[5] 1.001 3000
#> d.2[6] 1.001 3000
#> d.2[7] 1.001 3000
#> d.2[8] 1.001 3000
#> d.2[9] 1.001 3000
#> d.2[10] 1.001 3000
#> d.2[11] 1.001 3000
#> d.2[12] 1.001 3000
#> d.2[13] 1.001 3000
#> d.2[14] 1.001 3000
#> d.2[15] 1.001 3000
#> d.2[16] 1.002 1800
#> d.2[17] 1.002 1800
#> d.2[18] 1.002 1800
#> d.2[19] 1.002 1800
#> rho 1.000 1
#> sd.beta.2 1.002 1300
#> totresdev 1.001 3000
#> deviance 1.001 3000
#>
#> For each parameter, n.eff is a crude measure of effective sample size,
#> and Rhat is the potential scale reduction factor (at convergence, Rhat=1).
#>
#> DIC info (using the rule, pD = var(deviance)/2)
#> pD = 98.9 and DIC = 13409.6
#> DIC is an estimate of expected predictive error (lower deviance is better).
```

Two useful packages for investigating convergence of monitored parameters are the `mcmcplots`

and `coda`

packages. These can be used to generate plots that can provide information on MCMC chain overlap and autocorrelation:

```
# Traceplots
mcmcplots::traplot(mbnma, "sd.beta.1")
# Running mean plots
mcmcplots::rmeanplot(mbnma, "sd.beta.1")
# Posterior densities
mcmcplots::denplot(mbnma, "sd.beta.1")
# Autocorrelation plots
coda::autocorr.plot(mbnma)
```

A single function (`mcmcplots::mcmcplot()`

) can also be used to generate an HTLM with all these plots for all monitored parameters.

If there are problems with convergence, the first solution may be to run the model for more iterations (see Arguments to be sent to JAGS), which give the chains a greater number of calculations over which to converge. However, for models with a large number of parameters and relatively limited data, information may be too limited to allow convergence within a computationally reasonable number of iterations. In these situations there is not enough information in the data to support such a highly parameterised model, suggesting that a simpler model should be fitted. Alternatively, more informative priors can be used, but given the lack of information in the data relative to the model’s complexity, results are likely to be sensitive to these priors.

For a detailed review of MCMC convergence assessment see Sinharay (2003).

To assess how well a model fits the data, it can be useful to look at a plot of the contributions of each data point to the total deviance or residual deviance. This can be done using `devplot()`

. As individual deviance contributions are not automatically monitored in the model, this might require the model to be run for additional iterations.

Results can be plotted either as a scatter plot (`plot.type="scatter"`

) or a series of boxplots (`plot.type="box"`

).

```
# Run a first-order fractional polynomial time-course MBNMA
mbnma <- mb.run(network.pain,
fun=tfpoly(degree=1,
pool.1="rel", method.1="random",
method.power1="common"))
# Plot a box-plot of deviance contributions (the default)
devplot(mbnma, n.iter=1000)
```

```
#> `dev` not monitored in mbnma$parameters.to.save.
#> additional iterations will be run in order to obtain results for `dev`
```

From these plots we can see that whilst the model fit is good at later time points, it frequently underestimates responses at earlier time points.

A function that appropriately captures the time-course shape should show a reasonably flat shape of deviance contributions (i.e. contributions should be similar across all time points).

If saved to an object, the output of `devplot()`

contains the results for individual deviance contributions, and this can be used to identify any extreme outliers.

Another approach for assessing model fit can be to plot the fitted values, using `fitplot()`

. As with `devplot()`

, this may require running additional model iterations to monitor `theta`

.

Fitted values are plotted as connecting lines and observed values in the original dataset are plotted as points. These plots can be used to identify if the model fits the data well for different treatments and at different parts of the time-course.

Forest plots can be easily generated from MBNMA models using the `plot()`

method on an `"mbnma"`

object. By default this will plot a separate panel for each time-course parameter in the model. Forest plots can only be generated for parameters which vary by treatment/class.

Rankings can be calculated for different time-course parameters from MBNMA models by using `rank()`

on an `"mbnma"`

object. Any parameter monitored in an MBNMA model that varies by treatment/class can be ranked. A vector of these is assigned to `params`

. `lower_better`

indicates whether negative scores should be ranked as “better” (`TRUE`

) or “worse” (`FALSE`

)

In addition, it is possible to rank the Area Under the Curve (AUC) for a particular treatment by adding `"auc"`

to the vector of `params`

(included as the default). This will calculate the area under the predicted response over time, and will therefore be a function of all the time-course parameters in the model simultaneously. However, it will be dependent on the range of times chosen to integrate over (`int.range`

), and a different choice of time-frame may lead to different treatment rankings. `"auc"`

can also not currently be calculated from MBNMA models with more complex time-course functions (piecewise, fractional polynomials), nor with MBNMA models that use class effects.

```
# Identify quantile for knot at 1 week
timequant <- 1/max(network.pain$data.ab$time)
# Run a piecewise linear time-course MBNMA with a knot at 1 week
mbnma <- mb.run(network.pain,
fun=tspline(type="ls", knots = timequant,
pool.1 = "rel", method.1="common",
pool.2 = "rel", method.2="common"))
# Rank results based on AUC (calculated 0-10 weeks), more negative slopes considered to be "better"
ranks <- rank(mbnma, params=c("auc", "d.2"),
int.range=c(0,10), lower_better = TRUE, n.iter=1000)
```

```
print(ranks)
#>
#> ========================================
#> Treatment rankings
#> ========================================
#>
#> d.2 ranking
#>
#> |Treatment | Mean| Median| 2.5%| 97.5%|
#> |:---------|-----:|------:|----:|-----:|
#> |Pl_0 | 8.69| 9| 5| 13.00|
#> |Ce_100 | 17.50| 18| 7| 26.00|
#> |Ce_200 | 16.27| 16| 11| 21.00|
#> |Ce_400 | 18.16| 19| 8| 26.00|
#> |Du_90 | 8.21| 3| 1| 29.00|
#> |Et_10 | 23.42| 27| 4| 29.00|
#> |Et_30 | 14.86| 15| 7| 23.00|
#> |Et_5 | 4.54| 3| 1| 22.03|
#> |Et_60 | 24.27| 25| 15| 29.00|
#> |Et_90 | 19.68| 23| 3| 29.00|
#> |Lu_100 | 12.71| 13| 6| 20.00|
#> |Lu_200 | 13.98| 14| 8| 21.00|
#> |Lu_400 | 14.68| 14| 7| 22.00|
#> |Lu_NA | 7.08| 3| 1| 29.00|
#> |Na_1000 | 21.53| 22| 17| 26.00|
#> |Na_1500 | 18.97| 19| 11| 25.00|
#> |Na_250 | 20.79| 24| 3| 29.00|
#> |Na_750 | 22.19| 23| 12| 28.00|
#> |Ox_44 | 7.45| 4| 1| 26.00|
#> |Ro_12 | 21.81| 25| 4| 29.00|
#> |Ro_125 | 11.95| 8| 1| 29.00|
#> |Ro_25 | 24.88| 26| 13| 29.00|
#> |Tr_100 | 6.12| 6| 2| 13.00|
#> |Tr_200 | 7.53| 7| 3| 16.00|
#> |Tr_300 | 9.78| 9| 4| 19.00|
#> |Tr_400 | 7.04| 6| 2| 18.00|
#> |Va_10 | 21.25| 23| 8| 28.00|
#> |Va_20 | 16.10| 16| 4| 27.00|
#> |Va_5 | 13.54| 13| 3| 26.00|
#>
#>
#> auc ranking
#>
#> |Treatment | Mean| Median| 2.5%| 97.5%|
#> |:---------|-----:|------:|-----:|-----:|
#> |Pl_0 | 26.99| 27| 26.00| 28.00|
#> |Ce_100 | 22.35| 23| 16.00| 26.00|
#> |Ce_200 | 14.08| 14| 10.00| 18.02|
#> |Ce_400 | 13.28| 13| 6.00| 21.00|
#> |Du_90 | 17.68| 19| 6.00| 26.00|
#> |Et_10 | 27.48| 28| 20.00| 29.00|
#> |Et_30 | 6.04| 6| 3.00| 11.00|
#> |Et_5 | 12.78| 11| 2.00| 28.00|
#> |Et_60 | 3.51| 3| 2.00| 7.00|
#> |Et_90 | 5.83| 3| 1.00| 24.00|
#> |Lu_100 | 13.79| 14| 9.00| 19.00|
#> |Lu_200 | 15.75| 16| 10.00| 21.00|
#> |Lu_400 | 9.52| 9| 5.00| 16.00|
#> |Lu_NA | 11.41| 11| 4.00| 20.00|
#> |Na_1000 | 7.68| 8| 4.00| 12.00|
#> |Na_1500 | 8.85| 9| 4.00| 17.00|
#> |Na_250 | 28.32| 29| 24.98| 29.00|
#> |Na_750 | 19.78| 20| 11.00| 25.00|
#> |Ox_44 | 4.63| 3| 1.00| 17.00|
#> |Ro_12 | 20.99| 24| 4.00| 28.00|
#> |Ro_125 | 1.63| 1| 1.00| 5.02|
#> |Ro_25 | 16.77| 18| 5.00| 26.00|
#> |Tr_100 | 24.18| 25| 20.00| 27.00|
#> |Tr_200 | 23.19| 23| 19.00| 26.00|
#> |Tr_300 | 15.66| 16| 8.00| 22.00|
#> |Tr_400 | 11.27| 11| 4.00| 21.00|
#> |Va_10 | 21.67| 22| 13.00| 27.00|
#> |Va_20 | 13.17| 13| 4.00| 22.00|
#> |Va_5 | 16.70| 18| 5.98| 25.00|
```

The output is an object of `class("mb.rank")`

, containing a list for each ranked parameter in `params`

, which consists of a summary table of rankings and raw information on treatment ranking and probabilities. The summary median ranks with 95% credible intervals can be simply displayed using `print()`

.

Histograms for ranking results can also be plotted using the `plot()`

method, which takes the raw MCMC ranking results given in `rank.matrix`

and plots the number of MCMC iterations the parameter value for each treatment was ranked a particular position.

After performing an MBNMA, responses can be predicted from the parameter estimates using `predict()`

on an `"mbnma"`

object. A number of important parameters need to be identified to make robust predictions, though defaults can be used to generate a plot that gives a good indication of the time-course relationship assuming a reference treatment response of zero. For further information the help file can be accessed using `?predict.mbnma`

.

One key parameter is `E0`

, which defines what value(s) to use for the predicted response at time = 0. A single numeric value can be given for this to indicate a deterministic value, or a function representing a random number generator (RNG) distribution in R (stochastic) (e.g. `E0 = ~rnorm(n, 7, 0.2)`

. These values can be identified for the population of interest from external data (e.g. observational/registry).

The more challenging parameter(s) to identify are those for the network reference treatment response, supplied to `predict()`

in the `ref.resp`

argument. Typically in an MBNMA, relative effects are estimated and the network reference effect is modelled as a nuisance parameter. Therefore we need to provide an input for this reference treatment effect for all time-course parameters modelled using `pool="rel"`

so that we can apply the relative effects estimated in our model to it. There are two options for providing these values.

The first approach is to give values for each time-course parameter modelled using relative effects to `ref.resp`

. This is given as a list, with a separate named element for each time-course parameter. Each element can take either a single numeric value (deterministic), or a function representing a random number generator distribution in R (stochastic).

```
# Run an Emax time-course MBNMA using the osteoarthritis dataset
mbnma <- mb.run(network.pain,
fun=temax(pool.emax="rel", method.emax="common",
pool.et50="abs", method.et50="common"),
rho="dunif(0,1)", covar="varadj")
```

```
# Specify placebo time-course parameters
ref.params <- list(emax=-2)
# Predict responses for a selection of treatments using a stochastic E0 and
# placebo parameters defined in ref.params to estimate the network reference treatment effect
pred <- predict(mbnma, treats=c("Pl_0", "Ce_200", "Du_90", "Et_60",
"Lu_400", "Na_1000", "Ox_44", "Ro_25",
"Tr_300", "Va_20"),
E0=~rnorm(n, 8, 0.5), ref.resp=ref.params)
print(pred)
```

The second is to assign `ref.resp`

a data frame composed of single-arm studies of the network reference treatment. A separate synthesis model for the reference treatment effect will then be run, and the values from this used as the prediction reference treatment effect. This dataset could be a series of observational studies measured at multiple follow-up times that closely match the population of interest for the prediction. Alternatively it could be a subset of data from the original RCT dataset used for the MBNMA model (though this may be less generalisable to the population of interest).

```
# Generate a dataset of network reference treatment responses over time
placebo.df <- network.pain$data.ab[network.pain$data.ab$treatment==1,]
# Predict responses for a selection of treatments using a deterministic E0 and
#placebo.df to model the network reference treatment effect
pred <- predict(mbnma, treats=c("Pl_0", "Ce_200", "Du_90", "Et_60",
"Lu_400", "Na_1000", "Ox_44", "Ro_25",
"Tr_300", "Va_20"),
E0=10, ref.resp=placebo.df)
print(pred)
```

It is also possible specify the time points for which to predict responses (`times`

), given as a vector of positive numbers. If left as the default then the maximum follow-up in the dataset will be used as the upper limit for the range of predicted time-points.

An object of class `"mb.predict"`

is returned, which is a list of summary tables and MCMC prediction matrices for each treatment, in addition to the original `mbnma`

object. The `summary()`

method can be used to print mean posterior predictions at each time point for each treatment.

Predicted responses can also be plotted using the `plot()`

method on an object of `class("mb.predict")`

. Within the default arguments, the median predicted network reference treatment response is overlaid on the predicted response for each treatment. Setting `overlay.ref = FALSE`

prevents this and causes the network reference treatment predicted response to be plotted as a separate panel. Shaded counts of observations in the original dataset at each predicted time point can be plotted over the 95% CrI for each treatment by setting `disp.obs = TRUE`

.

This can be used to identify any extrapolation/interpretation of the time-course that might be occurring for a particular treatment, and where predictions might therefore be problematic.

To illustrate a situation in which this could be very informative, we can look at predicted responses for a quadratic time-course function fitted to the Obesity dataset:

```
# Fit a quadratic time-course MBNMA to the Obesity dataset
network.obese <- mb.network(obesityBW_CFB, reference = "plac")
mbnma <- mb.run(network.obese,
fun=tpoly(degree=2,
pool.1 = "rel", method.1="common",
pool.2="rel", method.2="common"))
# Define stochastic values centred at zero for network reference treatment
ref.params <- list(beta.1=~rnorm(n, 0, 0.05), beta.2=~rnorm(n, 0, 0.0001))
# Predict responses over the
pred.obese <- predict(mbnma, times=c(0:50), E0=100, treats = c(1,4,15),
ref.resp=ref.params)
# Plot predictions
plot(pred.obese, disp.obs = TRUE)
```

As you can see, within the limits of the observed data the predicted responses appear reasonable. However, extrapolation beyond this for `dexf_30MG`

leads to some rather strange results, suggesting an unrealistically huge increase in body weight after 50 weeks of treatment. On the other hand, the predicted response at 50 weeks follow-up in treatment 15 is within the limits of the observed data and so are likely to be more justifiable.

As a further addition to the plots of MBNMA predictions, it is possible to add predicted results from an NMA model. This is one in which time-points within an interval (specified in `overlay.nma`

) are “lumped” together to allow for analysis using standard NMA approaches (Lu and Ades 2004). Either a `"random"`

(the default) or `"common"`

effects NMA can be specified, and model fit statistics are reported below the resulting plot.

This can be useful to assess if the MBNMA predictions are in agreement with predictions from a lumped NMA model over a specific set of time-points, and can be a general indicator of the fit of the time-course model. However, it is important to note that the NMA model is not necessarily the more robust model, since it ignores potential differences in treatment effects that may arise from lumping time-points together. The wider the range specified in `overlay.nma`

, the greater the effect of lumping and the stronger the assumption of similarity between studies.

The NMA predictions are plotted over the range specified in `overlay.nma`

as a horizontal line, with the 95%CrI shown by a grey rectangle. The NMA predictions represent those for *any time-points within this range* since they lump together data at all these time-points. Predictions for treatments that are disconnected from the network reference treatment at data points specified within `overlay.nma`

cannot be estimated so are not included.

When performing an MBNMA by pooling relative treatment effects (`pool="rel"`

), the modelling approach assumes consistency between direct and indirect evidence within a network. This is an incredibly useful assumption as it allows us to improve precision on existing direct estimates, or to estimate relative effects between treatments that have not been compared in head-to-head trials, by making use of indirect evidence.

However, if this assumption does not hold it is extremely problematic for inference, so it is important to be able to test it. A number of different approaches exist to allow for this in standard Network Meta-Analysis (Dias et al. 2013). Two of these have been implemented within `MBNMAtime`

. It is important to note that in some model specifications there is likely to be sharing of model parameters (e.g. heterogeneity parameters, correlation coefficients) across networks which will lead to more conservative tests for consistency, and may lead to an inflated type II error.

Consistency is also likely to differ depending on the model used. Failing to appropriately model the time-course function may in fact induce inconsistency in the data. “Lumping” together different time points from studies in standard NMA is known to be a potential cause of inconsistency, which is one of the reasons why accounting for time-course using MBNMA is important (Pedder et al. 2019). When performing MBNMA, this is why it is important to first try to identify the best model possible in terms of time-course and common/random effects, and then to test for consistency within that model, rather than testing for consistency in models that are known not be be a good fit to the data.

Consistency testing can only be performed in networks in which closed loops of treatment comparisons exist that are drawn from independent sources of evidence. In networks which do not have any such loops of evidence, consistency cannot be formally tested (though it may still be present). The `mb.nodesplit.comparisons()`

function identifies loops of evidence that conform to this property, and identifies a treatment comparison within that loop for which direct and indirect evidence can be compared using node-splitting (see below).

```
# Loops of evidence within the alogliptin dataset
splits.alog <- mb.nodesplit.comparisons(network.alog)
print(splits.alog)
#> t1 t2 path
#> 8 3 4 3->1->4
#> 7 2 5 2->1->5
#> 6 2 4 2->1->4
#> 5 2 3 2->1->3
```

Another approach for consistency checking is node-splitting. This splits contributions for a particular treatment comparison into direct and indirect evidence, and the two can then be compared to test their similarity. `mb.nodesplit()`

takes similar arguments to `mb.run()`

that define the underlying MBNMA model in which to test for consistency, and returns an object of `class("mb.nodesplit")`

. There are two additional arguments required:

`comparisons`

indicates on which treatment comparisons to perform a node-split. The default value for this is to automatically identify all comparisons for which both direct and indirect evidence contributions are available using `mb.nodesplit.comparisons()`

.

`nodesplit.parameters`

indicates on which time-course parameters to perform a node-split. This can only take time-course parameters that have been assigned relative effects in the model (`pool="rel"`

). Alternatively the default `"all"`

can be used to split on all available time-course parameters in the model that have been pooled using relative effects.

As up to two models will need to be run for each treatment comparison to split, this function can take some time to run.

```
# Nodesplit using an Emax MBNMA
nodesplit <- mb.nodesplit(network.pain,
fun=temax(pool.emax="rel", method.emax = "random",
pool.et50="abs", method.et50 = "common"),
nodesplit.parameters="all"
)
```

```
print(nodesplit)
#> ========================================
#> Node-splitting analysis of inconsistency
#> ========================================
#>
#> emax
#>
#> |Comparison | p-value| Median| 2.5%| 97.5%|
#> |:-----------------|-------:|------:|------:|-----:|
#> |Ro_25 vs Ce_200 | 0.440| | | |
#> |-> direct | | 0.368| -0.419| 1.183|
#> |-> indirect | | 0.466| -0.317| 1.217|
#> | | | | | |
#> |Na_1000 vs Ce_200 | 0.357| | | |
#> |-> direct | | 0.219| -0.204| 0.688|
#> |-> indirect | | 0.403| -0.367| 1.176|
#> | | | | | |
```

Performing the `print()`

method on an object of `class("mb.nodesplit")`

prints a summary of the node-split results to the console, whilst the `summary()`

method will return a data frame of posterior summaries for direct and indirect estimates for each split treatment comparison and each time-course parameter.

It is possible to generate different plots of each node-split comparison using `plot()`

:

```
# Plot forest plots of direct and indirect results for each node-split comparison
plot(nodesplit, plot.type="forest")
# Plot posterior densities of direct and indirect results for each node-split comparisons
plot(nodesplit, plot.type="density")
```

As a further example, if we use a different time-course function (exponential) that is a less good fit for the data, and perform a node-split on the `rate`

time-course parameter, we find that there seems to be a strong discrepancy between direct and indirect estimates. This is strong evidence to reject the consistency assumption, and to either (as in this case) try to identify a better fitting model, or to re-examine the dataset to try to explain whether differences in studies making different comparisons may be causing this.

This highlights the importance of testing for consistency *after* identifying an appropriate time-course and common/random treatment effects model.

```
# Nodesplit on rate using an exponential MBNMA
ns.exp <- mb.nodesplit(network.pain,
fun=texp(pool.rate = "rel", method.rate="common"),
nodesplit.parameters="all")
```

```
print(ns.exp)
#> ========================================
#> Node-splitting analysis of inconsistency
#> ========================================
#>
#> rate
#>
#> |Comparison | p-value| Median| 2.5%| 97.5%|
#> |:-----------------|-------:|------:|------:|-----:|
#> |Ro_25 vs Ce_200 | 0.152| | | |
#> |-> direct | | 0.153| -0.200| 0.513|
#> |-> indirect | | 0.365| 0.150| 0.576|
#> | | | | | |
#> |Na_1000 vs Ce_200 | 0.000| | | |
#> |-> direct | | -0.036| -0.176| 0.103|
#> |-> indirect | | 0.385| 0.200| 0.576|
#> | | | | | |
plot(ns.exp, plot.type="forest")
```

`MBNMAtime`

provides a complete set of functions that allow for meta-analysis of longitudinal time-course data and plotting of a number of informative graphics. Functions are provided for ranking, prediction, and for assessing consistency when modelling using relative effects. By accounting for time-course in meta-analysis this can help to explain heterogeneity/inconsistency that may arise when lumping together different time-points using conventional NMA.

The package allows for flexible modelling of either relative or absolute effects interchangeably on different time-course parameters within the same analysis, whilst providing a straightforward syntax with which to define these models.

Dias, S., and A. E. Ades. 2016. “Absolute or Relative Effects? Arm-Based Synthesis of Trial Data.” Journal Article. *Res Synth Methods* 7 (1): 23–28. https://doi.org/10.1002/jrsm.1184.

Dias, S., N. J. Welton, A. J. Sutton, D. M. Caldwell, G. Lu, and A. E. Ades. 2013. “Evidence Synthesis for Decision Making 4: Inconsistency in Networks of Evidence Based on Randomized Controlled Trials.” Journal Article. *Med Decis Making* 33 (5): 641–56. https://doi.org/10.1177/0272989X12455847.

Gelman, A., J. B. Carlin, H. S. Stern, and D. B. Rubin. 2003. *Bayesian Data Analysis*. Book. 2nd ed. CRC Press.

Hong, H., H. Chu, J. Zhang, and B. P. Carlin. 2016. “A Bayesian Missing Data Framework for Generalized Multiple Outcome Mixed Treatment Comparisons.” Journal Article. *Res Synth Methods* 7 (1): 6–22. https://doi.org/10.1002/jrsm.1153.

JAGS Computer Program. 2017. https://mcmc-jags.sourceforge.io/.

Jansen, J. P., M. C. Vieira, and S. Cope. 2015. “Network Meta-Analysis of Longitudinal Data Using Fractional Polynomials.” Journal Article. *Stat Med* 34 (15): 2294–2311. https://doi.org/10.1002/sim.6492.

Karabis, A., L. Lindner, M. Mocarski, E. Huisman, and A. Greening. 2013. “Comparative Efficacy of Aclidinium Versus Glycopyrronium and Tiotropium, as Maintenance Treatment of Moderate to Severe Copd Patients: A Systematic Review and Network Meta-Analysis.” Journal Article. *Int J Chron Obstruct Pulmon Dis* 8: 405–23. https://doi.org/10.2147/COPD.S48967.

Karahalios, A. E., G. Salanti, S. L. Turner, G. P. Herbison, I. R. White, A. A. Veroniki, A. Nikolakopoulou, and J. E. McKenzie. 2017. “An Investigation of the Impact of Using Different Methods for Network Meta-Analysis: A Protocol for an Empirical Evaluation.” Journal Article. *Syst Rev* 6 (1): 119. https://doi.org/10.1186/s13643-017-0511-x.

Langford, O., J. K. Aronson, G. van Valkenhoef, and R. J. Stevens. 2016. “Methods for Meta-Analysis of Pharmacodynamic Dose-Response Data with Application to Multi-Arm Studies of Alogliptin.” Journal Article. *Stat Methods Med Res*. https://doi.org/10.1177/0962280216637093.

Lu, G., and A. E. Ades. 2004. “Combination of Direct and Indirect Evidence in Mixed Treatment Comparisons.” Journal Article. *Stat Med* 23 (20): 3105–24. https://doi.org/10.1002/sim.1875.

Pedder, H., M. Boucher, S. Dias, M. Bennetts, and N. J. Welton. 2020. “Performance of Model-Based Network Meta-Analysis (Mbnma) of Time-Course Relationships: A Simulation Study.” Journal Article. *Research Synthesis Methods* 11 (5): 678–97. https://doi.org/10.1002/jrsm.1432.

Pedder, H., S. Dias, M. Bennetts, M. Boucher, and N. J. Welton. 2019. “Modelling Time-Course Relationships with Multiple Treatments: Model-Based Network Meta-Analysis for Continuous Summary Outcomes.” Journal Article. *Res Synth Methods* 10 (2): 267–86.

PennState Eberly College of Science. n.d. “"Other Covariance Structures" Apr. 8, 2021.” https://online.stat.psu.edu/stat502/lesson/10/10.3.

Plummer, M. 2008. “Penalized Loss Functions for Bayesian Model Comparison.” Journal Article. *Biostatistics* 9 (3): 523–39. https://pubmed.ncbi.nlm.nih.gov/18209015/.

———. 2017. *JAGS User Manual*. Version 4.3.0. https://people.stat.sc.edu/hansont/stat740/jags_user_manual.pdf.

Sinharay, S. 2003. “Assessing Convergence of the Markov Chain Monte Carlo Algorithms: A Review.” RR-03-07. ETS Research Report Series. https://onlinelibrary.wiley.com/doi/pdf/10.1002/j.2333-8504.2003.tb01899.x.

Spiegelhalter, D. J., N. G. Best, B. P. Carlin, and A. van der Linde. 2002. “Bayesian Measures of Model Complexity and Fit.” Journal Article. *J R Statistic Soc B* 64 (4): 583–639.

Tallarita, M., M. De lorio, and G. Baio. 2019. “A Comparative Review of Network Meta-Analysis Models in Longitudinal Randomized Controlled Trial.” Journal Article. *Statistics in Medicine* 38 (16): 3053–72. https://doi.org/10.1002/sim.8169.