| name | network-meta-analysis |
| description | Network meta-analysis in R, including network setup, consistency, treatment rankings, and league tables. |
Network Meta-Analysis in R
Overview
Network meta-analysis (NMA) methods for comparing multiple treatments simultaneously using direct and indirect evidence. Covers network structure assessment, frequentist and Bayesian NMA approaches, consistency evaluation, treatment rankings, and visualization techniques.
Network Structure and Data Preparation
Pairwise Data Format
library(netmeta)
pairwise_data <- data.frame(
study = c("Study1", "Study1", "Study2", "Study2", "Study3", "Study3",
"Study4", "Study4", "Study5", "Study5", "Study5"),
treat1 = c("A", "A", "A", "B", "B", "B", "A", "C", "A", "B", "C"),
treat2 = c("B", "C", "B", "C", "C", "D", "C", "D", "B", "C", "D"),
TE = c(0.5, 0.3, 0.4, -0.2, 0.1, 0.3, 0.35, 0.25, 0.45, -0.15, 0.2),
seTE = c(0.1, 0.12, 0.11, 0.13, 0.15, 0.14, 0.09, 0.11, 0.10, 0.12, 0.13)
)
net <- netmeta(
TE = TE,
seTE = seTE,
treat1 = treat1,
treat2 = treat2,
studlab = study,
data = pairwise_data,
sm = "MD",
reference.group = "A",
all.treatments = NULL
)
summary(net)
Arm-Level Data Format
library(netmeta)
arm_data <- data.frame(
study = rep(c("S1", "S2", "S3"), c(2, 3, 2)),
treatment = c("A", "B", "A", "B", "C", "B", "C"),
n = c(50, 52, 48, 51, 49, 55, 53),
events = c(15, 22, 12, 25, 18, 28, 20)
)
library(meta)
pw <- pairwise(
treat = treatment,
event = events,
n = n,
studlab = study,
data = arm_data,
sm = "OR"
)
net_from_arm <- netmeta(pw)
Network Visualization
library(netmeta)
netgraph(net,
plastic = FALSE,
thickness = "number.of.studies",
multiarm = TRUE,
points = TRUE,
cex.points = 3,
col = "darkblue")
netgraph(net,
seq = "optimal",
plastic = FALSE,
thickness = "se.fixed",
number.of.studies = TRUE,
col = "#4477AA",
col.points = "#CC6677",
cex.points = 4,
labels = paste0(trts(net), "\n(n=", n.arms(net), ")"))
Frequentist NMA with netmeta
Basic Network Meta-Analysis
library(netmeta)
net <- netmeta(
TE = TE,
seTE = seTE,
treat1 = treat1,
treat2 = treat2,
studlab = study,
data = pairwise_data,
sm = "MD",
fixed = TRUE,
random = TRUE,
reference.group = "A"
)
summary(net)
net$TE.fixed["B", "A"]
net$TE.random["B", "A"]
net$seTE.random["B", "A"]
Forest Plot for NMA
library(netmeta)
forest(net,
reference.group = "A",
sortvar = TE,
smlab = "Mean Difference vs A",
drop.reference.group = TRUE,
label.left = "Favors A",
label.right = "Favors Treatment")
forest(net,
reference.group = "A",
direct = TRUE)
League Table
library(netmeta)
league <- netleague(
net,
digits = 2,
bracket = "(",
separator = " to ",
fixed = FALSE
)
print(league, common = FALSE)
league$random
Treatment Rankings
library(netmeta)
netrank(net, small.values = "good")
set.seed(123)
rank <- rankogram(net, nsim = 1000)
plot(rank)
plot(rank, cumulative = TRUE)
Bayesian NMA with gemtc
Basic Bayesian NMA
library(gemtc)
network_data <- list(
data.ab = data.frame(
study = c("S1", "S1", "S2", "S2", "S3", "S3", "S3"),
treatment = c("A", "B", "A", "C", "A", "B", "C"),
responders = c(15, 22, 12, 18, 20, 28, 24),
sampleSize = c(50, 52, 48, 49, 55, 58, 54)
)
)
network <- mtc.network(data.ab = network_data$data.ab)
plot(network)
model <- mtc.model(
network,
type = "consistency",
likelihood = "binom",
link = "logit",
linearModel = "random"
)
set.seed(123)
result <- mtc.run(
model,
n.adapt = 5000,
n.iter = 20000,
thin = 1
)
summary(result)
Model Diagnostics
library(gemtc)
library(coda)
plot(result)
gelman.diag(result$samples)
gelman.plot(result$samples)
effectiveSize(result$samples)
autocorr.plot(result$samples)
densplot(result$samples)
Treatment Rankings (Bayesian)
library(gemtc)
ranks <- rank.probability(result)
print(ranks)
plot(ranks)
sucra <- sucra(ranks)
print(sucra)
plot(ranks, beside = TRUE)
Bayesian NMA with multinma
Using multinma Package
library(multinma)
nma_data <- set_agd_arm(
data = arm_data,
study = study,
trt = treatment,
r = events,
n = n
)
plot(nma_data)
nma_fit <- nma(
nma_data,
trt_effects = "random",
prior_intercept = normal(scale = 10),
prior_trt = normal(scale = 10),
prior_het = half_normal(scale = 1)
)
summary(nma_fit)
print(nma_fit, pars = "d")
relative_effects(nma_fit, all_contrasts = TRUE)
Population-Adjusted NMA
library(multinma)
ipd_data <- set_ipd(
data = ipd_df,
study = study,
trt = treatment,
y = outcome
)
agd_data <- set_agd_arm(
data = agd_df,
study = study,
trt = treatment,
r = events,
n = n
)
combined_network <- combine_network(ipd_data, agd_data)
nma_cov <- nma(
combined_network,
trt_effects = "random",
regression = ~age + sex
)
Consistency Assessment
Global Inconsistency Tests
library(netmeta)
decomp <- decomp.design(net)
print(decomp)
decomp$Q.decomp
decomp$Q.het.design
decomp$Q.inc.detach
netheat(net)
Local Inconsistency (Node-Splitting)
library(netmeta)
netsplit_result <- netsplit(net)
print(netsplit_result)
forest(netsplit_result)
library(gemtc)
nodesplit <- mtc.nodesplit(network, comparisons = NULL)
ns_results <- lapply(nodesplit, function(model) {
mtc.run(model, n.adapt = 5000, n.iter = 20000)
})
mtc.nodesplit.comparisons(nodesplit)
Inconsistency Model
library(gemtc)
model_ume <- mtc.model(
network,
type = "ume",
likelihood = "binom",
link = "logit"
)
result_ume <- mtc.run(model_ume, n.adapt = 5000, n.iter = 20000)
summary(result)$DIC
summary(result_ume)$DIC
mtc.deviance(result)
Heterogeneity Assessment
library(netmeta)
net$tau
net$tau2
net$I2
net$Q
net$df.Q
net$pval.Q
net$lower.predict
net$upper.predict
Network Meta-Regression
library(netmeta)
net_reg <- netmeta(
TE = TE,
seTE = seTE,
treat1 = treat1,
treat2 = treat2,
studlab = study,
data = pairwise_data,
sm = "MD"
)
library(gemtc)
network_cov <- mtc.network(
data.ab = network_data$data.ab,
studies = data.frame(
study = c("S1", "S2", "S3"),
year = c(2010, 2015, 2020)
)
)
model_reg <- mtc.model(
network_cov,
type = "regression",
regressor = list(coefficient = "shared", variable = "year")
)
result_reg <- mtc.run(model_reg, n.adapt = 5000, n.iter = 20000)
summary(result_reg)
Subgroup and Sensitivity Analysis
library(netmeta)
net_low_rob <- netmeta(
TE = TE[rob == "low"],
seTE = seTE[rob == "low"],
treat1 = treat1[rob == "low"],
treat2 = treat2[rob == "low"],
studlab = study[rob == "low"],
sm = "MD"
)
comparison_table <- data.frame(
Model = c("All studies", "Low RoB only"),
Tau = c(net$tau, net_low_rob$tau),
Effect_B_vs_A = c(net$TE.random["B", "A"], net_low_rob$TE.random["B", "A"])
)
Reporting and Visualization
PRISMA-NMA Flow Diagram
Comparison-Adjusted Funnel Plot
library(netmeta)
funnel(net,
order = c("A", "B", "C", "D"),
pooled = "random",
pch = 1:4,
legend = TRUE)
Summary of Evidence Table
create_nma_summary <- function(net, ref = "A") {
treatments <- net$trts[net$trts != ref]
summary_df <- data.frame(
Comparison = paste(treatments, "vs", ref),
Effect_Fixed = sapply(treatments, function(t) net$TE.fixed[t, ref]),
CI_Lower_Fixed = sapply(treatments, function(t) net$lower.fixed[t, ref]),
CI_Upper_Fixed = sapply(treatments, function(t) net$upper.fixed[t, ref]),
Effect_Random = sapply(treatments, function(t) net$TE.random[t, ref]),
CI_Lower_Random = sapply(treatments, function(t) net$lower.random[t, ref]),
CI_Upper_Random = sapply(treatments, function(t) net$upper.random[t, ref])
)
summary_df <- summary_df |>
mutate(
Fixed = paste0(round(Effect_Fixed, 2), " (",
round(CI_Lower_Fixed, 2), ", ",
round(CI_Upper_Fixed, 2), ")"),
Random = paste0(round(Effect_Random, 2), " (",
round(CI_Lower_Random, 2), ", ",
round(CI_Upper_Random, 2), ")")
) |>
select(Comparison, Fixed, Random)
return(summary_df)
}
Key Packages Summary
| Package | Purpose |
|---|
| netmeta | Frequentist NMA (contrast-based) |
| gemtc | Bayesian NMA with JAGS |
| multinma | Bayesian NMA with Stan |
| bnma | Bayesian NMA |
| pcnetmeta | Patient-centered NMA |
| NMAoutlier | Outlier detection in NMA |
| nmathresh | Decision thresholds for NMA |
Best Practices
- Network geometry: Check connectivity before analysis
- Transitivity: Assess similarity of study populations across comparisons
- Consistency: Always assess local and global inconsistency
- Heterogeneity: Report tau, I², and consider prediction intervals
- Ranking: Present uncertainty (CrI for ranks, rankograms)
- Sensitivity: Conduct analyses excluding high RoB studies
- Reporting: Follow PRISMA-NMA extension guidelines
- Model selection: Compare fixed vs random effects, check model fit