Skip to contents

The default, and the argument

Every claim in powerape is tested at alpha = 0.05 in the form its literature uses: detection is the two-sided test read off the 95% interval, the minimum-effect claim a one-sided test and equivalence a TOST, both read off the 90% interval. The level is an argument of every power function, so a stricter convention is one keyword away:

library(powerape)

d <- ape_dgp(model = "probit",
             focal = pa_var("program", "binary", p = 0.5),
             covariates = list(pa_var("z", "normal")),
             baseline = 0.30, signal = 0.10)
d <- set_ape(d, target = 0.10)

ape_power(d, n = 2000, claim = "minimum", sesoi = 0.05, nsim = 400, seed = 1)
#> powerape -- minimum-effect claim (CI lower bound > 0.050)
#>   probit, n = 2000, assumed true APE +0.1000, 90% CI (alpha = 0.05), nsim = 400
#>   power = 0.787 (MCSE 0.020)
#>   outcomes: minimum 0.787 | detect-only 0.212 | inconclusive 0.000 | equivalence 0.000 | failed 0.000
ape_power(d, n = 2000, claim = "minimum", sesoi = 0.05, alpha = 0.01,
          nsim = 400, seed = 1)
#> powerape -- minimum-effect claim (CI lower bound > 0.050)
#>   probit, n = 2000, assumed true APE +0.1000, 98% CI (alpha = 0.01), nsim = 400
#>   power = 0.542 (MCSE 0.025)
#>   outcomes: minimum 0.542 | detect-only 0.445 | inconclusive 0.012 | equivalence 0.000 | failed 0.000
powerape dgp d, focal(program, binary p(0.5)) covariates((z, normal))     ///
    baseline(0.30) signal(0.10)
powerape setape d, target(0.10)

powerape power d, n(2000) claim(minimum) sesoi(0.05) nsim(400) seed(1)
powerape power d, n(2000) claim(minimum) sesoi(0.05) alpha(0.01) nsim(400) seed(1)
powerape -- minimum-effect claim (CI lower bound > 0.050)
  probit, n = 2000, assumed true APE + 0.1000,     90% CI (alpha = .05), nsim = 400
  fit: probit via engine(stata), effects(closed)
  power = 0.767 (MCSE 0.021)
  outcomes: minimum 0.767 | detect-only 0.230 | inconclusive 0.003 | equivalence 0.000 | failed 0.000

powerape -- minimum-effect claim (CI lower bound > 0.050)
  probit, n = 2000, assumed true APE + 0.1000,     98% CI (alpha = .01), nsim = 400
  fit: probit via engine(stata), effects(closed)
  power = 0.522 (MCSE 0.025)
  outcomes: minimum 0.522 | detect-only 0.468 | inconclusive 0.010 | equivalence 0.000 | failed 0.000

Maier and Lakens (2022) give two reasons not to stop at the default. First, with a fixed budget of observations the error rate that minimizes the combined cost of Type I and Type II errors is rarely exactly .05. Second, in large samples a p value just below .05 can be more likely under the null than under the alternative (Lindley’s paradox), so the level should fall as n grows. powerape supports both justifications.

Alpha as a function of the sample size

alpha may be a function of n. It is evaluated at every candidate sample size, so ape_n() designs jointly over the pair (alpha(n), n), and ape_curve(), ape_mde(), and ape_robust() use the level that belongs to their n. Any rule works; the natural one for regression coefficients is the Bayes-factor calibration of Wulff and Taylor (2024) in the alphaN package, where BF = 1 is the largest level that avoids Lindley’s paradox and BF = 3 asks a significant result to carry moderate evidence.

rule <- function(n) alphaN::alphaN(n, BF = 3)
rule(c(500, 2000, 8000))
#> [1] 0.0037278668 0.0017468970 0.0008248697

ape_n(d, power = 0.80, claim = "minimum", sesoi = 0.05, alpha = rule,
      nsim = 300, seed = 2, confirm = FALSE)
#> powerape required sample size -- minimum claim
#>   n = 5191 for 80% target power (achieved 0.823, MCSE 0.022)
#>   assumed true APE +0.1000, sesoi 0.050, 99.79% CI (alpha = 0.001042 by sample-size rule), probit
#>   search: 1 step(s); confirm with ape_power() at a larger nsim.
* alphaN's rule alphaN(n, BF = 3) (Wulff & Taylor, 2024) as an expression in @n
display chi2tail(1, ln(500) + 2*ln(3)), chi2tail(1, ln(2000) + 2*ln(3)), ///
    chi2tail(1, ln(8000) + 2*ln(3))

