Power curve over sample size for a mixed-effects design
Source:R/power_mixed.R
power_curve_mixed.RdSweep the number of subjects and compute mixed-effects power at each. Pass the result to
target_n() for the sample size at which power crosses a target, with an interval on it. A
thin wrapper around sweep_spec() over units$subject$n, kept because a sample-size curve is
the sweep users want most often. Requires the lme4 and lmerTest packages.
Arguments
- spec
A design specification (path or list).
- subject_ns
A numeric vector of subject counts to evaluate.
- focal
The fixed effects to test, or the named sets of coefficients to test jointly, as in
power_mixed().- n_sims
Number of Monte Carlo replicates per point. A power estimate carries a Monte Carlo standard error of about
sqrt(p * (1 - p) / n_sims), reported alongside it aspower_mcse. The default of 60 gives a standard error of 0.065 at a power of 0.5, which is too coarse to support a claim about a design; raise it to at least 200 for planning.- alpha
Significance level of the test, two-sided unless
alternativesays otherwise.- workers
Number of local worker processes over which to spread the replicates at each grid point. The default of 1 runs serially, and any worker count returns results identical to a serial run. One pool of that many processes serves the whole sweep.
- seeds
How the sample sizes are seeded:
"independent", the default, or"shared", as insweep_spec()'s.seeds.- alternative
The alternative hypothesis of every test,
"two.sided","greater"or"less", as inpower_mixed().
Value
A data frame with one row per sample size and focal effect, with columns n_subject,
effect, true, power, power_mcse, power_lo, power_hi, n_significant, type_s,
type_s_lo, type_s_hi, type_m, type_m_mcse, the n_attempted, n_returned,
n_converged, n_singular and n_warning fit counts, and fitter, as in power_mixed().
fitter names the direction of a one-sided test.
With sets of coefficients in focal, effect holds each set's name, a column coefficients
after it lists the set's coefficients, and fitter names the joint test.
n_singular typically falls as the sample size rises, so reading it down the sweep shows
where the model becomes supportable.
Details
Like power_mixed(), this runs pilotr's own simulation loop over the portable design
specification rather than wrapping an existing package, and differs from simr (Green and
MacLeod, 2016) and mixedpower (Kumle, Vo and Draschkow, 2021) in being driven by that
specification, in reporting Type S and Type M errors, and in built-in parallelisation: with
workers > 1 a single worker pool is created once and reused across all sample sizes.
Each sample size draws its own replicates, from the seed sweep_spec() gives its grid point,
so the points of the curve are independent, as target_n() assumes when it fits them.
seeds = "shared" runs every point from the specification's own seed instead, as up to 0.3.1.
For any axis other than sample size, call sweep_spec() directly. Effect size is the axis a
design analysis most often needs after sample size, and design_conditions() builds the
coefficient overrides for it.
The curve is the input to target_n(), which is where the sample size a preregistration
quotes should come from. Reading the crossing off a plot instead judges points whose Monte
Carlo intervals overlap, and reports the answer without the interval that goes with it.
In a crossed design, the number of items limits what more subjects can buy. When the effect of
a condition varies from item to item, as a by-item random slope says it does, part of the
standard error of the effect comes from the items sampled. That part shrinks only as items are
added. As subjects are added, the standard error therefore falls towards a floor set by the
number of items. Power at a fixed number of items levels off below 1 (Westfall, Kenny and Judd,
2014; Judd, Westfall and Kenny, 2017). The probit curve that target_n() fits rises towards 1
and cannot bend into such a plateau. A curve that levels off near the target can then be solved far
from the true crossing. One that levels off just below the target never crosses it, but Monte
Carlo noise can lift a simulated power over the target, and the curve can then be solved as
though it crossed. To compare designs that trade subjects for items, sweep units$item$n with
sweep_spec() at each of a few subject counts and solve each curve for the number of items, as
the power vignette shows. Power over items levels off in the same way, at a ceiling set by the
by-subject slope and the number of subjects. The subject counts must be large enough to put that
ceiling well above the target.
References
Green, P. and MacLeod, C. J. (2016). SIMR: An R package for power analysis of generalized linear mixed models by simulation. Methods in Ecology and Evolution, 7(4), 493-498. doi:10.1111/2041-210x.12504
Judd, C. M., Westfall, J. and Kenny, D. A. (2017). Experiments with more than one random factor: Designs, analytic models, and statistical power. Annual Review of Psychology, 68, 601-625. doi:10.1146/annurev-psych-122414-033702
Kumle, L., Vo, M. L.-H. and Draschkow, D. (2021). Estimating power in (generalized) linear mixed models: An open introduction and tutorial in R. Behavior Research Methods, 53, 2528-2543. doi:10.3758/s13428-021-01546-0
Westfall, J., Kenny, D. A. and Judd, C. M. (2014). Statistical power and optimal design in experiments in which samples of participants respond to samples of stimuli. Journal of Experimental Psychology: General, 143(5), 2020-2045. doi:10.1037/xge0000014
See also
target_n() to solve the returned curve for a sample size, sweep_spec() for any
other axis, and design_conditions() for effect-size grids.
Examples
# \donttest{
if (requireNamespace("lme4", quietly = TRUE) &&
requireNamespace("lmerTest", quietly = TRUE)) {
spec <- build_spec(list(name = "p", seed = 1, design_kind = "within",
include_items = TRUE, n_subject = 12, n_item = 12, factor_name = "cond",
lev1 = "a", lev2 = "b", intercept = 6, effect = 0.05,
subj_int_sd = 0.12, subj_slope_sd = 0.04, subj_corr = 0.2,
item_int_sd = 0.08, item_slope_sd = 0.02, item_corr = -0.1,
family = "shifted_lognormal", resp_name = "", sigma = 0.3, shift = 200))
# n_sims is small so the example runs quickly. Use 200 or more for real planning.
power_curve_mixed(spec, subject_ns = c(12, 18), n_sims = 8)
}
#> n_subject effect true power power_mcse power_lo power_hi n_significant
#> 1 12 effect 0.05 0.000 0.0000000 0.00000000 0.3244076 0
#> 2 18 effect 0.05 0.125 0.1169268 0.02241749 0.4708882 1
#> type_s type_s_lo type_s_hi type_m type_m_mcse n_attempted n_returned
#> 1 NA NA NA NA NA 8 8
#> 2 0 0 0.7934507 1.556333 NA 8 8
#> n_converged n_singular n_warning fitter
#> 1 3 5 0 lmerTest::lmer, Satterthwaite t
#> 2 0 8 0 lmerTest::lmer, Satterthwaite t
# }