Back to Claude Scientific Skills

Closed-Form Power Recipes

skills/statistical-power/references/closed_form_recipes.md

2.52.06.6 KB
Original Source

Closed-Form Power Recipes

Exact argument tables and the underlying statsmodels/scipy calls for every test the scripts/power.py helper supports. Use this when you need to call statsmodels directly, understand an argument, or handle a case the wrapper doesn't cover.

The four solver quantities — effect_size, sample size, alpha, power — obey one identity: pass three, set the fourth to None, and solve_power returns it.

Table of contents


Two independent means

Effect size = Cohen's d = (μ₁ − μ₂) / σ_pooled.

python
from statsmodels.stats.power import TTestIndPower
analysis = TTestIndPower()

# n per group for d=0.5, 80% power, two-sided
n1 = analysis.solve_power(effect_size=0.5, alpha=0.05, power=0.80,
                          ratio=1.0, alternative="two-sided")

# achieved power at n1=64 per group
pw = analysis.solve_power(effect_size=0.5, nobs1=64, alpha=0.05,
                          ratio=1.0, alternative="two-sided")

# minimum detectable d at n1=30 per group, 80% power
d_min = analysis.solve_power(nobs1=30, alpha=0.05, power=0.80, ratio=1.0,
                             alternative="two-sided")

ratio = nobs2 / nobs1. For 2:1 allocation set ratio=2.0; the returned nobs1 is the smaller group. alternative"two-sided", "larger", "smaller".

Paired / one-sample mean

Effect size = Cohen's dz for paired (mean difference / SD of the differences), or d for one-sample. Use TTestPower (single-sample solver); nobs is the number of pairs / observations.

python
from statsmodels.stats.power import TTestPower
TTestPower().solve_power(effect_size=0.4, alpha=0.05, power=0.80,
                         alternative="two-sided")  # -> number of pairs

Note: for paired designs dz depends on the within-pair correlation ρ: dz = d_raw / sqrt(2(1−ρ)). Higher ρ ⇒ larger dz ⇒ smaller n. If you only know the raw mean difference and SDs, estimate ρ or simulate.

One-way ANOVA

Effect size = Cohen's f = sqrt(η² / (1 − η²)). nobs here is total n across all groups; divide by k_groups for per-group n.

python
from statsmodels.stats.power import FTestAnovaPower
total_n = FTestAnovaPower().solve_power(effect_size=0.25, k_groups=4,
                                        alpha=0.05, power=0.80)
per_group = total_n / 4

Conversions: f = 0.10 (small), 0.25 (medium), 0.40 (large). From η²: f = sqrt(eta2/(1-eta2)). From R²: same formula with R².

Two proportions

Effect size = Cohen's h = 2·asin(√p₁) − 2·asin(√p₂). Convert proportions to h, then use the normal approximation NormalIndPower.

python
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportion_effectsize
h = proportion_effectsize(0.40, 0.55)
n1 = NormalIndPower().solve_power(effect_size=h, alpha=0.05, power=0.80,
                                  ratio=1.0, alternative="two-sided")

Alternative (exact-ish, gives per-group n directly, handles unequal n via ratio):

python
from statsmodels.stats.proportion import samplesize_proportions_2indep_onetail
# one-sided; double alpha intent by passing alpha/... per your convention

For small samples or rare events, prefer simulation with the exact test you'll run (Fisher's exact, or a chi-square with continuity correction).

One proportion

Test p against a fixed reference p₀. Convert both to the arcsine scale via Cohen's h and treat the reference group as infinite (ratio=0).

python
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportion_effectsize
h = proportion_effectsize(0.60, 0.50)
n = NormalIndPower().solve_power(effect_size=h, alpha=0.05, power=0.80, ratio=0.0)

For exact binomial planning use statsmodels.stats.proportion.proportion_effectsize with the exact-test power via simulation if the sample is small.

Correlation

Effect size = Pearson r. No statsmodels solver; use the Fisher z transform (implemented in power.py). Required n for r at power 1−β, two-sided:

z_r = arctanh(r)
n   = ((z_{1-α/2} + z_{1-β}) / z_r)^2 + 3

pingouin.power_corr(r=0.3, power=0.8, alternative="two-sided") gives the same answer if you prefer a library call.

Chi-square

Effect size = Cohen's w = sqrt(Σ (p_i − p0_i)² / p0_i). For a contingency table, w = sqrt(χ²/N) and equals Cramér's V·sqrt(min(r−1, c−1)). Degrees of freedom: goodness-of-fit dof = k − 1; contingency dof = (r−1)(c−1). n_bins = dof + 1.

python
from statsmodels.stats.power import GofChisquarePower
n = GofChisquarePower().solve_power(effect_size=0.3, n_bins=5, alpha=0.05, power=0.80)

w benchmarks: 0.10 (small), 0.30 (medium), 0.50 (large).

Multiple regression

Effect size = Cohen's f² = R²/(1−R²) for the overall model, or ΔR²/(1−R²_full) for a set of added predictors. power.py solves this directly via the noncentral F (noncentrality λ = f²·n), which is more reliable than statsmodels' FTestPower for sample-size search.

python
from power import sample_size, power
# detect f^2 = 0.15 from 3 tested predictors (3 total in the model)
sample_size("linear_regression", effect_size=0.15, df_num=3, k_total=3, power=0.80)
  • df_num = number of predictors being tested (the numerator df).
  • k_total = total predictors in the model (including controls). df_denom = n − k_total − 1.

f² benchmarks: 0.02 (small), 0.15 (medium), 0.35 (large).

Effect-size units per test

Testpower.py test=Effect sizeSmall / Medium / Large
Two independent meanst_indCohen's d0.2 / 0.5 / 0.8
Paired / one-samplet_paired, t_oneCohen's d (dz)0.2 / 0.5 / 0.8
One-way ANOVAanovaCohen's f0.1 / 0.25 / 0.4
Two proportionstwo_proportionsCohen's h (auto from props)0.2 / 0.5 / 0.8
One proportionone_proportionCohen's h (auto)0.2 / 0.5 / 0.8
CorrelationcorrelationPearson r0.1 / 0.3 / 0.5
Chi-squarechi2Cohen's w0.1 / 0.3 / 0.5
Regression (ΔR²)linear_regressionCohen's f²0.02 / 0.15 / 0.35

Benchmarks are last-resort conventions — prefer a smallest-effect-of-interest. See effect_sizes.md.