powerape n d, power(0.80) claim(minimum) sesoi(0.05)                     ///
    alpharule(chi2tail(1, ln(@n) + 2*ln(3)))                              ///
    nsim(300) seed(2) noconfirm engine(mata)
.00372787 .0017469 .00082487

powerape required sample size -- minimum claim
  n = 5189 for 80% target power (achieved 0.763, MCSE 0.025)
  assumed true APE + 0.1000, sesoi 0.050,  99.79% CI (alpha =   .001 by sample-size rule), probit
  fit: Mata engine (Fisher scoring)
  search: 1 step(s); confirm with powerape power at a larger nsim.

The requirement rises well above the fixed-.05 answer (about 2,100 for this design), because the level keeps tightening as the search moves to larger n. The returned object stores the rule and the level it produced at the chosen n, and power_statement() says so.

pw_rule <- ape_power(d, n = 5000, claim = "minimum", sesoi = 0.05,
                     alpha = rule, nsim = 400, seed = 3)
pw_rule
#> powerape -- minimum-effect claim (CI lower bound > 0.050)
#>   probit, n = 5000, assumed true APE +0.1000, 99.79% CI (alpha = 0.001063 by sample-size rule), nsim = 400
#>   power = 0.790 (MCSE 0.020)
#>   outcomes: minimum 0.790 | detect-only 0.210 | inconclusive 0.000 | equivalence 0.000 | failed 0.000
power_statement(pw_rule)
#> We conducted a simulation-based power analysis for the average partial effect
#> (APE) of program using the powerape package (version 1.11.4), following the
#> confidence-interval approach of Riesthuis (2024). The assumed data-generating
#> process was a probit model with focal variable program (binary, prevalence
#> 0.50); parametric covariates z (continuous, mean 0, SD 1), with the focal
#> variable and the covariates mutually independent; baseline outcome rate 0.300
#> with the focal at reference; nuisance covariates contribute a latent
#> pseudo-R-squared of 0.10. The assumed true APE was 0.100 (10.0 percentage
#> points). At a sample size of n = 5000, simulated power for the minimum-effect
#> claim (the 99.79% confidence interval's lower bound exceeding the smallest
#> effect size of interest, 0.050; a one-sided test at alpha = 0.106%, set as a
#> function of the sample size) was 0.790 (Monte Carlo SE 0.020; 400
#> replications). Across replications, the probability of concluding a
#> meaningful effect was 0.790, of detection without meaningfulness 0.210, of an
#> inconclusive result 0.000, and of equivalence 0.000. Estimation used maximum
#> likelihood with delta-method Wald confidence intervals; replications without
#> a maximum-likelihood estimate, from a failed fit or from separation (a focal
#> cell with no events or no non-events), counted against the claim (0.0% of
#> replications).
powerape power d, n(5000) claim(minimum) sesoi(0.05)                      ///
    alpharule(chi2tail(1, ln(@n) + 2*ln(3))) nsim(400) seed(3)
powerape statement
powerape -- minimum-effect claim (CI lower bound > 0.050)
  probit, n = 5000, assumed true APE + 0.1000,  99.79% CI (alpha =  .0011 by sample-size rule), nsim = 400
  fit: probit via engine(stata), effects(closed)
  power = 0.802 (MCSE 0.020)
  outcomes: minimum 0.802 | detect-only 0.198 | inconclusive 0.000 | equivalence 0.000 | failed 0.000

We conducted a simulation-based power analysis for the average partial effect
(APE) of program using the powerape package for Stata (version 0.1.3 (Stata
port of powerape 1.11.3)), following the confidence-interval approach of
Riesthuis (2024). The assumed data-generating process was a probit model with
focal variable program (binary, prevalence 0.50); parametric covariates z
(continuous, mean 0, SD 1), with the focal variable and the covariates
mutually independent; baseline outcome rate 0.300 with the focal at reference;
nuisance covariates contribute a latent pseudo-R-squared of 0.10. The assumed
true APE was 0.100 (10.0 percentage points). At a sample size of n = 5000,
simulated power for the minimum-effect claim (the 99.79% confidence interval's
lower bound exceeding the smallest effect size of interest, 0.050; a one-sided
test at alpha = .106%, set as a function of the sample size) was 0.802 (Monte
Carlo SE 0.020; 400 replications). Across replications, the probability of
concluding a meaningful effect was 0.802, of detection without meaningfulness
0.198, of an inconclusive result 0.000, and of equivalence 0.000. Estimation
used maximum likelihood with delta-method Wald confidence intervals;
replications without a maximum-likelihood estimate, from a failed fit or from
separation (a focal cell with no events or no non-events), counted against the
claim (0.0% of replications).

