---
title: "Prediction and forest plots"
author: "metaGLMM authors"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Prediction and forest plots}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>",
                      fig.width = 7, fig.height = 5)
set.seed(20260821)
library(metaGLMM)
```

## A fitted model

```{r fit}
dat <- data.frame(
  study = paste0("Study ", 1:8),
  estimate = c(-0.80, -0.30, 0.10, 0.70, 1.10, -0.50, 0.40, 1.30),
  vi = c(0.04, 0.05, 0.03, 0.06, 0.04, 0.05, 0.03, 0.05),
  ni = c(100, 90, 120, 80, 110, 95, 130, 85)
)

fit <- metaGLMM(
  estimate ~ 1,
  data = dat,
  vi = dat$vi,
  ni = dat$ni,
  tau2 = NA,
  tau2_var = TRUE,
  family = gaussian(link = "identity"),
  fast = TRUE
)
stopifnot(is.finite(fit$tau), fit$tau > 0)
```

## Prediction for a future study

`predict.metaGLMM()` accepts either `newdata` or a numeric contrast matrix,
but not both.  Neither is required for this intercept-only example: the
confidence interval targets the pooled mean, while the prediction interval
targets the underlying true effect in one future study.

```{r prediction}
pred_conf <- predict(fit, type = "link", interval = "confidence")
pred_pred <- predict(fit, type = "link", interval = "prediction",
                     random_scale = 1)
pred_response <- predict(fit, type = "response", interval = "confidence")

pred_conf
pred_pred
pred_response
stopifnot(diff(as.numeric(pred_pred[1, c("lower", "upper")])) >
            diff(as.numeric(pred_conf[1, c("lower", "upper")])))
```

A confidence interval uses `X V_beta X'`.  A prediction interval adds
`random_scale^2 * tau2` for a future study-specific effect; the observed
study's sampling variance `vi` is not added.  Set `random_scale = 0` for
the fixed mean, or use another loading when the random effect has a known
scale.

```{r contrast}
L <- matrix(1, nrow = 1L, ncol = 1L,
            dimnames = list("future study" = "(Intercept)"))
predict(fit, contrast = L, type = "link",
        interval = "prediction", random_scale = 1)
```

For a log or logit link, `type = "exp"` returns the exponentiated prediction
and interval endpoints.  It is restricted to those link scales so that
transformations remain explicit.

## Forest plots

The conservative forest interface accepts study-level estimates, variances,
and labels explicitly.  This is the safest choice for arm-level or otherwise
complex input.

```{r forest}
plot_data <- forest(
  fit,
  estimate = dat$estimate,
  vi = dat$vi,
  labels = dat$study,
  parm = "(Intercept)",
  ci_methods = c("wald", "profile", "SBC"),
  ci_args = list(renge.c = 10, silent = TRUE),
  xlab = "Effect estimate"
)
plot_data[plot_data$kind != "study", ]
```

The lower panel shows the selected fixed-effect intervals as filled diamonds,
followed by the plug-in prediction interval.  `ci_methods` is case-insensitive
and preserves the requested order.  Profile and SBC rows require a single
coefficient selected by `parm`; arbitrary linear contrasts are available for
the Wald row only.  The prediction row remains the Wald-type plug-in interval
described above, rather than a profile- or Bartlett-corrected prediction
interval.  The right column reports each estimate and its limits numerically.

The same display can be requested through the generic plot method:

```{r plot-method}
plot(fit, type = "forest",
     estimate = dat$estimate,
     vi = dat$vi,
     labels = dat$study,
     parm = "(Intercept)",
     ci_methods = c("wald", "profile", "SBC"),
     ci_args = list(renge.c = 10, silent = TRUE),
     xlab = "Effect estimate")
```

Both calls use base graphics and request Wald, profile, and SBC confidence
intervals.  A prediction interval is included when the fitted heterogeneity
is finite.
Automatic extraction is available only when a one-row-per-study structure (or
a safely matched two-arm structure) is evident; otherwise pass the vectors as
above.

## Plain data exchange

`as_metafor_data()` returns an ordinary `data.frame` with the conventional
`yi`, `vi`, and `slab` columns.  It is useful for exchanging study-level
summaries without changing the fitted object's class.

```{r exchange}
exchange_dat <- as_metafor_data(fit)
stopifnot(is.data.frame(exchange_dat))
head(exchange_dat[c("yi", "vi", "slab")])
```
