This vignette is written for baggr users who want to analyse binary data. For more general introduction to meta-analysis concepts and baggr workflow, please read vignette("baggr")
.
Here, we will show how to:
There are certain aspects of modelling binary data that are not yet covered by baggr, but may be important for your data:
Typically, a meta-analysis of binary data is done on summary statistics such as \(\log(OR)\) or \(\log(RR)\). The reason for this is two-fold: 1) they are the statistics most commonly reported by studies and 2) they are approximately normally distributed. The second assumption needs to be treated carefully, as we will show later.
For the first example we will use a simple summary data based on (part of) Table 6 in Yusuf et al. (1985), a famous study of impact of beta blockers on occurrence of strokes and mortality.
<- read.table(text="
df_yusuf trial a n1i c n2i
Balcon 14 56 15 58
Clausen 18 66 19 64
Multicentre 15 100 12 95
Barber 10 52 12 47
Norris 21 226 24 228
Kahler 3 38 6 31
Ledwich 2 20 3 20
", header=TRUE)
In a typical notation, a
(c
) are numbers of events in treatment (control) groups, while n1
(n2
) are total patients in treatment (control) group. We can also calculate \(b = n_1 - a\) and \(d = n_2 - c\), i.e. numbers of “non-events” in treatment and control groups respectively.
In our examples we will use OR metric; \(\log(OR)\) and its SE can easily be calculated from the values (if you’re not familiar with OR, see here):
# This is a calculation we could do by hand:
# df <- df_yusuf
# df$b <- df$n1i-df$a
# df$d <- df$n2i-df$c
# df$tau <- log((df$a*df$d)/(df$b*df$c))
# df$se <- sqrt(1/df$a + 1/df$b + 1/df$c + 1/df$d)
# But prepare_ma() automates these operations:
<- prepare_ma(df_yusuf, group = "trial", effect = "logOR")
df_ma
df_ma#> group a n1 c n2 b d tau se
#> 1 Balcon 14 56 15 58 42 43 -0.04546237 0.4303029
#> 2 Clausen 18 66 19 64 48 45 -0.11860574 0.3888993
#> 3 Multicentre 15 100 12 95 85 83 0.19933290 0.4169087
#> 4 Barber 10 52 12 47 42 35 -0.36464311 0.4855042
#> 5 Norris 21 226 24 228 205 204 -0.13842138 0.3147471
#> 6 Kahler 3 38 6 31 35 25 -1.02961942 0.7540368
#> 7 Ledwich 2 20 3 20 18 17 -0.46262352 0.9735052
We use tau
and se
notation for our effect, same as we would for analysing continuous data with baggr()
. In fact, the model we use for logOR is the same default “Rubin” model (Rubin (1974)) with partial pooling. Once \(\log(OR)\) and is SE have been calculated, there are no differences between this process and analysis continuous quantities in baggr.
<- baggr(df_ma, effect = "logarithm of odds ratio") bg_model_agg
The argument we specified, effect
, does not impact results, but it makes outputs nicer, as we will see in the next section.
As with continuous quantities, the prior is chosen automatically. However, it is important to review the automatic prior choice when analysing quantities on log scale, because the priors may be needlessly diffuse (in other words, when using the command above baggr does not “know” that data are logged, although it does try to adjust the scale of priors).
We summarised our data as log(OR). Alternatively, we could work with log(RR). Risk difference is also a simple model that could be useful in some cases. Each model makes different assumptions on how the treatment of interest works. How do we choose the appropriate model for our data?
The basic tool that can help us choose is a labbe
plot (introduced by L’Abbé, Detsky, and O’Rourke (1987)), which is built into baggr:
labbe(df_ma, plot_model = TRUE, shade_se = "or")
#> Warning: There were 5 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
#> Warning: There were 4 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
#> Warning: Removed 1 row(s) containing missing values (geom_path).
When we make the plot, two Bayesian models are run, one for OR and one for RR. The mean treatment effect from these 2 lines is then used to plot the dotted/dashed lines corresponding to OR and RR estimates. In our case we can see that there is no meaningful difference between the two models, given the size of the effect and estimates.
Risk ratio is also normally distributed and can be easily calculated from contingency tables (i.e. a
, b
, c
, d
columns). All of the analysis flow would be identical in that case – with exception of interpretation of treatment effect, of course.
Before we present the results for the OR model, a reminder on how risk and odd ratios compare. If an event is rare (rule of thumb: up to 10%), OR and RR will be similar. For really rare events there is no difference. The higher the event rate, the more discrepancy, e.g.
<- 9; b <- 1; c <- 99; d <- 1
a cat("Risk ratio is", (a/(a+b))/(c/(c+d)), "\n" )
#> Risk ratio is 0.9090909
cat("Odds ratio is", a*d/(b*c), "\n")
#> Odds ratio is 0.09090909
<- 10; b <- 20; c <- 100; d <- 100
a cat("Risk ratio is", (a/(a+b))/(c/(c+d)), "\n" )
#> Risk ratio is 0.6666667
cat("Odds ratio is", a*d/(b*c), "\n")
#> Odds ratio is 0.5
We can illustrate the similarities and differences of OR and RR with one example:
par(mfrow = c(2,3), oma = rep(2,4))
for(es in c(1, .9, .8, .5, .25, .1)){
<- seq(0,1,length=100)
p_bsl <- es*p_bsl
p_trt_rr <- es*(p_bsl/(1-p_bsl))
odds_trt <- odds_trt / (1 + odds_trt)
p_trt_or plot(p_trt_or ~ p_bsl, type = "l",
xlab = "control event rate", ylab = "treatment event rate", main = paste0("RR=OR=",es))
lines(p_trt_rr ~ p_bsl, lty = "dashed")
}title(outer = TRUE, "Compare RR (dashed) and OR (solid) of the same magnitude")
A very good discussion of these choices and intuitive plots are provided by Deeks (2002) (see especially Figures 1-3).
All of the outputs are explained in more detail in the “main” package vignette, vignette("baggr")
. Here, I simply illustrate their behaviour.
bg_model_agg#> Model type: Rubin model with aggregate data
#> Pooling of effects: partial
#>
#> Aggregate treatment effect (on logarithm of odds ratio):
#> Hypermean (tau) = -0.16 with 95% interval -0.59 to 0.25
#> Hyper-SD (sigma_tau) = 0.24 with 95% interval 0.01 to 0.80
#> Total pooling (1 - I^2) = 0.84 with 95% interval 0.35 to 1.00
#>
#> Treatment effects on logarithm of odds ratio:
#> mean sd 2.5% 50% 97.5% pooling
#> Balcon -0.130 0.25 -0.63 -0.135 0.40 0.77
#> Clausen -0.149 0.24 -0.63 -0.145 0.33 0.74
#> Multicentre -0.068 0.26 -0.53 -0.084 0.50 0.76
#> Barber -0.201 0.27 -0.81 -0.187 0.29 0.80
#> Norris -0.148 0.22 -0.58 -0.147 0.30 0.68
#> Kahler -0.251 0.33 -1.10 -0.214 0.31 0.89
#> Ledwich -0.175 0.33 -0.90 -0.162 0.48 0.92
Default plot (plot(bg_model_agg)
) will show pooled group results. If you prefer a typical forest plot style, you can use forest_plot
, which can also plot comparison with source data (via print
argument):
forest_plot(bg_model_agg, show = "both", print = "inputs")
Hypothetical treatment effect in a new trial is obtained through effect_plot
.
effect_plot(bg_model_agg)
We can transform printed and plotted outputs to show exponents (or other transforms); for example:
::grid.arrange(
gridExtraplot(bg_model_agg, transform = exp) + xlab("Effect on OR"),
effect_plot(bg_model_agg, transform = exp) + xlim(0, 3) + xlab("Effect on OR"),
ncol = 2)
We can compare with no pooling and full pooling models using baggr_compare
# Instead of writing...
# bg1 <- baggr(df_ma, pooling = "none")
# bg2 <- baggr(df_ma, pooling = "partial")
# bg3 <- baggr(df_ma, pooling = "full")
# ...we use this one-liner
<- baggr_compare(df_ma, effect = "logarithm of odds ratio") bg_c
plot(bg_c)
We can also examine the compared models directly, by accessing them via $models
. This is useful e.g. for effect_plot
:
effect_plot(
"Partial pooling, default prior" = bg_c$models$partial,
"Full pooling, default prior" = bg_c$models$full) +
theme(legend.position = "bottom")
You can use leave-one-out cross-validation to compare the models quantitatively. See documentation of ?loocv
for more details of calculation.
<- loocv(df_ma, pooling = "partial")
a <- loocv(df_ma, pooling = "full")
b #a; b; #you can print out individual loocv() calculations
loo_compare(a,b) #...but typically we compare them to each other
Let’s assume that you have access to underlying individual-level data. In our example above, we do not, but we can create a data.frame with individual level data since we know the contingency table. In practice this is not necessary as baggr will do the appropriate conversions behind the scenes, but for our example we will convert our data by hand. Fortunately, a built-in function, binary_to_individual
can do the conversion for us:
<- binary_to_individual(df_yusuf, group = "trial")
df_ind head(df_ind)
#> group treatment outcome
#> 1 Balcon 1 1
#> 2 Balcon 1 1
#> 3 Balcon 1 1
#> 4 Balcon 1 1
#> 5 Balcon 1 1
#> 6 Balcon 1 1
We can now use a logistic regression model
<- baggr(df_ind, model = "logit", effect = "logarithm of odds ratio") bg_model_ind
Note: as in other cases,
baggr()
will detect model appropriately (in this case by noting thatoutcome
has only 0’s and 1’s) and notify the user, so in everyday use, it is not necessary to setmodel = "logit"
. Alternatively, if we wrotebaggr(df_yusuf, model = "logit")
, the conversion to individual-level data would be done automatically behind the scenes.
Note: The results of this vignette hosted on CRAN have very short run time (
iter = 500, chains = 2
) due to server constraints. We recommend replicating this example yourself.
If we denote binary outcome as \(y\), treatment as \(z\) (both indexed over individual units by \(i\)), under partial pooling the logistic regression model assumes that
\[ y_i \sim \text{Bernoulli}(\text{logit}^{-1}[\mu_{\text{group}(i)} + \tau_{\text{group}(i)}z_i]) \]
where \(\mu_k\)’s and \(\tau_k\)’s are parameters to estimate. The former is a group-specific mean probability of event in untreated units. We are primarily interested in the latter, the group-specific treatment effects.
Under this formulation, odds ratio of event between treatment and non-treatment are given by \(\tau_k\). This means, the modelling assumption is the same as in the model of summary data we saw above. Moreover, baggr’s default prior for \(\tau_k\) is set in the same way for summary and individual-level data (you can see it as bg_model_ind$formatted_prior
). One difference is that parameter \(\mu\) is estimated. However, unless dealing with extreme values of \(\mu\), i.e. with rare or common events (which we discuss below), this should not impact the modelled result.
Therefore, the result we get from the two models will be close (albeit with some numerical variation):
baggr_compare(bg_model_agg, bg_model_ind)
#>
#> Mean treatment effects:
#> 2.5% mean 97.5% median sd
#> Model 1 -0.592475 -0.163363 0.252405 -0.159529 0.207259
#> Model 2 -0.621807 -0.178877 0.244590 -0.163909 0.220379
#>
#> SD for treatment effects:
#> 2.5% mean 97.5% median sd
#> Model 1 0.0099659 0.241417 0.796619 0.180275 0.217139
#> Model 2 0.0130397 0.270591 0.792229 0.205181 0.227678
#>
#> Posterior predictive effects:
#> 2.5% mean 97.5% median sd
#> Model 1 -0.970432 -0.161850 0.603902 -0.152583 0.370509
#> Model 2 -1.070650 -0.192026 0.627206 -0.180501 0.403952
There will be difference in speed – in our example logistic model has to work with 1101 rows of data, while the summary level model only had 7 datapoints. Therefore in “standard” cases you are better off summarising your data and using the faster aggregate data model.
To sum up:
Note: as shown above, you can only create input data for the logistic model from
a
,b
/n1
,c
,d
/n2
columns. You cannot do it from OR data only, as the probability in the untreated is then unknown.
In the previous example we analysed individual-level data and suggested that typically it is sufficient to work with summarised data, e.g. to speed up modelling. Summarising is also essential as it allows us to better understand the inputs and use diagnostics such as L’Abbe plot.
The generic function for doing this in baggr is prepare_ma
. It can be used with both continuous and binary data. In our case we can summarise df_ind
to obtain either odds ratios or risk ratios:
prepare_ma(df_ind, effect = "logOR")
#> group a n1 c n2 b d tau se
#> 1 Balcon 14 56 15 58 42 43 -0.04546237 0.4303029
#> 2 Clausen 18 66 19 64 48 45 -0.11860574 0.3888993
#> 3 Multicentre 15 100 12 95 85 83 0.19933290 0.4169087
#> 4 Barber 10 52 12 47 42 35 -0.36464311 0.4855042
#> 5 Norris 21 226 24 228 205 204 -0.13842138 0.3147471
#> 6 Kahler 3 38 6 31 35 25 -1.02961942 0.7540368
#> 7 Ledwich 2 20 3 20 18 17 -0.46262352 0.9735052
prepare_ma(df_ind, effect = "logRR")
#> group a n1 c n2 b d tau se
#> 1 Balcon 14 56 15 58 42 43 -0.03390155 0.3209310
#> 2 Clausen 18 66 19 64 48 45 -0.08483888 0.2782276
#> 3 Multicentre 15 100 12 95 85 83 0.17185026 0.3598245
#> 4 Barber 10 52 12 47 42 35 -0.28341767 0.3779232
#> 5 Norris 21 226 24 228 205 204 -0.12472076 0.2836811
#> 6 Kahler 3 38 6 31 35 25 -0.89674614 0.6643991
#> 7 Ledwich 2 20 3 20 18 17 -0.40546511 0.8563488
In both cases the effect (OR or RR) and its SE were renamed to tau
and se
, so that the resulting data frame can be used directly as input to baggr()
.
For already summarised data, you can use the same function to move between different effect measures. For example you can take OR measures a <- prepare_ma(df_ind, effect = "logOR")
and convert them to RR by using prepare_ma(a, effect = "logRR")
.
Consider data on four fictional studies:
<- data.frame(group = paste("Study", LETTERS[1:5]),
df_rare a = c(0, 2, 1, 3, 1), c = c(2, 2, 3, 3, 5),
n1i = c(120, 300, 110, 250, 95),
n2i = c(120, 300, 110, 250, 95))
df_rare#> group a c n1i n2i
#> 1 Study A 0 2 120 120
#> 2 Study B 2 2 300 300
#> 3 Study C 1 3 110 110
#> 4 Study D 3 3 250 250
#> 5 Study E 1 5 95 95
In Study A you can see that no events occurred in a
column.
We have shown above how prepare_ma()
can be used to summarise the rates (if we work with individual-level data) and calculate/convert between log OR’s and log RR’s. It can also be used to apply corrections to event rates, which it does automatically:
<- prepare_ma(df_rare, effect = "logOR")
df_rare_logor #> Applied default rare event correction (0.25) in 1 study
# df_rare_logor <- prepare_ma(df_rare_ind, effect = "logOR")
df_rare_logor#> group a n1 c n2 b d tau se
#> 1 Study A 0.25 120.25 2.25 120.25 120.25 118.25 -2.213996 2.1121593
#> 2 Study B 2.00 300.00 2.00 300.00 298.00 298.00 0.000000 1.0033501
#> 3 Study C 1.00 110.00 3.00 110.00 109.00 107.00 -1.117131 1.1626923
#> 4 Study D 3.00 250.00 3.00 250.00 247.00 247.00 0.000000 0.8214401
#> 5 Study E 1.00 95.00 5.00 95.00 94.00 90.00 -1.652923 1.1053277
Note how the output of prepare_ma
now differs from the original df_rare
for “Study A”: a (default) value of 0.25 was added, because there were no events in treatment arm. That means \(\log(OR)=\log(0)=-\infty\). It is typical to correct for rare events when analysing summary level data. A great overview of the subject and how different meta-analysis methods perform is provided by Bradburn et al. (2007). You can modify the amount of correction by setting the rare_event_correction
argument.
<- prepare_ma(df_rare, effect = "logOR",
pma01 rare_event_correction = 0.1)
<- prepare_ma(df_rare, effect = "logOR",
pma1 rare_event_correction = 1)
pma01#> group a n1 c n2 b d tau se
#> 1 Study A 0.1 120.1 2.1 120.1 120.1 118.1 -3.061315 3.2392876
#> 2 Study B 2.0 300.0 2.0 300.0 298.0 298.0 0.000000 1.0033501
#> 3 Study C 1.0 110.0 3.0 110.0 109.0 107.0 -1.117131 1.1626923
#> 4 Study D 3.0 250.0 3.0 250.0 247.0 247.0 0.000000 0.8214401
#> 5 Study E 1.0 95.0 5.0 95.0 94.0 90.0 -1.652923 1.1053277
It is important to compare different models with different rare_event_correction
values:
<- baggr(pma01, effect = "logOR")
bg_correction01 <- baggr(df_rare_logor, effect = "logOR")
bg_correction025 <- baggr(pma1, effect = "logOR")
bg_correction1 <- baggr(df_rare, model = "logit", effect = "logOR") bg_rare_ind
<- baggr_compare(
bgc1 "Correct by .10" = bg_correction01,
"Correct by .25" = bg_correction025,
"Correct by 1.0" = bg_correction1,
"Individual data" = bg_rare_ind
)
bgc1#>
#> Mean treatment effects:
#> 2.5% mean 97.5% median sd
#> Correct by .10 -2.51203 -0.778586 0.559743 -0.722063 0.730008
#> Correct by .25 -2.21778 -0.740446 0.735501 -0.700311 0.718989
#> Correct by 1.0 -2.14776 -0.695989 0.501903 -0.665544 0.639930
#> Individual data -3.42799 -1.051640 0.555629 -0.964462 0.932139
#>
#> SD for treatment effects:
#> 2.5% mean 97.5% median sd
#> Correct by .10 0.0320293 1.038220 3.44729 0.751045 1.058990
#> Correct by .25 0.0573392 0.979203 3.28069 0.747933 0.854921
#> Correct by 1.0 0.0355791 0.817400 2.78088 0.626471 0.754252
#> Individual data 0.0465321 1.439280 5.17476 1.007400 1.556050
#>
#> Posterior predictive effects:
#> 2.5% mean 97.5% median sd
#> Correct by .10 -4.33682 -0.782998 2.36306 -0.667824 1.68742
#> Correct by .25 -4.01153 -0.787199 2.59755 -0.713487 1.55014
#> Correct by 1.0 -3.47875 -0.663525 1.89645 -0.663352 1.29393
#> Individual data -5.94063 -1.027490 2.97608 -0.923239 2.25331
plot(bgc1) + theme(legend.position = "right")
> Note: The results of this vignette hosted on CRAN have very short run time (iter = 500, chains = 2
) due to server constraints. We recommend replicating this example yourself.
Note in the result above that:
We will explore the last point in more detail soon. First, however, let us note that the issue with modelling rare events is not limited to zero-events. As mentioned, \(\log(OR)\) is approximately Gaussian. The quality of the approximation will depend on probabilities in all cells of the contingency table (which we estimate through a
, b
, c
, d
). Therefore, treating \(\log(OR)\) as Gaussian might lead to different results in the individual-level vs summary-level models if the events are rare. With low counts in our example it will definitely be the case.
Let us generate similar data as above but now without the 0 cell:
<- data.frame(group = paste("Study", LETTERS[1:5]),
df_rare a = c(1, 2, 1, 3, 1), c = c(2, 2, 3, 3, 5),
n1i = c(120, 300, 110, 250, 95),
n2i = c(120, 300, 110, 250, 95))
<- prepare_ma(df_rare, effect = "logOR") df_rare_logor
<- baggr(df_rare_logor, effect = "logOR")
bg_rare_agg <- baggr(df_rare, effect = "logOR", model = "logit") bg_rare_ind
Let’s compare again, both on group level but also in terms of hypothetical treatment effect:
<- baggr_compare(
bgc2 "Summary-level (Rubin model on logOR)" = bg_rare_agg,
"Individual-level (logistic model)" = bg_rare_ind
)
bgc2#>
#> Mean treatment effects:
#> 2.5% mean 97.5% median sd
#> Summary-level (Rubin model on logOR) -2.03322 -0.594534 0.757456 -0.580524 0.692283
#> Individual-level (logistic model) -2.10898 -0.640781 1.697050 -0.660518 0.771650
#>
#> SD for treatment effects:
#> 2.5% mean 97.5% median sd
#> Summary-level (Rubin model on logOR) 0.0319960 0.821064 2.98571 0.592833 0.798774
#> Individual-level (logistic model) 0.0397361 0.984583 3.65714 0.695107 0.995077
#>
#> Posterior predictive effects:
#> 2.5% mean 97.5% median sd
#> Summary-level (Rubin model on logOR) -3.26031 -0.571274 2.41385 -0.585260 1.36712
#> Individual-level (logistic model) -3.46293 -0.601783 2.44283 -0.654009 1.43838
plot(bgc2)
The results are still a bit different.
So far we’ve been using the logistic model with default priors (that are zero-centered and scaled to data). Our justification was that the priors on treatment effects are the same for both individual-level and aggregate-level data models. However, recall the logistic model includes additional \(K\) parameters, log odds of event in the control arms. (For simplicity we refer to them as baselines.) There are different ways in which we can assign them priors:
prior_control
argument when callin baggr
, using the same syntax as for other priors, e.g. normal(0, 5)
.prior_control
(which is now interpreted as hypermean, rather than \(K\) independent priors) and prior_control_sd
(hyper-SD for baseline parameters). To enable the hierarchical structure of baselines we need to specify pooling_control = "partial"
(analogously to specifying pooling
argument for treatment effects).Choosing between the three is especially important in the case of rare events, as this will have a potentially large impact on the results. Let’s continue with the example from previous section and consider three models:
bg_rare_ind
),We exaggerate our prior choices on purpose, to best illustrate the differences.
<- baggr(df_rare, effect = "logOR", model = "logit",
bg_rare_pool_bsl pooling_control = "partial",
prior_control = normal(-4.59, 1), prior_control_sd = normal(0, 2))
<- baggr(df_rare, effect = "logOR", model = "logit",
bg_rare_strong_prior prior_control = normal(-4.59, 10))
<- baggr_compare(
bgc3 "Rubin model" = bg_rare_agg,
"Independent N(0,10^2)" = bg_rare_ind,
# "Prior N(-4.59, 2^2)" = bg_rare_prior2,
"Hierarchical prior" = bg_rare_pool_bsl,
"Independent N(-4.59, 10^2)" = bg_rare_strong_prior
)
bgc3#>
#> Mean treatment effects:
#> 2.5% mean 97.5% median sd
#> Rubin model -2.03322 -0.594534 0.757456 -0.580524 0.692283
#> Independent N(0,10^2) -2.10898 -0.640781 1.697050 -0.660518 0.771650
#> Hierarchical prior -3.40316 -0.906489 0.231484 -0.720164 0.843882
#> Independent N(-4.59, 10^2) -2.10850 -0.710652 0.771733 -0.739816 0.693946
#>
#> SD for treatment effects:
#> 2.5% mean 97.5% median sd
#> Rubin model 0.0319960 0.821064 2.98571 0.592833 0.798774
#> Independent N(0,10^2) 0.0397361 0.984583 3.65714 0.695107 0.995077
#> Hierarchical prior 0.0313130 0.759884 2.51616 0.536101 0.692717
#> Independent N(-4.59, 10^2) 0.0487667 0.946172 3.89155 0.680980 0.968723
#>
#> Posterior predictive effects:
#> 2.5% mean 97.5% median sd
#> Rubin model -3.24154 -0.583101 2.15720 -0.572413 1.35052
#> Independent N(0,10^2) -3.64900 -0.682170 2.61133 -0.719023 1.52753
#> Hierarchical prior -4.11429 -0.892347 1.49875 -0.707084 1.42302
#> Independent N(-4.59, 10^2) -3.31953 -0.741245 2.56536 -0.871970 1.59607
plot(bgc3) + theme(legend.position = "right")
Note: The results of this vignette hosted on CRAN have very short run time (
iter = 500, chains = 2
) due to server constraints. We recommend replicating this example yourself. In particular for hierarchical prior on baselines, we recommend longer runs.
We can see that
Each of the models considered could now be compared using loocv()
(see the example earlier in the vignette) and their out-of-sample performance can be assessed.
This section provides only a short example. To learn more about meta-regression see e.g. Baker et al. (2009)
Two types of covariates may be present in your data:
In both cases we only need to name one extra argument to baggr
: covariates=
, followed by a vector of column names in input data. You should remember that your treatment effect estimate will vary a lot not only with choice of covariates, but also the contrasts that you use.
#let's use the data.frame we created from Yusuf et al earlier
$study_grouping <- c(1,1,1,0,0,0,0)
df_ma$different_contrasts <- c(1,1,1,0,0,0,0) - .5
df_ma<- baggr(df_ma, covariates = c("study_grouping"), effect = "logarithm of odds ratio") bg_cov1
baggr_compare("No covariate" = bg_model_agg,
"With covariates, 0-1 coding" = bg_cov1)
#> Compared models use different covariates. Hyperparameters are not directly comparable.
#>
#> Mean treatment effects:
#> 2.5% mean 97.5% median sd
#> No covariate -0.592475 -0.163363 0.252405 -0.159529 0.207259
#> With covariates, 0-1 coding -1.051430 -0.352260 0.326648 -0.342322 0.340114
#>
#> SD for treatment effects:
#> 2.5% mean 97.5% median sd
#> No covariate 0.0099659 0.241417 0.796619 0.180275 0.217139
#> With covariates, 0-1 coding 0.0101796 0.306131 1.048080 0.228047 0.306088
#>
#> Posterior predictive effects:
#> 2.5% mean 97.5% median sd
#> No covariate -0.971674 -0.159899 0.556726 -0.151191 0.383449
#> With covariates, 0-1 coding -1.491100 -0.352570 0.724153 -0.347732 0.551297
#>
#> Mean (SD) for covariates:
#> model study_grouping
#> No covariate 0.366148 (0.484953)
To access the covariate estimates, see fixed_effects()
function. They are also printed out by default:
bg_cov1#> Model type: Rubin model with aggregate data
#> Pooling of effects: partial
#>
#> Aggregate treatment effect (on logarithm of odds ratio):
#> Hypermean (tau) = -0.35 with 95% interval -1.05 to 0.33
#> Hyper-SD (sigma_tau) = 0.31 with 95% interval 0.01 to 1.05
#> Total pooling (1 - I^2) = 0.79 with 95% interval 0.23 to 1.00
#>
#> Treatment effects on logarithm of odds ratio:
#> mean sd 2.5% 50% 97.5% pooling
#> Balcon -0.0097 0.30 -0.62 -0.0091 0.58 0.71
#> Clausen -0.0302 0.29 -0.63 -0.0229 0.53 0.68
#> Multicentre 0.0748 0.30 -0.51 0.0686 0.68 0.70
#> Barber -0.3514 0.33 -1.04 -0.3428 0.27 0.74
#> Norris -0.2564 0.27 -0.77 -0.2598 0.28 0.62
#> Kahler -0.4507 0.42 -1.43 -0.4055 0.28 0.84
#> Ledwich -0.3538 0.43 -1.27 -0.3380 0.48 0.89
#>
#> Covariate (fixed) effects on logarithm of odds ratio:
#> [,1]
#> mean 0.37
#> sd 0.48
Note: in the example above we did not manually set priors for \(\beta\) coefficients. Users can do it by passing argument
prior_beta
tobaggr()
.