Alpha from error costs

The second route is decision-theoretic. For a design with a fixed n, the weighted combined error rate

w(α)=cost⋅α+prior⋅β(α)cost+prior w(\alpha) = \frac{cost \cdot \alpha + prior \cdot \beta(\alpha)}{cost + prior}

weighs a Type I error cost times as heavily as a Type II error and the two hypotheses by their prior odds (Mudge et al., 2012). powerape evaluates the whole curve from one simulation: ape_power() keeps the estimate and standard error of every replication, and power at any level is a re-threshold of those draws. power_at() exposes the curve; ape_alpha() minimizes or balances it.

pw <- ape_power(d, n = 2000, claim = "minimum", sesoi = 0.05,
                nsim = 1000, seed = 4)
power_at(pw, alpha = c(0.005, 0.01, 0.025, 0.05, 0.10))
#>   alpha conf power       mcse minimum detect_only inconclusive equivalence
#> 1 0.005 0.99 0.485 0.01580427   0.485       0.498        0.017           0
#> 2 0.010 0.98 0.581 0.01560253   0.581       0.413        0.006           0
#> 3 0.025 0.95 0.696 0.01454593   0.696       0.301        0.003           0
#> 4 0.050 0.90 0.778 0.01314215   0.778       0.222        0.000           0
#> 5 0.100 0.80 0.876 0.01042228   0.876       0.124        0.000           0
#>   failed
#> 1      0
#> 2      0
#> 3      0
#> 4      0
#> 5      0

ja <- ape_alpha(pw, cost = 4, prior = 1)      # Cohen's 4:1 weighting
ja
#> powerape justified alpha -- minimum-effect claim (CI lower bound > 0.050), n = 2000, nsim = 1000
#>   objective: minimize the weighted combined error rate (Type I cost x4, prior odds H1:H0 = 1)
#>   alpha* = 0.0290 -> power 0.713 (MCSE 0.014), beta 0.287, weighted combined error 0.0806
#>   at alpha = 0.05: power 0.778, weighted combined error 0.0844
#>   flat range: alpha in [0.0190, 0.0460] is indistinguishable from the optimum at this nsim
#>   cap 0.050 not binding (unconstrained optimum 0.0290)
plot(ja)

powerape power d, n(2000) claim(minimum) sesoi(0.05) nsim(1000) seed(4) name(pw)
powerape powerat pw, alpha(0.005 0.01 0.025 0.05 0.10)
powerape alpha pw, cost(4) prior(1)

local opt = r(alpha)
local lo  = r(range_lo)
local hi  = r(range_hi)
matrix G = r(grid)
svmat G, names(col)
twoway line wcer alpha, xline(`opt') xline(`lo' `hi', lpattern(dash))     ///
    ytitle("Weighted combined error rate") xtitle("Alpha")
powerape -- minimum-effect claim (CI lower bound > 0.050)
  probit, n = 2000, assumed true APE + 0.1000,     90% CI (alpha = .05), nsim = 1000
  fit: probit via engine(stata), effects(closed)
  power = 0.796 (MCSE 0.013)
  outcomes: minimum 0.796 | detect-only 0.202 | inconclusive 0.002 | equivalence 0.000 | failed 0.000

power at other error rates (re-thresholding the stored draws of pw)
    alpha      conf    power     mcse
   0.0050    0.9900   0.4560   0.0158
   0.0100    0.9800   0.5450   0.0157
   0.0250    0.9500   0.6930   0.0146
   0.0500    0.9000   0.7960   0.0127
   0.1000    0.8000   0.8920   0.0098

powerape justified alpha -- minimum-effect claim (CI lower bound > 0.050), n = 2000, nsim = 1000
  objective: minimize the weighted combined error rate (Type I cost x4, prior odds H1:H0 = 1)
  alpha* = 0.0420 -> power 0.774 (MCSE 0.013), beta 0.226, weighted combined error 0.0788
  at alpha = 0.05: power 0.796, weighted combined error 0.0808
  flat range: alpha in [0.0235, 0.0500] is indistinguishable from the optimum at this nsim
  cap 0.050 not binding (unconstrained optimum 0.0420)
The weighted combined error rate against alpha, with the optimum and the range of levels indistinguishable from it.

Two things to notice. The objective is flat around its optimum, so the result reports the range of levels that cannot be told apart from the optimum at this number of replications; that range, not the point, is what to preregister unless nsim is large. And the search is capped at .05: when the unconstrained optimum lies above the conventional level, as it does for designs below their required n with equal error costs, the function says so rather than returning it, following Maier and Lakens’s advice that raising alpha needs a justification of its own and that journals may accept only lowering.

