---
title: "Gaussian aggregate outcomes"
author: "metaGLMM authors"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Gaussian aggregate outcomes}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>",
                      fig.width = 6, fig.height = 4)
set.seed(20260821)
library(metaGLMM)
```

## Weighted study estimates

A Gaussian analysis uses one aggregate estimate and its sampling variance per
row.  The sample size `ni` is retained in the fitted object and can be used by
downstream code, while the Gaussian weighted likelihood is driven by `vi`.

```{r data}
gaussian_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),
  moderator = c(0, 1, 0, 1, 0, 1, 0, 1),
  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)
)
gaussian_dat
```

## Basic random-effects analysis and forest plot

For a single-group meta-analysis, fit an intercept-only model.  Each input row
is already one study estimate, so `as_metafor_data()` can pass the study
estimates and sampling variances directly to `forest()`.

```{r basic-random-fit}
gaussian_fit <- metaGLMM(
  estimate ~ 1,
  data = gaussian_dat,
  vi = gaussian_dat$vi,
  ni = gaussian_dat$ni,
  tau2 = NA,
  family = gaussian(link = "identity"),
  tau2_var = TRUE,
  fast = TRUE
)

summary(gaussian_fit)
coef(gaussian_fit)
confint(gaussian_fit, method = "wald")
stopifnot(is.finite(gaussian_fit$tau), gaussian_fit$tau > 0)
```

The study rows show the supplied Gaussian estimates and their sampling
intervals.  The lower rows compare the three supported fixed-effect intervals
and the plug-in prediction interval.

```{r basic-forest}
gaussian_studies <- as_metafor_data(
  gaussian_fit, labels = gaussian_dat$study
)
head(gaussian_studies)

forest(
  gaussian_fit,
  labels = gaussian_dat$study,
  xlab = "Effect estimate",
  ci_methods = c("Wald", "profile", "SBC")
)
```

## Fixed-effect reference

Setting `tau2_var = FALSE` with `tau2 = 0` requests the exact no-random-effect
model.  Its fixed-effect estimates can be compared with the weighted least
squares reference in base R.

```{r fixed-fit}
fixed_fit <- metaGLMM(
  estimate ~ moderator,
  data = gaussian_dat,
  vi = gaussian_dat$vi,
  ni = gaussian_dat$ni,
  tau2 = 0,
  tau2_var = FALSE,
  family = gaussian(link = "identity"),
  fast = TRUE
)

X <- model.matrix(~ moderator, data = gaussian_dat)
W <- sweep(X, 1, gaussian_dat$vi, "/")
beta_wls <- solve(crossprod(X, W),
                  crossprod(X, gaussian_dat$estimate / gaussian_dat$vi))
names(beta_wls) <- colnames(X)

cbind(metaGLMM = coef(fixed_fit), weighted_least_squares = beta_wls)
```

This no-random-effect fit is an analytical reference only.  The next section
uses the same study summaries while estimating a non-zero between-study
component.

## Estimating heterogeneity

Set `tau2 = NA` and `tau2_var = TRUE` to estimate the non-negative
between-study variance.  `tau` is its square root.

```{r random-fit}
random_fit <- metaGLMM(
  estimate ~ moderator,
  data = gaussian_dat,
  vi = gaussian_dat$vi,
  ni = gaussian_dat$ni,
  tau2 = NA,
  tau2_var = TRUE,
  family = gaussian(link = "identity"),
  fast = TRUE
)

summary(random_fit)
random_fit$tau2
random_fit$tau
logLik(random_fit)
nobs(random_fit)
stopifnot(is.finite(random_fit$tau), random_fit$tau > 0)
```

The alternative `tau2_param = "log_tau2"` uses an unconstrained optimization
parameterization while reporting `tau2` on its original scale.  Both
parameterizations expose the same fixed-effect API.

```{r log-parameterization, eval=FALSE}
random_fit_log <- metaGLMM(
  estimate ~ moderator,
  data = gaussian_dat,
  vi = gaussian_dat$vi,
  ni = gaussian_dat$ni,
  tau2 = NA,
  tau2_var = TRUE,
  tau2_param = "log_tau2",
  family = gaussian(link = "identity"),
  fast = TRUE
)
summary(random_fit_log)
```

## Formula and contrasts

The model matrix is the source of truth for coefficient names.  This remains
true for no-intercept models, factors, and interactions.

```{r formula-contract}
gaussian_dat$design <- factor(c("A", "A", "B", "B", "A", "B", "A", "B"))
interaction_fit <- metaGLMM(
  estimate ~ 0 + design + moderator:design,
  data = gaussian_dat,
  vi = gaussian_dat$vi,
  ni = gaussian_dat$ni,
  tau2 = 0,
  tau2_var = FALSE,
  family = gaussian(link = "identity"),
  fast = TRUE
)

colnames(model.matrix(interaction_fit))
names(coef(interaction_fit))
```
