Fits the CAMS model (Panaro, Röver & Friede 2025) for consistent inference on treatment effect modification from modifier-level aggregate data (vignettes often use classic subgroup tables). Modifier-level treatment effects are inputs and derived outputs; the methodological target is not a separate meta-analysis within each modifier level.
Usage
cams(
x,
reference = NULL,
modifier = NULL,
trial = NULL,
subgroup = NULL,
trial_var = "study",
subgroup_var = "level",
contrast = "all",
contrast_sep = "-",
big_var = 100^2,
method = "Bayesian",
tau.prior = NULL,
tau.interaction.prior = NULL,
fixed.prior = NULL,
fit_args = list(),
precompute = FALSE,
lazy_latent = TRUE,
silent = 2,
seed = getOption("interactmeta.seed", 42L),
...
)Arguments
- x
A
metafor::escalcobject, a data frame withyi/vi, or output fromifrac()(must includeifrac_*design columns). Also accepts a data frame built from trial-level columns (trial,subgroup,yi,vi).- reference
Subgroup label used as reference in
ifrac()and in the model. Required whenxis not already anifrac()result.- modifier
Column that holds the two levels of the within-trial contrast (default
"subgroup", so the shipped examples keep working). Overridessubgroupandsubgroup_varwhen set. Level labels stay as stored. Display headings capitalize the first letter of this name (doseis shown asDose).- trial, subgroup
When
xisescalc/ a data frame, column names passed toifrac()(defaults"trial"and"subgroup").subgroupremains accepted for the default column name; prefermodifierfor the argument.- trial_var, subgroup_var
Column names used in the model (defaults match
trialandsubgroup).subgroup_varremains accepted.- contrast, contrast_sep, big_var
Passed to
ifrac()when information fractions are computed insidecams(). To append trial-level combined or interaction contrast rows to anifrac()table, callifrac()withinclude_combined/include_interactionbefore fitting.- method
Inferential method.
"Bayesian"(default) fits with bayesmeta. Any value accepted bymetafor::rma()for itsmethodargument fits both orthogonal frequentist blocks (e.g."REML","ML","DL","PM","EB","SJ","HE"/"CO","HS","HSk","EE","GENQ","PMM","GENQM"). Aliases"bayes"and"bayesian"match"Bayesian"."frequentist"selects"REML".- tau.prior, tau.interaction.prior
Heterogeneity priors for \(\tau_m\) (overall) and \(\tau_g\) (interaction), passed to bayesmeta. Defaults when both are omitted match bayesmeta: improper
"uniform"on both blocks (see Priors)."uniform"and"sqrt"are the improper bayesmeta densities. A half-normal or half-Cauchy is a density, for examplefunction(tau) bayesmeta::dhalfnormal(tau, scale = 0.5). Iftau.interaction.prior = NULLand the user suppliedtau.prior, the interaction prior inheritstau.prior.- fixed.prior
Normal prior for the bayesmeta fixed effects, as
c(mean = 0, sd = 10).NULLuses that default (needed for a stablebmroverall block with improper tau; not bayesmeta's improper flat \(\mu\)). Applied as independent normals on \((\alpha,\theta)\) and on \(\gamma\), not as an independent normal on the reconstructed \(\beta=\theta-\gamma\)."uniform"is the improper flat prior.- fit_args
List passed to the backend (bayesmeta quadrature or
metafor::rma()). Model choices such as priors do not belong here. For Bayesian fits, optionaln_support(integer) is forwarded to bothbayesmeta::bmr()andbayesmeta::bayesmeta()asn.support(fewer\taunodes → smaller DIRECT product grid).- precompute
If
TRUE, build the default parameter matrix and warm the effect cache before returning (legacy eager behaviour). DefaultFALSE:print.interactmeta(),summary.interactmeta(), andtidy()trigger reporting work on first use.- lazy_latent
For Bayesian fits, skip per-trial
convolve()marginals at fit time (defaultTRUE). Trial-level$latentobjects are built when shrinkage or other code needs them. SetFALSEfor the full latent at fit.- silent
Sampler verbosity; default
2suppresses messages.- seed
Random seed for reproducible draws (default from option
"interactmeta.seed", currently42L). Set toNAfor a non-reproducible draw. Ifseedis also passed in..., a warning is given and this argument wins.- ...
For
method = "Bayesian", bayesmeta quadrature options (e.g.delta,interval.type). For metafor methods, additionalrma()arguments (e.g.test,control). Merged intofit_argswhen not already set.
Value
A CAMS fit with class "interactmeta" plus attribute
"interactmeta" (plot settings and ifrac_data for forestplot()).
Details
cams() is the fitter.
By default, ifrac() is run inside the fitter on the input (e.g. a
metafor::escalc object). You can also pass an existing ifrac() tibble.
For two subgroups per trial, CAMS fits an information-fraction
meta-regression of combined coordinates \(m_j\) and a random-effects
meta-analysis of within-trial contrasts \(g_j\) (Panaro, Röver &
Friede 2025, §3.4). Default inference is Bayesian (bayesmeta);
method = "REML" (or other metafor::rma() methods) gives
frequentist inference.
Sampling assumption (within-trial covariance)
Within each trial,
\(g_j=y_{Bj}-y_{Aj}\) with \(\mathrm{Var}(g_j)=v_{Aj}+v_{Bj}\) and
information fraction
\(\pi_j=(1/v_{Bj})/(1/v_{Aj}+1/v_{Bj})\) (IV weight of the non-reference
subgroup). That choice makes \(\mathrm{Cov}(m_j,g_j)=0\) when
\(\mathrm{Cov}(y_{Aj},y_{Bj})=0\). This package currently assumes that
within-trial subgroup sampling covariance is zero. Separate overall and
interaction fits (bmr/bayesmeta or two rmas) rely on that
orthogonality. If subgroup estimates share a control arm or other
within-trial dependence, both \(\mathrm{Var}(g_j)\) and separate
fits for \(m_j\) and \(g_j\) are misspecified unless \(\pi_j\) and the variances are
redefined with the covariance (not implemented here).
Model
cams() fits the comparison CAMS model with free ecological /
composition slope \(\beta\) (b_ifrac): population lines
\(\mu_A=\alpha\), \(\mu_B=\alpha+\gamma+\beta\), \(M(\pi)=\alpha+\pi\beta+\gamma\).
Inference
CAMS can be fitted using Bayesian or frequentist inference. These are not
two different scientific models. Set method = "Bayesian" (default) for
bayesmeta, or any metafor::rma() method for the two
frequentist components (default frequentist choice is "REML"). See
vignette("cams").
For Bayesian fits, bayesmeta options may go in fit_args or ....
For metafor fits, further rma() arguments (e.g. test, control) may
be passed in ... or fit_args (fit_args wins on conflicts).
swada(), da(), and ad() are not alternative CAMS models. SWADA
does not use the CAMS information-fraction (IF) construction; IF
quantities and IF-based plots are not available for SWADA objects.
Comparison helpers may combine multiple user-supplied fits only when
those fits are passed explicitly.
Related analyses
CAMS is the statistical model. da() (Difference of Averages) and
ad() (Average Difference) are different constructions (Panaro,
Röver & Friede 2026) for a treatment-by-subgroup interaction from
subgroup-level aggregate data. They can use different study weights and
need not agree. swada() applies the SWADA weighting principle so
those analyses share one contribution vector; under the default
Interaction RE-weights, the DA interaction equals AD. conventional()
is a compatibility bundle of DA and AD. None of these is the 2002 van
Houwelingen joint bivariate mixed model.
Method
method selects inference: "Bayesian" uses bayesmeta; values documented
for metafor::rma() ("REML", "ML", "DL", "PM", …) use metafor on
both components.
The fit stores fit$method (e.g. "Bayesian", "REML"), fit$inference,
and fit$support (DIRECT quadrature support or one-row metafor estimate).
print.interactmeta() shows the same wording as
the cams(..., method = ) argument (Bayesian, REML, …).
Priors
Bayesian CAMS passes priors through to bayesmeta. They are stored on
fit$priors. Heterogeneity defaults match bayesmeta::bayesmeta():
- fixed effects
independent \(\mathrm{Normal}(0, 10)\) on the overall-block means \((\alpha,\theta)\) and on the interaction mean \(\gamma\), with \(\theta=\beta+\gamma\). Stored
b_ifracis the reconstructed ecological slope \(\beta=\theta-\gamma\); that prior is not an independent \(N(0,10)\) on \(\beta\). (bayesmeta's improper flat \(\mu\) is not the CAMS default: the overall block is fit withbmr, where a proper normal is the reliable companion to improper tau)tau.priorimproper uniform \(p(\tau_m)\propto 1\) (\(\tau_m \ge 0\)), bayesmeta's
tau.prior = "uniform"tau.interaction.priorthe same improper uniform on \(\tau_g\) when both heterogeneity priors are omitted. If
tau.interaction.priorisNULLand the user suppliedtau.prior, the interaction prior inherits that density
Character shorthands "uniform" and "sqrt" are the improper bayesmeta
densities \(p(\tau)\propto 1\) and \(p(\tau)\propto \tau^{-1/2}\)
(\(\tau \ge 0\)). They are not \(U(0, U)\).
Frequentist method values ignore Bayesian priors (warning if supplied).
Empirical heterogeneity (not the package default). CDSR calibration
suggests interaction heterogeneity is often substantially smaller than
overall treatment-effect heterogeneity, so a common weakly informative
half-normal on both blocks can be too diffuse for \(\tau_g\)
(Panaro, Röver & Friede, arXiv:2606.23968). That paper reports
measure-specific empirical scales (e.g. \(\mathrm{HN}(0.5)\)-style WIP
for overall log-ratio / SMD-type endpoints in Röver et al., 2021;
tighter interaction scales such as \(\mathrm{HN}(0.1)\) in sensitivity
analyses and \(\mathrm{HN}(0.07)\) empirically for log-ROR). Vignettes
set those half-normal densities explicitly; they are not silently
applied by cams().
Fixed-effect parameterization: bayesmeta/bmr receive independent
\(N(0, 10)\) on \((\alpha,\theta)\) (beta.prior.cov = diag(sd^2)) and
mu.prior on \(\gamma\). That prior factors across the two orthogonal
blocks. Independent normals on \((\alpha,\beta,\gamma)\) would couple
\(\theta\) and \(\gamma\) a priori and are not what this engine uses.
Intervals
For Bayesian CAMS, public extractors (modifiers(), interactions(),
combined(), predict_cams_by_if(), shrinkage()) report credible
intervals from the DIRECT-node Gaussian mixture (.weight and within-node
.cond_* moments). Default interval.type is "central" (equal-tailed);
pass interval.type = "shortest" to cams() for the narrowest interval rule.
Default combined() and interactions() at type = "Overall" with no
exponentiate use stored bmr / bayesmeta intervals when possible;
other requests still use the mixture grid.
plot_contribution() uses central (equal-tailed) mixture intervals for
ribbon limits by default (ribbon_interval.type). Alpha and theta are never
convolved with each other.
References
Panaro RV, Röver C, Friede T (2025). Consistent Bayesian meta-analysis on subgroup specific effects and interactions. arXiv:2512.18785. https://doi.org/10.48550/arXiv.2512.18785
Panaro RV, Röver C, Friede T (2026). Subgroup comparisons within and across studies in meta-analysis. Research Synthesis Methods (in press). arXiv:2508.15531. https://doi.org/10.48550/arXiv.2508.15531
Panaro RV, Röver C, Friede T (2026). Empirical prior distributions for treatment-by-subgroup interaction heterogeneity in random-effects meta-analysis. arXiv:2606.23968. https://doi.org/10.48550/arXiv.2606.23968
Röver C, Bender R, Dias S, Schmid CH, Schmidli H, Sturtz S, Weber S, Friede T (2021). On weakly informative prior distributions for the heterogeneity parameter in Bayesian random-effects meta-analysis. Research Synthesis Methods, 12(4), 448–474. https://doi.org/10.1002/jrsm.1475
Examples
if (FALSE) { # \dontrun{
library(interactmeta)
es <- escalc(measure = "IRR", yi = edss$yi, vi = edss$vi, data = edss)
fit <- cams(es, reference = "Lower", method = "Bayesian")
fit_reml <- cams(es, reference = "Lower", method = "REML")
forestplot(fit)
plot_contribution(fit)
plot(fit)
} # }