ape_alpha(pw, cost = 1, prior = 1)
#> powerape justified alpha -- minimum-effect claim (CI lower bound > 0.050), n = 2000, nsim = 1000
#>   objective: minimize the weighted combined error rate (Type I cost x1, prior odds H1:H0 = 1)
#>   alpha* = 0.0500 -> power 0.778 (MCSE 0.013), beta 0.222, weighted combined error 0.1360
#>   at alpha = 0.05: power 0.778, weighted combined error 0.1360
#>   flat range: alpha in [0.0400, 0.0500] is indistinguishable from the optimum at this nsim
#>   cap 0.050 BINDS: unconstrained optimum alpha = 0.1165 (weighted combined error 0.1082);
#>     raising alpha above the conventional level needs its own justification (Maier & Lakens, 2022).
powerape alpha pw, cost(1) prior(1)
powerape justified alpha -- minimum-effect claim (CI lower bound > 0.050), n = 2000, nsim = 1000
  objective: minimize the weighted combined error rate (Type I cost x1, prior odds H1:H0 = 1)
  alpha* = 0.0490 -> power 0.795 (MCSE 0.013), beta 0.205, weighted combined error 0.1270
  at alpha = 0.05: power 0.796, weighted combined error 0.1270
  flat range: alpha in [0.0425, 0.0500] is indistinguishable from the optimum at this nsim
  cap 0.050 BINDS: unconstrained optimum alpha = 0.1140 (weighted combined error 0.1020);
    raising alpha above the conventional level needs its own justification (Maier & Lakens, 2022).

Balancing instead of minimizing asks for cost * alpha = prior * beta; at a design’s required n for 80% power, Cohen’s 4:1 weighting returns the familiar .05/.20 pair by construction.

The frontier level: one alpha for a whole MDE grid

ape_alpha() needs a pinned effect, because its Type II error is one minus power at the planning value. In a minimum-detectable-effect analysis the effect is the answer, not an input, and re-optimizing the level at every candidate effect chases a moving target. The way out is a fixed point: the level that is optimal for the effect that is just detectable at the target power. Under the normal approximation that first-order condition loses both n and the standard error, so one level prices every sample size, and alpha_frontier() returns it in closed form.

alpha_frontier(cost = 4, power = 0.80)                    # two-sided, 0.0274
#> [1] 0.02737173
alpha_frontier(cost = 1, power = 0.80, claim = "minimum") # exactly 1 - power
#> [1] 0.2
powerape frontier, cost(4) power(0.80)
powerape frontier, cost(1) power(0.80) claim(minimum)
frontier alpha (detect claim, cost 4, power 0.80, prior 1) =   0.0274

frontier alpha (minimum claim, cost 1, power 0.80, prior 1) =   0.2000

Passing it as alpha re-prices an MDE analysis (or a grid of them) under the justified level:

ape_mde(d, n = 3000, claim = "detect",
        alpha = alpha_frontier(cost = 4, power = 0.80), seed = 6)
