---
title: "Effect sizes: maximal models, fitted pilots, and safeguard power"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Effect sizes: maximal models, fitted pilots, and safeguard power}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
set.seed(1)
```

The hardest part of a power analysis is rarely the simulation --- it is choosing
a defensible effect size and a realistic random-effects structure. This vignette
ties together four `mixpower` features that address exactly that:

- **maximal models** with one or more correlated random slopes,
- **`mp_from_fit()`** to drive power from an existing (pilot or published) fit,
- **`mp_sesoi()`** to plan around a *smallest effect size of interest*, and
- **`mp_safeguard_effect()`** for an uncertainty-aware, conservative effect,

and closes with how to read the **Type S / Type M** diagnostics that `mp_power()`
reports for free.

```{r}
library(mixpower)
```

## Maximal models: correlated random slopes

A within-participant manipulation usually varies *within* every subject, so the
"maximal" random-effects structure includes a random slope for it. Ignoring a
real random slope inflates the Type I error rate, so simulating from --- and
fitting --- the maximal model gives honest power.

`random_effects` takes a per-group `intercept_sd`, an optional named list of
`slopes`, and an optional correlation `cor` (a scalar applied to every pair of
terms, or a full correlation matrix). Each fixed effect you name also becomes a
balanced design predictor, so several effects are crossed as a factorial.

```{r}
d <- mp_design(clusters = list(subject = 20), trials_per_cell = 6)

a <- mp_assumptions(
  fixed_effects = list(`(Intercept)` = 0, x1 = 0.5, x2 = 0.3),
  random_effects = list(subject = list(
    intercept_sd = 0.4,
    slopes = list(x1 = 0.3, x2 = 0.3),
    cor = 0.1
  )),
  residual_sd = 1
)
a
```

```{r}
scn_max <- mp_scenario_lme4(
  y ~ x1 + x2 + (1 + x1 + x2 | subject),
  design = d,
  assumptions = a,
  predictor = "x1"
)

mp_power(scn_max, nsim = 15, seed = 2024)$power
```

For a single within-subject factor the same machinery reduces to the familiar
`(1 + condition | subject)` form with one slope and an intercept-slope
correlation.

## Power from a fitted pilot model

When you have a pilot or published model, `mp_from_fit()` turns it into a
scenario: new responses are simulated from the fit (keeping its estimated
variance components), the model is refit, and the focal term is tested. The
fixed effects start at the fitted coefficients but can be varied for planning.

```{r}
m <- lme4::lmer(Reaction ~ Days + (Days | Subject), data = lme4::sleepstudy)
scn_fit <- mp_from_fit(m, test_term = "Days")
scn_fit$assumptions
```

```{r}
mp_power(scn_fit, nsim = 15, seed = 1)$power
```

## Planning around a smallest effect size of interest

Pilot and published effects are often optimistic. A robust habit is to plan
power for the *smallest effect you would still care about*. `mp_sesoi()` returns
a copy of a scenario with the focal effect replaced --- either explicitly, or by
scaling the assumed effect (the default `multiplier = 0.85` is a conservative
15% reduction).

```{r}
scn_sesoi <- mp_sesoi(scn_fit, multiplier = 0.85)
c(
  full  = scn_fit$assumptions$fixed_effects$Days,
  sesoi = scn_sesoi$assumptions$fixed_effects$Days
)
```

Sweeping the multiplier shows how quickly power erodes as the effect of interest
shrinks --- the whole point of planning for a SESOI rather than the (large)
pilot estimate:

```{r}
mults <- c(1, 0.5, 0.3, 0.2)
powers <- vapply(
  mults,
  function(mult) mp_power(mp_sesoi(scn_fit, multiplier = mult), nsim = 15, seed = 1)$power,
  numeric(1)
)
data.frame(multiplier = mults, effect = mults * scn_fit$assumptions$fixed_effects$Days, power = powers)
```

## Safeguard power

A *safeguard* effect (Perugini, Gallucci & Costantini, 2014) goes one step
further: instead of a point estimate, it uses the confidence-interval bound
nearest zero, so sampling uncertainty in the pilot is propagated into the plan.
`mp_safeguard_effect()` computes it; feed the result straight into `mp_sesoi()`.

```{r}
sg <- mp_safeguard_effect(m, term = "Days", conf_level = 0.90)
sg
```

```{r}
scn_safe <- mp_sesoi(scn_fit, effect = sg)
mp_power(scn_safe, nsim = 15, seed = 1)$power
```

The safeguard effect (the lower confidence bound) is smaller than the point
estimate. Here the `sleepstudy` `Days` effect is large enough that even its
conservative bound stays well powered; for a borderline effect, that same buffer
is exactly what stops an over-optimistic pilot from promising power it cannot
deliver --- compare it against the multiplier sweep above.

## Reading Type S and Type M errors

Every `mp_power()` run reports two diagnostics that go beyond the power figure
(Gelman & Carlin, 2014):

- **Type S** (sign): among significant replicates, the fraction with the *wrong*
  sign.
- **Type M** (magnitude): among significant replicates, the average ratio of the
  estimated to the true effect --- the *exaggeration* factor.

At low power, significant estimates are systematically inflated (Type M well
above 1); at high power they are well calibrated.

```{r}
under <- mp_power(mp_sesoi(scn_fit, multiplier = 0.25), nsim = 25, seed = 7)
summary(under)
under$diagnostics$type_m
```

A Type M near 1 means significant estimates are about right; a value like 1.5
means published "hits" from this design would overstate the effect by ~50%. This
is why planning for a SESOI or safeguard effect --- and powering well --- matters
for replicable findings.