powerape frontier, cost(4) power(0.80)
powerape mde d, n(3000) claim(detect) alpha(`r(alpha)') seed(6)

At equal costs the one-sided frontier level is exactly 1 - power – the frontier is where alpha and beta balance. Unlike ape_alpha(), the frontier level is an approximation (normal, and pinned to the just-detectable effect) and carries no .05 cap, so treat levels above the convention with the same caution as an uncapped ape_alpha() optimum.

Checking the size, and reporting

Because the Type I error of a SESOI claim is its size at the claim boundary, size = TRUE simulates the world in which the true effect equals the SESOI and reports the realized error rate at the chosen level, something analytic compromise-power tools cannot do. On the standard routes it matches the nominal level within Monte Carlo error; on clustered panels with few units it may not, and then the realized number is the one to report.

ja4 <- ape_alpha(pw, cost = 4, prior = 1, size = TRUE, nsim_size = 500,
                 seed = 5)
ja4
#> powerape justified alpha -- minimum-effect claim (CI lower bound > 0.050), n = 2000, nsim = 1000
#>   objective: minimize the weighted combined error rate (Type I cost x4, prior odds H1:H0 = 1)
#>   alpha* = 0.0290 -> power 0.713 (MCSE 0.014), beta 0.287, weighted combined error 0.0806
#>   at alpha = 0.05: power 0.778, weighted combined error 0.0844
#>   flat range: alpha in [0.0190, 0.0460] is indistinguishable from the optimum at this nsim
#>   cap 0.050 not binding (unconstrained optimum 0.0290)
#>   realized size at the claim boundary (true APE = 0.050): 0.0240 (MCSE 0.0068, nsim 500)
power_statement(ja4)
#> We chose the error rate for the minimum-effect claim (the 94.2% confidence
#> interval's lower bound exceeding the smallest effect size of interest, 0.050;
#> a one-sided test at alpha = 2.9%) by minimizing the weighted combined Type I
#> and Type II error rate (Maier & Lakens, 2022; Mudge et al., 2012), weighting
#> a Type I error 4 times a Type II error and taking prior odds of 1 for the
#> planning value (APE = 0.100) against the claim boundary, using the powerape
#> package (version 1.11.4). The assumed data-generating process was a probit
#> model with focal variable program (binary, prevalence 0.50); parametric
#> covariates z (continuous, mean 0, SD 1), with the focal variable and the
#> covariates mutually independent; baseline outcome rate 0.300 with the focal
#> at reference; nuisance covariates contribute a latent pseudo-R-squared of
#> 0.10. At n = 2000 the optimum within the conventional cap of alpha = 0.05 was
#> alpha = 0.0290: simulated power 0.713 (Monte Carlo SE 0.014; 1000
#> replications), a Type II error rate of 0.287, and a weighted combined error
#> rate of 0.0806 versus 0.0844 at alpha = 0.05; error rates between 0.0190 and
#> 0.0460 are indistinguishable from the optimum at this precision. A
#> verification simulation at the claim boundary (true APE = 0.050; 500
#> replications) gave a realized error rate of 0.0240 (Monte Carlo SE 0.0068) at
#> the chosen alpha.
powerape alpha pw, cost(4) prior(1) size nsimsize(500) seed(5)
powerape statement _lastalpha
powerape justified alpha -- minimum-effect claim (CI lower bound > 0.050), n = 2000, nsim = 1000
  objective: minimize the weighted combined error rate (Type I cost x4, prior odds H1:H0 = 1)
  alpha* = 0.0420 -> power 0.774 (MCSE 0.013), beta 0.226, weighted combined error 0.0788
  at alpha = 0.05: power 0.796, weighted combined error 0.0808
  flat range: alpha in [0.0235, 0.0500] is indistinguishable from the optimum at this nsim
  cap 0.050 not binding (unconstrained optimum 0.0420)
  realized size at the claim boundary (true APE = 0.050): 0.0380 (MCSE 0.0086, nsim 500)

We chose the error rate for the minimum-effect claim (the 91.6% confidence
interval's lower bound exceeding the smallest effect size of interest, 0.050;
a one-sided test at alpha = 4.2%) by minimizing the weighted combined Type I
and Type II error rate (Maier & Lakens, 2022; Mudge et al., 2012), weighting a
Type I error 4 times a Type II error and taking prior odds of 1 for the
planning value (APE = 0.100) against the claim boundary, using the powerape
package for Stata (version 0.1.3 (Stata port of powerape 1.11.3)). The assumed
data-generating process was a probit model with focal variable program
(binary, prevalence 0.50); parametric covariates z (continuous, mean 0, SD 1),
with the focal variable and the covariates mutually independent; baseline
outcome rate 0.300 with the focal at reference; nuisance covariates contribute
a latent pseudo-R-squared of 0.10. At n = 2000 the optimum within the
conventional cap of alpha = .05 was alpha = 0.0420: simulated power 0.774
(Monte Carlo SE 0.013; 1000 replications), a Type II error rate of 0.226, and
a weighted combined error rate of 0.0788 versus 0.0808 at alpha = 0.05; error
rates between 0.0235 and 0.0500 are indistinguishable from the optimum at this
precision. A verification simulation at the claim boundary (true APE = 0.050;
500 replications) gave a realized error rate of 0.0380 (Monte Carlo SE 0.0086)
at the chosen alpha.

The statement names the objective, the weights, the level, the flat range, the cap, and the verification, ready for a preregistration.

References

Maier, M., & Lakens, D. (2022). Justify your alpha: A primer on two practical approaches. Advances in Methods and Practices in Psychological Science, 5(2).

Mudge, J. F., Baker, L. F., Edge, C. B., & Houlahan, J. E. (2012). Setting an optimal alpha that minimizes errors in null hypothesis significance tests. PLOS ONE, 7(2), e32734.

Wulff, J. N., & Taylor, L. (2024). How and why alpha should depend on sample size: A Bayesian-frequentist compromise for significance testing. Strategic Organization, 22(3), 550–581.