This sample size calculator helps you determine the optimal sample size needed for your statistical tests. It provides comprehensive power analysis with visual charts showing the relationship between sample size, effect size, and statistical power. Whether you're comparing means (paired/unpaired), proportions, multiple groups (ANOVA), categorical associations (chi-square), or building predictive models (multiple regression), this calculator will help ensure your research has adequate statistical power to detect meaningful effects.
If you are looking to calculate the sample size based on a desired margin of error for confidence intervals, try our Margin of Error Sample Size Calculator.
Range: 0.1 to 2 (standard deviations)
Get properly formatted citations for academic work •20k+ calculations in the past 30 days
StatsCalculators Team. (2026). Sample Size & Power Analysis Calculator. StatsCalculators. Retrieved February 2, 2026 from https://statscalculators.com/calculators/hypothesis-testing/sample-size-and-power-analysis-calculator
Statistical power is the probability that your study will correctly detect an effect when there is one. Failing to do so results in a Type II error.
A power of 0.8 (or 80%) is typically considered adequate, indicating there is a 20% chance of overlooking a real effect.
Sample size calculation is a crucial step in research design and hypothesis testing. It helps you:
Warning: Conducting a study with inadequate sample size can lead to:
You're testing a new button design and want to detect a 2% increase in conversion rate (from 10% to 12%).
Without proper sample size calculation:
For this example, we need:
Input these values into the calculator and it will give you 3841 samples per group.
While traditional sample size calculation is crucial, modern A/B testing platforms often use sequential testing approaches:
Whether using traditional fixed-sample approaches or modern sequential methods, proper planning of sample size and monitoring procedures is essential for valid and reliable results.
For comparing two independent means, the sample size per group is:
where:
Let's calculate the sample size needed to detect a medium effect size (d = 0.5) with 80% power at α = 0.05:
Step-by-step calculation:
from statsmodels.stats.power import TTestIndPower
import numpy as np
# Parameters
effect_size = 0.5 # Cohen's d (medium effect)
alpha = 0.05 # Significance level
power = 0.8 # Desired power
# Create power analysis object
analysis = TTestIndPower()
# Calculate sample size (per group)
sample_size = analysis.solve_power(
effect_size=effect_size,
alpha=alpha,
power=power,
ratio=1.0, # Equal group sizes
alternative='two-sided'
)
print(f"Sample size per group: {np.ceil(sample_size):.0f}")
# For unequal group sizes with ratio 2:1
ratio = 2.0
sample_size_unequal = analysis.solve_power(
effect_size=effect_size,
alpha=alpha,
power=power,
ratio=ratio,
alternative='two-sided'
)
n1 = np.ceil(sample_size_unequal)
n2 = np.ceil(sample_size_unequal * ratio)
print(f"Unequal groups (ratio 1:{ratio:.0f}):")
print(f"- Group 1 sample size: {n1:.0f}")
print(f"- Group 2 sample size: {n2:.0f}")
print(f"- Total sample size: {n1 + n2:.0f}")Output:
Sample size per group: 64 Unequal groups (ratio 1:2): - Group 1 sample size: 48 - Group 2 sample size: 96 - Total sample size: 144
library(tidyverse)
library(pwr)
# Parameters
effect_size <- 0.5 # Cohen's d (medium effect)
sig_level <- 0.05 # Significance level (alpha)
power <- 0.8 # Desired power
type <- "two.sample" # Two-sample t-test
# Calculate sample size (equal group sizes)
result <- pwr.t.test(d = effect_size,
sig.level = sig_level,
power = power,
type = type,
alternative = "two.sided")
# Print results
print(str_glue("Sample size per group: {ceiling(result$n)}"))
# For unequal group sizes with allocation ratio r = 2
r <- 2
n1 <- pwr.t.test(d = effect_size,
sig.level = sig_level,
power = power,
type = type)$n * (1 + r) / (2 * r)
n2 <- r * n1
print(str_glue("Unequal groups (ratio 1:{r}):"))
print(str_glue("- Group 1 sample size: {ceiling(n1)}"))
print(str_glue("- Group 2 sample size: {ceiling(n2)}"))
print(str_glue("- Total sample size: {ceiling(n1) + ceiling(n2)}"))Output:
Two-sample t test power calculation
n = 63.76561
d = 0.5
sig.level = 0.05
power = 0.8
alternative = two.sided
NOTE: n is number in *each* group
Unequal groups (ratio 1:2):
- Group 1 sample size: 48
- Group 2 sample size: 96
- Total sample size: 144
Calculate the statistical power you'll achieve with a given sample size and effect size:
from statsmodels.stats.power import TTestIndPower
# Given parameters
effect_size = 0.5 # Cohen's d
alpha = 0.05 # Significance level
sample_size = 50 # Per group
analysis = TTestIndPower()
power = analysis.power(
effect_size=effect_size,
nobs1=sample_size,
alpha=alpha,
ratio=1.0,
alternative='two-sided'
)
print(f"With n={sample_size} per group and d={effect_size}:")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=50 per group and d=0.5: Statistical power: 0.6969 (69.69%)
library(tidyverse)
library(pwr)
# Given parameters
effect_size <- 0.5 # Cohen's d
sig_level <- 0.05 # Significance level
sample_size <- 50 # Per group
# Calculate power
result <- pwr.t.test(
d = effect_size,
n = sample_size,
sig.level = sig_level,
type = "two.sample",
alternative = "two.sided"
)
print(str_glue("With n={sample_size} per group and d={effect_size}:"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=50 per group and d=0.5: Statistical power: 0.6969 (69.69%)
Calculate the smallest effect size you can detect with a given sample size and desired power:
from statsmodels.stats.power import TTestIndPower
# Given parameters
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 64 # Per group
analysis = TTestIndPower()
effect_size = analysis.solve_power(
effect_size=None,
nobs1=sample_size,
alpha=alpha,
power=power,
ratio=1.0,
alternative='two-sided'
)
print(f"With n={sample_size} per group and power={power}:")
print(f"Minimum detectable effect size: {effect_size:.4f}")Output:
With n=64 per group and power=0.8: Minimum detectable effect size: 0.4991
library(tidyverse)
library(pwr)
# Given parameters
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 64 # Per group
# Calculate minimum detectable effect size
result <- pwr.t.test(
n = sample_size,
sig.level = sig_level,
power = power,
type = "two.sample",
alternative = "two.sided"
)
print(str_glue("With n={sample_size} per group and power={power}:"))
print(str_glue("Minimum detectable effect size: {round(result$d, 4)}"))Output:
With n=64 per group and power=0.8: Minimum detectable effect size: 0.4991
For paired samples, the required number of pairs is:
where:
Note: Higher correlation between pairs reduces the required sample size, making paired designs more efficient when correlation is strong.
For a paired t-test with expected effect size d = 0.5, correlation ρ = 0.6, significance level α = 0.05, and power = 0.8:
Therefore, we need 26 pairs of observations.
from statsmodels.stats.power import TTestPower
import numpy as np
# Parameters
d = 0.5 # Effect size (Cohen's d)
alpha = 0.05 # Significance level
power = 0.8 # Desired power
rho = 0.6 # Correlation between pairs
# Adjusted effect size for paired design
# For paired t-test, effective effect size is larger due to correlation
d_adj = d / np.sqrt(2 * (1 - rho))
# Create power analysis object
analysis = TTestPower()
# Calculate sample size (number of pairs)
sample_size = analysis.solve_power(
effect_size=d_adj,
alpha=alpha,
power=power,
alternative='two-sided'
)
print(f"Sample size (number of pairs): {np.ceil(sample_size):.0f}")
print(f"Correlation reduces required sample size from")
independent_n = analysis.solve_power(effect_size=d, nobs=None, alpha=alpha, power=power, alternative='two-sided')
print(f"{np.ceil(independent_n):.0f} (independent) to {np.ceil(sample_size):.0f} (paired)")Output:
Sample size (number of pairs): 28 Correlation reduces required sample size from 64 (independent) to 28 (paired)
library(tidyverse)
library(pwr)
# Parameters
d <- 0.5 # Effect size (Cohen's d)
sig.level <- 0.05 # Significance level
power <- 0.8 # Desired power
rho <- 0.6 # Correlation between pairs
# Adjusted effect size for paired design
d_adj <- d / sqrt(2 * (1 - rho))
# Calculate sample size
result <- pwr.t.test(
d = d_adj,
sig.level = sig.level,
power = power,
type = "paired"
)
print(result)
print(str_glue("Sample size per group: {ceiling(result$n)}"))Output:
Paired t test power calculation
n = 27.0998
d = 0.559017
sig.level = 0.05
power = 0.8
alternative = two.sided
NOTE: n is number of *pairs*
Sample size per group: 28
Calculate the statistical power for a paired t-test:
from statsmodels.stats.power import TTestPower
import numpy as np
# Given parameters
d = 0.5 # Effect size (Cohen's d)
alpha = 0.05 # Significance level
sample_size = 30 # Number of pairs
rho = 0.6 # Correlation between pairs
# Adjusted effect size
d_adj = d / np.sqrt(2 * (1 - rho))
analysis = TTestPower()
power = analysis.power(
effect_size=d_adj,
nobs=sample_size,
alpha=alpha,
alternative='two-sided'
)
print(f"With n={sample_size} pairs, d={d}, and ρ={rho}:")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=30 pairs, d=0.5, and ρ=0.6: Statistical power: 0.8411 (84.11%)
library(pwr)
# Given parameters
d <- 0.5 # Effect size (Cohen's d)
sig_level <- 0.05 # Significance level
sample_size <- 30 # Number of pairs
rho <- 0.6 # Correlation between pairs
# Adjusted effect size
d_adj <- d / sqrt(2 * (1 - rho))
# Calculate power
result <- pwr.t.test(
d = d_adj,
n = sample_size,
sig.level = sig_level,
type = "paired",
alternative = "two.sided"
)
print(str_glue("With n={sample_size} pairs, d={d}, and ρ={rho}:"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=30 pairs, d=0.5, and ρ=0.6: Statistical power: 0.8411 (84.11%)
Calculate the minimum detectable effect size for a paired t-test:
from statsmodels.stats.power import TTestPower
import numpy as np
# Given parameters
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 28 # Number of pairs
rho = 0.6 # Correlation between pairs
analysis = TTestPower()
# Solve for adjusted effect size
d_adj = analysis.solve_power(
effect_size=None,
nobs=sample_size,
alpha=alpha,
power=power,
alternative='two-sided'
)
# Convert back to original effect size
d = d_adj * np.sqrt(2 * (1 - rho))
print(f"With n={sample_size} pairs, power={power}, and ρ={rho}:")
print(f"Minimum detectable effect size: {d:.4f}")Output:
With n=28 pairs, power=0.8, and ρ=0.6: Minimum detectable effect size: 0.4913
library(pwr)
# Given parameters
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 28 # Number of pairs
rho <- 0.6 # Correlation between pairs
# Calculate minimum detectable adjusted effect size
result <- pwr.t.test(
n = sample_size,
sig.level = sig_level,
power = power,
type = "paired",
alternative = "two.sided"
)
# Convert back to original effect size
d <- result$d * sqrt(2 * (1 - rho))
print(str_glue("With n={sample_size} pairs, power={power}, and ρ={rho}:"))
print(str_glue("Minimum detectable effect size: {round(d, 4)}"))Output:
With n=28 pairs, power=0.8, and ρ=0.6: Minimum detectable effect size: 0.4912
For comparing two proportions, the required sample size per group is:
where:
Cohen's h Effect Size Guidelines:
Let's calculate the sample size needed to detect a difference between proportions p₁ = 0.6 and p₂ = 0.4 with 80% power at α = 0.05:
from statsmodels.stats.power import zt_ind_solve_power
from statsmodels.stats.proportion import proportion_effectsize
import numpy as np
# Parameters
p1 = 0.6 # Proportion in group 1
p2 = 0.4 # Proportion in group 2
alpha = 0.05 # Significance level
power = 0.8 # Desired power
# Calculate effect size (Cohen's h)
# This uses the arcsine transformation
h = proportion_effectsize(p1, p2)
print(f"Cohen's h = {h:.4f}")
# Calculate sample size per group
sample_size = zt_ind_solve_power(
effect_size=h,
alpha=alpha,
power=power,
ratio=1.0, # Equal group sizes
alternative='two-sided'
)
print(f"Sample size per group: {np.ceil(sample_size):.0f}")
# Alternative: Using NormalIndPower (another approach)
from statsmodels.stats.power import NormalIndPower
analysis = NormalIndPower()
sample_size_alt = analysis.solve_power(
effect_size=h,
alpha=alpha,
power=power,
ratio=1.0,
alternative='two-sided'
)
print(f"Sample size (alternative method): {np.ceil(sample_size_alt):.0f}")Output:
Cohen's h = 0.4027 Sample size per group: 97 Sample size (alternative method): 97
library(tidyverse)
library(pwr)
# Parameters
p1 <- 0.6 # Proportion in group 1
p2 <- 0.4 # Proportion in group 2
sig_level <- 0.05 # Significance level (alpha)
power <- 0.8 # Desired power
# Calculate effect size (Cohen's h)
h <- 2 * asin(sqrt(p1)) - 2 * asin(sqrt(p2))
print(str_glue("Cohen's h = {round(h, 4)}"))
# Calculate sample size
result <- pwr.2p.test(h = h,
sig.level = sig_level,
power = power)
# Print results
print(result)
print(str_glue("Sample size per group: {ceiling(result$n)}"))Output:
Cohen's h = 0.027
Difference of proportion power calculation for binomial distribution (arcsine transformation)
h = 0.4027158
n = 96.79194
sig.level = 0.05
power = 0.8
alternative = two.sided
NOTE: same sample sizes
Sample size per group: 97
Calculate the statistical power for a proportion test:
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportion_effectsize
# Given parameters
p1 = 0.3 # Baseline proportion
p2 = 0.4 # Alternative proportion
alpha = 0.05 # Significance level
sample_size = 100 # Per group
# Calculate Cohen's h
h = proportion_effectsize(p1, p2)
analysis = NormalIndPower()
power = analysis.power(
effect_size=h,
nobs1=sample_size,
alpha=alpha,
alternative='two-sided'
)
print(f"With n={sample_size} per group, p1={p1}, and p2={p2}:")
print(f"Cohen's h: {h:.4f}")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=100 per group, p1=0.3, and p2=0.4: Cohen's h: -0.2102 Statistical power: 0.3181 (31.81%)
library(pwr)
# Given parameters
p1 <- 0.3 # Baseline proportion
p2 <- 0.4 # Alternative proportion
sig_level <- 0.05 # Significance level
sample_size <- 100 # Per group
# Calculate Cohen's h
h <- ES.h(p1, p2)
# Calculate power
result <- pwr.2p.test(
h = h,
n = sample_size,
sig.level = sig_level,
alternative = "two.sided"
)
print(str_glue("With n={sample_size} per group, p1={p1}, and p2={p2}:"))
print(str_glue("Cohen's h: {round(h, 4)}"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=100 per group, p1=0.3, and p2=0.4: Cohen's h: -0.2102 Statistical power: 0.3181 (31.81%)
Calculate the minimum detectable proportion difference:
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportion_effectsize
from scipy.optimize import fsolve
import numpy as np
# Given parameters
p1 = 0.3 # Baseline proportion
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 194 # Per group
# Solve for Cohen's h
analysis = NormalIndPower()
h = analysis.solve_power(
effect_size=None,
nobs1=sample_size,
alpha=alpha,
power=power,
alternative='two-sided'
)
# Convert h back to proportions (approximate)
p2 = np.sin((h + 2*np.arcsin(np.sqrt(p1)))/2)**2
effect_size = p2 - p1
print(f"With n={sample_size} per group, p1={p1}, and power={power}:")
print(f"Minimum detectable proportion difference: {effect_size:.4f}")
print(f"This means p2 = {p2:.4f}")Output:
With n=194 per group, p1=0.3, and power=0.8: Minimum detectable proportion difference: 0.1366 This means p2 = 0.4366
library(pwr)
# Given parameters
p1 <- 0.3 # Baseline proportion
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 194 # Per group
# Calculate minimum detectable Cohen's h
result <- pwr.2p.test(
n = sample_size,
sig.level = sig_level,
power = power,
alternative = "two.sided"
)
# Convert h back to proportions (approximate)
# h = 2*arcsin(sqrt(p2)) - 2*arcsin(sqrt(p1))
# Solving for p2
h <- result$h
p2 <- (sin(asin(sqrt(p1)) + h/2))^2
effect_size <- p2 - p1
print(str_glue("With n={sample_size} per group, p1={p1}, and power={power}:"))
print(str_glue("Minimum detectable proportion difference: {round(effect_size, 4)}"))
print(str_glue("This means p2 = {round(p2, 4)}"))Output:
With n=194 per group, p1=0.3, and power=0.8: Minimum detectable proportion difference: 0.1366 This means p2 = 0.4366
For one-way ANOVA with k groups, the sample size per group is:
where:
Note:
This formula provides an approximation of the sample size for a one-way ANOVA. For more accurate results, especially when dealing with small effect sizes or complex designs, it is recommended to use specialized software (e.g., G*Power, R, Python, or our calculator above).
Cohen's f Effect Size Guidelines:
Let's calculate the sample size needed for a one-way ANOVA with 3 groups, a medium effect size (f = 0.25), 80% power, and α = 0.05:
Therefore, we need approximately 84 subjects per group, for a total of 252 subjects.
from statsmodels.stats.power import FTestAnovaPower
import numpy as np
# Parameters
k = 3 # Number of groups
f = 0.25 # Cohen's f (medium effect size)
alpha = 0.05 # Significance level
power = 0.8 # Desired power
# Create power analysis object
analysis = FTestAnovaPower()
# Calculate TOTAL sample size first (statsmodels returns total, not per-group)
total_sample_size = analysis.solve_power(
effect_size=f,
nobs=None,
alpha=alpha,
power=power,
k_groups=k
)
# Handle array conversion (statsmodels may return array)
if isinstance(total_sample_size, np.ndarray):
total_sample_size = float(total_sample_size.item())
else:
total_sample_size = float(total_sample_size)
# Divide by k to get per-group size
per_group = total_sample_size / k
print(f"Sample size per group: {np.ceil(per_group):.0f}")
print(f"Total sample size: {np.ceil(total_sample_size):.0f}")
# Also calculate what power we'd have with different sample sizes
for n_per_group in [30, 50, 70, 100]:
# Note: nobs must be TOTAL sample size (per-group × k)
pwr = analysis.power(
effect_size=f,
nobs=n_per_group * k,
alpha=alpha,
k_groups=k
)
print(f"Power with n={n_per_group} per group: {pwr:.3f} ({pwr*100:.1f}%)")Output:
Sample size per group: 53 Total sample size: 158 Power with n=30 per group: 0.540 (54.0%) Power with n=50 per group: 0.780 (78.0%) Power with n=70 per group: 0.907 (90.7%) Power with n=100 per group: 0.978 (97.8%)
library(pwr)
# Parameters
groups <- 3 # Number of groups
f <- 0.25 # Cohen's f (medium effect size)
sig_level <- 0.05 # Significance level (alpha)
power <- 0.8 # Desired power
# Calculate sample size
result <- pwr.anova.test(k = groups,
f = f,
sig.level = sig_level,
power = power)
# Print results
print(result)
print(paste("Total sample size:", ceiling(result$n) * groups))Output:
Balanced one-way analysis of variance power calculation
k = 3
n = 52.3966
f = 0.25
sig.level = 0.05
power = 0.8
NOTE: n is number in each group
Total sample size: 159
Calculate the statistical power for one-way ANOVA:
from statsmodels.stats.power import FTestAnovaPower
# Given parameters
k = 3 # Number of groups
f = 0.25 # Cohen's f effect size
alpha = 0.05 # Significance level
sample_size = 60 # Per group
analysis = FTestAnovaPower()
# Note: nobs must be TOTAL sample size
power = analysis.power(
effect_size=f,
nobs=sample_size * k,
alpha=alpha,
k_groups=k
)
print(f"With n={sample_size} per group (total={sample_size*k}), k={k}, and f={f}:")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=60 per group (total=180), k=3, and f=0.25: Statistical power: 0.8546 (85.46%)
library(pwr)
# Given parameters
k <- 3 # Number of groups
f <- 0.25 # Cohen's f effect size
sig_level <- 0.05 # Significance level
sample_size <- 60 # Per group
# Calculate power
result <- pwr.anova.test(
k = k,
n = sample_size,
f = f,
sig.level = sig_level
)
print(str_glue("With n={sample_size} per group (total={sample_size*k}), k={k}, and f={f}:"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=60 per group (total=180), k=3, and f=0.25: Statistical power: 0.8546 (85.46%)
Calculate the minimum detectable Cohen's f for ANOVA:
from statsmodels.stats.power import FTestAnovaPower
# Given parameters
k = 3 # Number of groups
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 53 # Per group
analysis = FTestAnovaPower()
# Solve for effect size (note: nobs is TOTAL sample size)
f = analysis.solve_power(
effect_size=None,
nobs=sample_size * k,
alpha=alpha,
power=power,
k_groups=k
)
print(f"With n={sample_size} per group (total={sample_size*k}), k={k}, and power={power}:")
print(f"Minimum detectable effect size (Cohen's f): {f:.4f}")Output:
With n=53 per group (total=159), k=3, and power=0.8: Minimum detectable effect size (Cohen's f): 0.2485
library(pwr)
# Given parameters
k <- 3 # Number of groups
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 53 # Per group
# Calculate minimum detectable effect size
result <- pwr.anova.test(
k = k,
n = sample_size,
sig.level = sig_level,
power = power
)
print(str_glue("With n={sample_size} per group (total={sample_size*k}), k={k}, and power={power}:"))
print(str_glue("Minimum detectable effect size (Cohen's f): {round(result$f, 4)}"))Output:
With n=53 per group (total=159), k=3, and power=0.8: Minimum detectable effect size (Cohen's f): 0.2485
The chi-square test is used for categorical data to test independence in contingency tables or goodness-of-fit. Sample size calculations use Cohen's w effect size.
For a chi-square test, the sample size is calculated using:
where:
Cohen's w Effect Size Guidelines:
Calculate the required sample size for a chi-square test with 1 degree of freedom (2x2 contingency table), medium effect size (w = 0.3), 80% power, and α = 0.05:
from statsmodels.stats.power import GofChisquarePower
import numpy as np
# Parameters
df = 1 # Degrees of freedom
w = 0.3 # Cohen's w (medium effect size)
alpha = 0.05 # Significance level
power = 0.8 # Desired power
# Create power analysis object
analysis = GofChisquarePower()
# Calculate sample size
n = analysis.solve_power(
effect_size=w,
nobs=None,
alpha=alpha,
power=power,
n_bins=df + 1 # number of categories = df + 1
)
print(f"Sample size needed: {np.ceil(n):.0f}")
# Verify the power with this sample size
power_check = analysis.power(
effect_size=w,
nobs=np.ceil(n),
alpha=alpha,
n_bins=df + 1
)
print(f"Achieved power: {power_check:.4f} ({power_check*100:.2f}%)")Output:
Sample size needed: 88 Achieved power: 0.8013 (80.13%)
library(pwr)
library(stringr)
# Parameters
df <- 1 # Degrees of freedom
w <- 0.3 # Cohen's w (medium effect size)
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
# Calculate sample size
result <- pwr.chisq.test(
w = w,
df = df,
sig.level = sig_level,
power = power
)
print(result)
print(str_glue("Sample size needed: {ceiling(result$N)}"))Output:
Chi square power calculation
w = 0.3
N = 87.20854
df = 1
sig.level = 0.05
power = 0.8
NOTE: N is the number of observations
Sample size needed: 88Calculate the statistical power with n = 100, df = 1, and w = 0.3:
from statsmodels.stats.power import GofChisquarePower
# Given parameters
df = 1 # Degrees of freedom
w = 0.3 # Cohen's w effect size
alpha = 0.05 # Significance level
sample_size = 100 # Total sample size
analysis = GofChisquarePower()
power = analysis.power(
effect_size=w,
nobs=sample_size,
alpha=alpha,
n_bins=df + 1
)
print(f"With n={sample_size}, df={df}, and w={w}:")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=100, df=1, and w=0.3: Statistical power: 0.8508 (85.08%)
library(pwr)
library(stringr)
# Given parameters
df <- 1 # Degrees of freedom
w <- 0.3 # Cohen's w effect size
sig_level <- 0.05 # Significance level
sample_size <- 100 # Total sample size
# Calculate power
result <- pwr.chisq.test(
w = w,
N = sample_size,
df = df,
sig.level = sig_level
)
print(str_glue("With n={sample_size}, df={df}, and w={w}:"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=100, df=1, and w=0.3: Statistical power: 0.8508 (85.08%)
Calculate the minimum detectable Cohen's w with n = 88, df = 1, and 80% power:
from statsmodels.stats.power import GofChisquarePower
# Given parameters
df = 1 # Degrees of freedom
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 88 # Total sample size
analysis = GofChisquarePower()
w = analysis.solve_power(
effect_size=None,
nobs=sample_size,
alpha=alpha,
power=power,
n_bins=df + 1
)
print(f"With n={sample_size}, df={df}, and power={power}:")
print(f"Minimum detectable effect size (Cohen's w): {w:.4f}")Output:
With n=88, df=1, and power=0.8: Minimum detectable effect size (Cohen's w): 0.2994
library(pwr)
library(stringr)
# Given parameters
df <- 1 # Degrees of freedom
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 88 # Total sample size
# Calculate minimum detectable effect size
result <- pwr.chisq.test(
N = sample_size,
df = df,
sig.level = sig_level,
power = power
)
print(str_glue("With n={sample_size}, df={df}, and power={power}:"))
print(str_glue("Minimum detectable effect size (Cohen's w): {round(result$w, 4)}"))Output:
With n=88, df=1, and power=0.8: Minimum detectable effect size (Cohen's w): 0.2987
Note: The slight difference between Python (0.2994) and R (0.2987) results is normal and expected. Different packages use different numerical algorithms and convergence criteria. Both values are practically equivalent.
Multiple regression models the relationship between a dependent variable and multiple independent variables. Sample size calculations use Cohen's f² effect size (proportion of variance explained).
For multiple regression, the required sample size is:
where:
Cohen's f² Effect Size Guidelines:
Note: Relationship to R²:
Calculate the required sample size for a multiple regression with 5 predictors, medium effect size (f² = 0.15), 80% power, and α = 0.05:
from statsmodels.stats.power import FTestAnovaPower
import numpy as np
# Parameters
num_predictors = 5 # Number of independent variables
f_squared = 0.15 # Cohen's f² (medium effect size)
alpha = 0.05 # Significance level
power = 0.8 # Desired power
# Convert f² to f
cohens_f = np.sqrt(f_squared)
# Create power analysis object
analysis = FTestAnovaPower()
# For multiple regression: k_groups = num_predictors + 1
k_groups = num_predictors + 1
# Solve for required sample size
nobs_result = analysis.solve_power(
effect_size=cohens_f,
nobs=None, # This is what we want to solve for
alpha=alpha,
power=power,
k_groups=k_groups
)
# Round up to get total sample size
total_n = int(np.ceil(nobs_result))
print(f"Cohen's f: {cohens_f:.4f}")
print(f"Sample size needed: {total_n}")
print(f"(Minimum 10-15 observations per predictor recommended)")
# Verify the power with this sample size
power_check = analysis.power(
effect_size=cohens_f,
nobs=total_n,
alpha=alpha,
k_groups=k_groups
)
print(f"Achieved power: {power_check:.4f} ({power_check*100:.2f}%)")Output:
Cohen's f: 0.3873 Sample size needed: 92 (Minimum 10-15 observations per predictor recommended) Achieved power: 0.8042 (80.42%)
library(pwr)
library(stringr)
# Parameters
num_predictors <- 5 # Number of independent variables
f_squared <- 0.15 # Cohen's f² (medium effect size)
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
# Convert f² to f
cohens_f <- sqrt(f_squared)
# Calculate sample size using pwr.f2.test
# u = numerator df = number of predictors
# v = denominator df = n - u - 1
result <- pwr.f2.test(
u = num_predictors,
f2 = f_squared,
sig.level = sig_level,
power = power
)
# Total sample size = v + u + 1
total_n <- ceiling(result$v + num_predictors + 1)
print(result)
print(str_glue("Cohen's f: {round(cohens_f, 4)}"))
print(str_glue("Sample size needed: {total_n}"))Output:
Multiple regression power calculation
u = 5
v = 85.21369
f2 = 0.15
sig.level = 0.05
power = 0.8
Cohen's f: 0.3873
Sample size needed: 92Calculate the statistical power with n = 100, 5 predictors, and f² = 0.15:
from statsmodels.stats.power import FTestAnovaPower
import numpy as np
# Given parameters
num_predictors = 5 # Number of predictors
f_squared = 0.15 # Cohen's f² effect size
alpha = 0.05 # Significance level
sample_size = 100 # Total sample size
# Convert f² to f
cohens_f = np.sqrt(f_squared)
# Create power analysis object
analysis = FTestAnovaPower()
# For multiple regression: k_groups = num_predictors + 1
k_groups = num_predictors + 1
# Calculate power
power = analysis.power(
effect_size=cohens_f,
nobs=sample_size,
alpha=alpha,
k_groups=k_groups
)
print(f"With n={sample_size}, k={num_predictors}, and f²={f_squared}:")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=100, k=5, and f²=0.15: Statistical power: 0.8430 (84.30%)
library(pwr)
library(stringr)
# Given parameters
num_predictors <- 5 # Number of predictors
f_squared <- 0.15 # Cohen's f² effect size
sig_level <- 0.05 # Significance level
sample_size <- 100 # Total sample size
# Calculate v (denominator df)
v <- sample_size - num_predictors - 1
# Calculate power
result <- pwr.f2.test(
u = num_predictors,
v = v,
f2 = f_squared,
sig.level = sig_level
)
print(str_glue("With n={sample_size}, k={num_predictors}, and f²={f_squared}:"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=100, k=5, and f²=0.15: Statistical power: 0.8489 (84.89%)
Calculate the minimum detectable Cohen's f² with n = 92, 5 predictors, and 80% power:
from statsmodels.stats.power import FTestAnovaPower
import numpy as np
# Given parameters
num_predictors = 5 # Number of predictors
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 92 # Total sample size
# Create power analysis object
analysis = FTestAnovaPower()
# For multiple regression: k_groups = num_predictors + 1
k_groups = num_predictors + 1
# Solve for Cohen's f (effect size)
cohens_f = analysis.solve_power(
effect_size=None, # This is what we want to solve for
nobs=sample_size,
alpha=alpha,
power=power,
k_groups=k_groups
)
# Handle array conversion (statsmodels may return array)
if isinstance(cohens_f, np.ndarray):
cohens_f = float(cohens_f.item())
else:
cohens_f = float(cohens_f)
# Convert to f²
f_squared = cohens_f ** 2
print(f"With n={sample_size}, k={num_predictors}, and power={power}:")
print(f"Minimum detectable effect size (Cohen's f²): {f_squared:.4f}")
print(f"Minimum detectable effect size (Cohen's f): {cohens_f:.4f}")Output:
With n=92, k=5, and power=0.8: Minimum detectable effect size (Cohen's f²): 0.1486 Minimum detectable effect size (Cohen's f): 0.3855
library(pwr)
library(stringr)
# Given parameters
num_predictors <- 5 # Number of predictors
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 92 # Total sample size
# Calculate v (denominator df)
v <- sample_size - num_predictors - 1
# Calculate minimum detectable effect size
result <- pwr.f2.test(
u = num_predictors,
v = v,
sig.level = sig_level,
power = power
)
# Convert f to f²
cohens_f <- sqrt(result$f2)
print(str_glue("With n={sample_size}, k={num_predictors}, and power={power}:"))
print(str_glue("Minimum detectable effect size (Cohen's f²): {round(result$f2, 4)}"))
print(str_glue("Minimum detectable effect size (Cohen's f): {round(cohens_f, 4)}"))Output:
With n=92, k=5, and power=0.8: Minimum detectable effect size (Cohen's f²): 0.1486 Minimum detectable effect size (Cohen's f): 0.3855
This sample size calculator helps you determine the optimal sample size needed for your statistical tests. It provides comprehensive power analysis with visual charts showing the relationship between sample size, effect size, and statistical power. Whether you're comparing means (paired/unpaired), proportions, multiple groups (ANOVA), categorical associations (chi-square), or building predictive models (multiple regression), this calculator will help ensure your research has adequate statistical power to detect meaningful effects.
If you are looking to calculate the sample size based on a desired margin of error for confidence intervals, try our Margin of Error Sample Size Calculator.
Range: 0.1 to 2 (standard deviations)
Get properly formatted citations for academic work •20k+ calculations in the past 30 days
StatsCalculators Team. (2026). Sample Size & Power Analysis Calculator. StatsCalculators. Retrieved February 2, 2026 from https://statscalculators.com/calculators/hypothesis-testing/sample-size-and-power-analysis-calculator
Statistical power is the probability that your study will correctly detect an effect when there is one. Failing to do so results in a Type II error.
A power of 0.8 (or 80%) is typically considered adequate, indicating there is a 20% chance of overlooking a real effect.
Sample size calculation is a crucial step in research design and hypothesis testing. It helps you:
Warning: Conducting a study with inadequate sample size can lead to:
You're testing a new button design and want to detect a 2% increase in conversion rate (from 10% to 12%).
Without proper sample size calculation:
For this example, we need:
Input these values into the calculator and it will give you 3841 samples per group.
While traditional sample size calculation is crucial, modern A/B testing platforms often use sequential testing approaches:
Whether using traditional fixed-sample approaches or modern sequential methods, proper planning of sample size and monitoring procedures is essential for valid and reliable results.
For comparing two independent means, the sample size per group is:
where:
Let's calculate the sample size needed to detect a medium effect size (d = 0.5) with 80% power at α = 0.05:
Step-by-step calculation:
from statsmodels.stats.power import TTestIndPower
import numpy as np
# Parameters
effect_size = 0.5 # Cohen's d (medium effect)
alpha = 0.05 # Significance level
power = 0.8 # Desired power
# Create power analysis object
analysis = TTestIndPower()
# Calculate sample size (per group)
sample_size = analysis.solve_power(
effect_size=effect_size,
alpha=alpha,
power=power,
ratio=1.0, # Equal group sizes
alternative='two-sided'
)
print(f"Sample size per group: {np.ceil(sample_size):.0f}")
# For unequal group sizes with ratio 2:1
ratio = 2.0
sample_size_unequal = analysis.solve_power(
effect_size=effect_size,
alpha=alpha,
power=power,
ratio=ratio,
alternative='two-sided'
)
n1 = np.ceil(sample_size_unequal)
n2 = np.ceil(sample_size_unequal * ratio)
print(f"Unequal groups (ratio 1:{ratio:.0f}):")
print(f"- Group 1 sample size: {n1:.0f}")
print(f"- Group 2 sample size: {n2:.0f}")
print(f"- Total sample size: {n1 + n2:.0f}")Output:
Sample size per group: 64 Unequal groups (ratio 1:2): - Group 1 sample size: 48 - Group 2 sample size: 96 - Total sample size: 144
library(tidyverse)
library(pwr)
# Parameters
effect_size <- 0.5 # Cohen's d (medium effect)
sig_level <- 0.05 # Significance level (alpha)
power <- 0.8 # Desired power
type <- "two.sample" # Two-sample t-test
# Calculate sample size (equal group sizes)
result <- pwr.t.test(d = effect_size,
sig.level = sig_level,
power = power,
type = type,
alternative = "two.sided")
# Print results
print(str_glue("Sample size per group: {ceiling(result$n)}"))
# For unequal group sizes with allocation ratio r = 2
r <- 2
n1 <- pwr.t.test(d = effect_size,
sig.level = sig_level,
power = power,
type = type)$n * (1 + r) / (2 * r)
n2 <- r * n1
print(str_glue("Unequal groups (ratio 1:{r}):"))
print(str_glue("- Group 1 sample size: {ceiling(n1)}"))
print(str_glue("- Group 2 sample size: {ceiling(n2)}"))
print(str_glue("- Total sample size: {ceiling(n1) + ceiling(n2)}"))Output:
Two-sample t test power calculation
n = 63.76561
d = 0.5
sig.level = 0.05
power = 0.8
alternative = two.sided
NOTE: n is number in *each* group
Unequal groups (ratio 1:2):
- Group 1 sample size: 48
- Group 2 sample size: 96
- Total sample size: 144
Calculate the statistical power you'll achieve with a given sample size and effect size:
from statsmodels.stats.power import TTestIndPower
# Given parameters
effect_size = 0.5 # Cohen's d
alpha = 0.05 # Significance level
sample_size = 50 # Per group
analysis = TTestIndPower()
power = analysis.power(
effect_size=effect_size,
nobs1=sample_size,
alpha=alpha,
ratio=1.0,
alternative='two-sided'
)
print(f"With n={sample_size} per group and d={effect_size}:")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=50 per group and d=0.5: Statistical power: 0.6969 (69.69%)
library(tidyverse)
library(pwr)
# Given parameters
effect_size <- 0.5 # Cohen's d
sig_level <- 0.05 # Significance level
sample_size <- 50 # Per group
# Calculate power
result <- pwr.t.test(
d = effect_size,
n = sample_size,
sig.level = sig_level,
type = "two.sample",
alternative = "two.sided"
)
print(str_glue("With n={sample_size} per group and d={effect_size}:"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=50 per group and d=0.5: Statistical power: 0.6969 (69.69%)
Calculate the smallest effect size you can detect with a given sample size and desired power:
from statsmodels.stats.power import TTestIndPower
# Given parameters
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 64 # Per group
analysis = TTestIndPower()
effect_size = analysis.solve_power(
effect_size=None,
nobs1=sample_size,
alpha=alpha,
power=power,
ratio=1.0,
alternative='two-sided'
)
print(f"With n={sample_size} per group and power={power}:")
print(f"Minimum detectable effect size: {effect_size:.4f}")Output:
With n=64 per group and power=0.8: Minimum detectable effect size: 0.4991
library(tidyverse)
library(pwr)
# Given parameters
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 64 # Per group
# Calculate minimum detectable effect size
result <- pwr.t.test(
n = sample_size,
sig.level = sig_level,
power = power,
type = "two.sample",
alternative = "two.sided"
)
print(str_glue("With n={sample_size} per group and power={power}:"))
print(str_glue("Minimum detectable effect size: {round(result$d, 4)}"))Output:
With n=64 per group and power=0.8: Minimum detectable effect size: 0.4991
For paired samples, the required number of pairs is:
where:
Note: Higher correlation between pairs reduces the required sample size, making paired designs more efficient when correlation is strong.
For a paired t-test with expected effect size d = 0.5, correlation ρ = 0.6, significance level α = 0.05, and power = 0.8:
Therefore, we need 26 pairs of observations.
from statsmodels.stats.power import TTestPower
import numpy as np
# Parameters
d = 0.5 # Effect size (Cohen's d)
alpha = 0.05 # Significance level
power = 0.8 # Desired power
rho = 0.6 # Correlation between pairs
# Adjusted effect size for paired design
# For paired t-test, effective effect size is larger due to correlation
d_adj = d / np.sqrt(2 * (1 - rho))
# Create power analysis object
analysis = TTestPower()
# Calculate sample size (number of pairs)
sample_size = analysis.solve_power(
effect_size=d_adj,
alpha=alpha,
power=power,
alternative='two-sided'
)
print(f"Sample size (number of pairs): {np.ceil(sample_size):.0f}")
print(f"Correlation reduces required sample size from")
independent_n = analysis.solve_power(effect_size=d, nobs=None, alpha=alpha, power=power, alternative='two-sided')
print(f"{np.ceil(independent_n):.0f} (independent) to {np.ceil(sample_size):.0f} (paired)")Output:
Sample size (number of pairs): 28 Correlation reduces required sample size from 64 (independent) to 28 (paired)
library(tidyverse)
library(pwr)
# Parameters
d <- 0.5 # Effect size (Cohen's d)
sig.level <- 0.05 # Significance level
power <- 0.8 # Desired power
rho <- 0.6 # Correlation between pairs
# Adjusted effect size for paired design
d_adj <- d / sqrt(2 * (1 - rho))
# Calculate sample size
result <- pwr.t.test(
d = d_adj,
sig.level = sig.level,
power = power,
type = "paired"
)
print(result)
print(str_glue("Sample size per group: {ceiling(result$n)}"))Output:
Paired t test power calculation
n = 27.0998
d = 0.559017
sig.level = 0.05
power = 0.8
alternative = two.sided
NOTE: n is number of *pairs*
Sample size per group: 28
Calculate the statistical power for a paired t-test:
from statsmodels.stats.power import TTestPower
import numpy as np
# Given parameters
d = 0.5 # Effect size (Cohen's d)
alpha = 0.05 # Significance level
sample_size = 30 # Number of pairs
rho = 0.6 # Correlation between pairs
# Adjusted effect size
d_adj = d / np.sqrt(2 * (1 - rho))
analysis = TTestPower()
power = analysis.power(
effect_size=d_adj,
nobs=sample_size,
alpha=alpha,
alternative='two-sided'
)
print(f"With n={sample_size} pairs, d={d}, and ρ={rho}:")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=30 pairs, d=0.5, and ρ=0.6: Statistical power: 0.8411 (84.11%)
library(pwr)
# Given parameters
d <- 0.5 # Effect size (Cohen's d)
sig_level <- 0.05 # Significance level
sample_size <- 30 # Number of pairs
rho <- 0.6 # Correlation between pairs
# Adjusted effect size
d_adj <- d / sqrt(2 * (1 - rho))
# Calculate power
result <- pwr.t.test(
d = d_adj,
n = sample_size,
sig.level = sig_level,
type = "paired",
alternative = "two.sided"
)
print(str_glue("With n={sample_size} pairs, d={d}, and ρ={rho}:"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=30 pairs, d=0.5, and ρ=0.6: Statistical power: 0.8411 (84.11%)
Calculate the minimum detectable effect size for a paired t-test:
from statsmodels.stats.power import TTestPower
import numpy as np
# Given parameters
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 28 # Number of pairs
rho = 0.6 # Correlation between pairs
analysis = TTestPower()
# Solve for adjusted effect size
d_adj = analysis.solve_power(
effect_size=None,
nobs=sample_size,
alpha=alpha,
power=power,
alternative='two-sided'
)
# Convert back to original effect size
d = d_adj * np.sqrt(2 * (1 - rho))
print(f"With n={sample_size} pairs, power={power}, and ρ={rho}:")
print(f"Minimum detectable effect size: {d:.4f}")Output:
With n=28 pairs, power=0.8, and ρ=0.6: Minimum detectable effect size: 0.4913
library(pwr)
# Given parameters
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 28 # Number of pairs
rho <- 0.6 # Correlation between pairs
# Calculate minimum detectable adjusted effect size
result <- pwr.t.test(
n = sample_size,
sig.level = sig_level,
power = power,
type = "paired",
alternative = "two.sided"
)
# Convert back to original effect size
d <- result$d * sqrt(2 * (1 - rho))
print(str_glue("With n={sample_size} pairs, power={power}, and ρ={rho}:"))
print(str_glue("Minimum detectable effect size: {round(d, 4)}"))Output:
With n=28 pairs, power=0.8, and ρ=0.6: Minimum detectable effect size: 0.4912
For comparing two proportions, the required sample size per group is:
where:
Cohen's h Effect Size Guidelines:
Let's calculate the sample size needed to detect a difference between proportions p₁ = 0.6 and p₂ = 0.4 with 80% power at α = 0.05:
from statsmodels.stats.power import zt_ind_solve_power
from statsmodels.stats.proportion import proportion_effectsize
import numpy as np
# Parameters
p1 = 0.6 # Proportion in group 1
p2 = 0.4 # Proportion in group 2
alpha = 0.05 # Significance level
power = 0.8 # Desired power
# Calculate effect size (Cohen's h)
# This uses the arcsine transformation
h = proportion_effectsize(p1, p2)
print(f"Cohen's h = {h:.4f}")
# Calculate sample size per group
sample_size = zt_ind_solve_power(
effect_size=h,
alpha=alpha,
power=power,
ratio=1.0, # Equal group sizes
alternative='two-sided'
)
print(f"Sample size per group: {np.ceil(sample_size):.0f}")
# Alternative: Using NormalIndPower (another approach)
from statsmodels.stats.power import NormalIndPower
analysis = NormalIndPower()
sample_size_alt = analysis.solve_power(
effect_size=h,
alpha=alpha,
power=power,
ratio=1.0,
alternative='two-sided'
)
print(f"Sample size (alternative method): {np.ceil(sample_size_alt):.0f}")Output:
Cohen's h = 0.4027 Sample size per group: 97 Sample size (alternative method): 97
library(tidyverse)
library(pwr)
# Parameters
p1 <- 0.6 # Proportion in group 1
p2 <- 0.4 # Proportion in group 2
sig_level <- 0.05 # Significance level (alpha)
power <- 0.8 # Desired power
# Calculate effect size (Cohen's h)
h <- 2 * asin(sqrt(p1)) - 2 * asin(sqrt(p2))
print(str_glue("Cohen's h = {round(h, 4)}"))
# Calculate sample size
result <- pwr.2p.test(h = h,
sig.level = sig_level,
power = power)
# Print results
print(result)
print(str_glue("Sample size per group: {ceiling(result$n)}"))Output:
Cohen's h = 0.027
Difference of proportion power calculation for binomial distribution (arcsine transformation)
h = 0.4027158
n = 96.79194
sig.level = 0.05
power = 0.8
alternative = two.sided
NOTE: same sample sizes
Sample size per group: 97
Calculate the statistical power for a proportion test:
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportion_effectsize
# Given parameters
p1 = 0.3 # Baseline proportion
p2 = 0.4 # Alternative proportion
alpha = 0.05 # Significance level
sample_size = 100 # Per group
# Calculate Cohen's h
h = proportion_effectsize(p1, p2)
analysis = NormalIndPower()
power = analysis.power(
effect_size=h,
nobs1=sample_size,
alpha=alpha,
alternative='two-sided'
)
print(f"With n={sample_size} per group, p1={p1}, and p2={p2}:")
print(f"Cohen's h: {h:.4f}")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=100 per group, p1=0.3, and p2=0.4: Cohen's h: -0.2102 Statistical power: 0.3181 (31.81%)
library(pwr)
# Given parameters
p1 <- 0.3 # Baseline proportion
p2 <- 0.4 # Alternative proportion
sig_level <- 0.05 # Significance level
sample_size <- 100 # Per group
# Calculate Cohen's h
h <- ES.h(p1, p2)
# Calculate power
result <- pwr.2p.test(
h = h,
n = sample_size,
sig.level = sig_level,
alternative = "two.sided"
)
print(str_glue("With n={sample_size} per group, p1={p1}, and p2={p2}:"))
print(str_glue("Cohen's h: {round(h, 4)}"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=100 per group, p1=0.3, and p2=0.4: Cohen's h: -0.2102 Statistical power: 0.3181 (31.81%)
Calculate the minimum detectable proportion difference:
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportion_effectsize
from scipy.optimize import fsolve
import numpy as np
# Given parameters
p1 = 0.3 # Baseline proportion
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 194 # Per group
# Solve for Cohen's h
analysis = NormalIndPower()
h = analysis.solve_power(
effect_size=None,
nobs1=sample_size,
alpha=alpha,
power=power,
alternative='two-sided'
)
# Convert h back to proportions (approximate)
p2 = np.sin((h + 2*np.arcsin(np.sqrt(p1)))/2)**2
effect_size = p2 - p1
print(f"With n={sample_size} per group, p1={p1}, and power={power}:")
print(f"Minimum detectable proportion difference: {effect_size:.4f}")
print(f"This means p2 = {p2:.4f}")Output:
With n=194 per group, p1=0.3, and power=0.8: Minimum detectable proportion difference: 0.1366 This means p2 = 0.4366
library(pwr)
# Given parameters
p1 <- 0.3 # Baseline proportion
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 194 # Per group
# Calculate minimum detectable Cohen's h
result <- pwr.2p.test(
n = sample_size,
sig.level = sig_level,
power = power,
alternative = "two.sided"
)
# Convert h back to proportions (approximate)
# h = 2*arcsin(sqrt(p2)) - 2*arcsin(sqrt(p1))
# Solving for p2
h <- result$h
p2 <- (sin(asin(sqrt(p1)) + h/2))^2
effect_size <- p2 - p1
print(str_glue("With n={sample_size} per group, p1={p1}, and power={power}:"))
print(str_glue("Minimum detectable proportion difference: {round(effect_size, 4)}"))
print(str_glue("This means p2 = {round(p2, 4)}"))Output:
With n=194 per group, p1=0.3, and power=0.8: Minimum detectable proportion difference: 0.1366 This means p2 = 0.4366
For one-way ANOVA with k groups, the sample size per group is:
where:
Note:
This formula provides an approximation of the sample size for a one-way ANOVA. For more accurate results, especially when dealing with small effect sizes or complex designs, it is recommended to use specialized software (e.g., G*Power, R, Python, or our calculator above).
Cohen's f Effect Size Guidelines:
Let's calculate the sample size needed for a one-way ANOVA with 3 groups, a medium effect size (f = 0.25), 80% power, and α = 0.05:
Therefore, we need approximately 84 subjects per group, for a total of 252 subjects.
from statsmodels.stats.power import FTestAnovaPower
import numpy as np
# Parameters
k = 3 # Number of groups
f = 0.25 # Cohen's f (medium effect size)
alpha = 0.05 # Significance level
power = 0.8 # Desired power
# Create power analysis object
analysis = FTestAnovaPower()
# Calculate TOTAL sample size first (statsmodels returns total, not per-group)
total_sample_size = analysis.solve_power(
effect_size=f,
nobs=None,
alpha=alpha,
power=power,
k_groups=k
)
# Handle array conversion (statsmodels may return array)
if isinstance(total_sample_size, np.ndarray):
total_sample_size = float(total_sample_size.item())
else:
total_sample_size = float(total_sample_size)
# Divide by k to get per-group size
per_group = total_sample_size / k
print(f"Sample size per group: {np.ceil(per_group):.0f}")
print(f"Total sample size: {np.ceil(total_sample_size):.0f}")
# Also calculate what power we'd have with different sample sizes
for n_per_group in [30, 50, 70, 100]:
# Note: nobs must be TOTAL sample size (per-group × k)
pwr = analysis.power(
effect_size=f,
nobs=n_per_group * k,
alpha=alpha,
k_groups=k
)
print(f"Power with n={n_per_group} per group: {pwr:.3f} ({pwr*100:.1f}%)")Output:
Sample size per group: 53 Total sample size: 158 Power with n=30 per group: 0.540 (54.0%) Power with n=50 per group: 0.780 (78.0%) Power with n=70 per group: 0.907 (90.7%) Power with n=100 per group: 0.978 (97.8%)
library(pwr)
# Parameters
groups <- 3 # Number of groups
f <- 0.25 # Cohen's f (medium effect size)
sig_level <- 0.05 # Significance level (alpha)
power <- 0.8 # Desired power
# Calculate sample size
result <- pwr.anova.test(k = groups,
f = f,
sig.level = sig_level,
power = power)
# Print results
print(result)
print(paste("Total sample size:", ceiling(result$n) * groups))Output:
Balanced one-way analysis of variance power calculation
k = 3
n = 52.3966
f = 0.25
sig.level = 0.05
power = 0.8
NOTE: n is number in each group
Total sample size: 159
Calculate the statistical power for one-way ANOVA:
from statsmodels.stats.power import FTestAnovaPower
# Given parameters
k = 3 # Number of groups
f = 0.25 # Cohen's f effect size
alpha = 0.05 # Significance level
sample_size = 60 # Per group
analysis = FTestAnovaPower()
# Note: nobs must be TOTAL sample size
power = analysis.power(
effect_size=f,
nobs=sample_size * k,
alpha=alpha,
k_groups=k
)
print(f"With n={sample_size} per group (total={sample_size*k}), k={k}, and f={f}:")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=60 per group (total=180), k=3, and f=0.25: Statistical power: 0.8546 (85.46%)
library(pwr)
# Given parameters
k <- 3 # Number of groups
f <- 0.25 # Cohen's f effect size
sig_level <- 0.05 # Significance level
sample_size <- 60 # Per group
# Calculate power
result <- pwr.anova.test(
k = k,
n = sample_size,
f = f,
sig.level = sig_level
)
print(str_glue("With n={sample_size} per group (total={sample_size*k}), k={k}, and f={f}:"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=60 per group (total=180), k=3, and f=0.25: Statistical power: 0.8546 (85.46%)
Calculate the minimum detectable Cohen's f for ANOVA:
from statsmodels.stats.power import FTestAnovaPower
# Given parameters
k = 3 # Number of groups
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 53 # Per group
analysis = FTestAnovaPower()
# Solve for effect size (note: nobs is TOTAL sample size)
f = analysis.solve_power(
effect_size=None,
nobs=sample_size * k,
alpha=alpha,
power=power,
k_groups=k
)
print(f"With n={sample_size} per group (total={sample_size*k}), k={k}, and power={power}:")
print(f"Minimum detectable effect size (Cohen's f): {f:.4f}")Output:
With n=53 per group (total=159), k=3, and power=0.8: Minimum detectable effect size (Cohen's f): 0.2485
library(pwr)
# Given parameters
k <- 3 # Number of groups
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 53 # Per group
# Calculate minimum detectable effect size
result <- pwr.anova.test(
k = k,
n = sample_size,
sig.level = sig_level,
power = power
)
print(str_glue("With n={sample_size} per group (total={sample_size*k}), k={k}, and power={power}:"))
print(str_glue("Minimum detectable effect size (Cohen's f): {round(result$f, 4)}"))Output:
With n=53 per group (total=159), k=3, and power=0.8: Minimum detectable effect size (Cohen's f): 0.2485
The chi-square test is used for categorical data to test independence in contingency tables or goodness-of-fit. Sample size calculations use Cohen's w effect size.
For a chi-square test, the sample size is calculated using:
where:
Cohen's w Effect Size Guidelines:
Calculate the required sample size for a chi-square test with 1 degree of freedom (2x2 contingency table), medium effect size (w = 0.3), 80% power, and α = 0.05:
from statsmodels.stats.power import GofChisquarePower
import numpy as np
# Parameters
df = 1 # Degrees of freedom
w = 0.3 # Cohen's w (medium effect size)
alpha = 0.05 # Significance level
power = 0.8 # Desired power
# Create power analysis object
analysis = GofChisquarePower()
# Calculate sample size
n = analysis.solve_power(
effect_size=w,
nobs=None,
alpha=alpha,
power=power,
n_bins=df + 1 # number of categories = df + 1
)
print(f"Sample size needed: {np.ceil(n):.0f}")
# Verify the power with this sample size
power_check = analysis.power(
effect_size=w,
nobs=np.ceil(n),
alpha=alpha,
n_bins=df + 1
)
print(f"Achieved power: {power_check:.4f} ({power_check*100:.2f}%)")Output:
Sample size needed: 88 Achieved power: 0.8013 (80.13%)
library(pwr)
library(stringr)
# Parameters
df <- 1 # Degrees of freedom
w <- 0.3 # Cohen's w (medium effect size)
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
# Calculate sample size
result <- pwr.chisq.test(
w = w,
df = df,
sig.level = sig_level,
power = power
)
print(result)
print(str_glue("Sample size needed: {ceiling(result$N)}"))Output:
Chi square power calculation
w = 0.3
N = 87.20854
df = 1
sig.level = 0.05
power = 0.8
NOTE: N is the number of observations
Sample size needed: 88Calculate the statistical power with n = 100, df = 1, and w = 0.3:
from statsmodels.stats.power import GofChisquarePower
# Given parameters
df = 1 # Degrees of freedom
w = 0.3 # Cohen's w effect size
alpha = 0.05 # Significance level
sample_size = 100 # Total sample size
analysis = GofChisquarePower()
power = analysis.power(
effect_size=w,
nobs=sample_size,
alpha=alpha,
n_bins=df + 1
)
print(f"With n={sample_size}, df={df}, and w={w}:")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=100, df=1, and w=0.3: Statistical power: 0.8508 (85.08%)
library(pwr)
library(stringr)
# Given parameters
df <- 1 # Degrees of freedom
w <- 0.3 # Cohen's w effect size
sig_level <- 0.05 # Significance level
sample_size <- 100 # Total sample size
# Calculate power
result <- pwr.chisq.test(
w = w,
N = sample_size,
df = df,
sig.level = sig_level
)
print(str_glue("With n={sample_size}, df={df}, and w={w}:"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=100, df=1, and w=0.3: Statistical power: 0.8508 (85.08%)
Calculate the minimum detectable Cohen's w with n = 88, df = 1, and 80% power:
from statsmodels.stats.power import GofChisquarePower
# Given parameters
df = 1 # Degrees of freedom
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 88 # Total sample size
analysis = GofChisquarePower()
w = analysis.solve_power(
effect_size=None,
nobs=sample_size,
alpha=alpha,
power=power,
n_bins=df + 1
)
print(f"With n={sample_size}, df={df}, and power={power}:")
print(f"Minimum detectable effect size (Cohen's w): {w:.4f}")Output:
With n=88, df=1, and power=0.8: Minimum detectable effect size (Cohen's w): 0.2994
library(pwr)
library(stringr)
# Given parameters
df <- 1 # Degrees of freedom
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 88 # Total sample size
# Calculate minimum detectable effect size
result <- pwr.chisq.test(
N = sample_size,
df = df,
sig.level = sig_level,
power = power
)
print(str_glue("With n={sample_size}, df={df}, and power={power}:"))
print(str_glue("Minimum detectable effect size (Cohen's w): {round(result$w, 4)}"))Output:
With n=88, df=1, and power=0.8: Minimum detectable effect size (Cohen's w): 0.2987
Note: The slight difference between Python (0.2994) and R (0.2987) results is normal and expected. Different packages use different numerical algorithms and convergence criteria. Both values are practically equivalent.
Multiple regression models the relationship between a dependent variable and multiple independent variables. Sample size calculations use Cohen's f² effect size (proportion of variance explained).
For multiple regression, the required sample size is:
where:
Cohen's f² Effect Size Guidelines:
Note: Relationship to R²:
Calculate the required sample size for a multiple regression with 5 predictors, medium effect size (f² = 0.15), 80% power, and α = 0.05:
from statsmodels.stats.power import FTestAnovaPower
import numpy as np
# Parameters
num_predictors = 5 # Number of independent variables
f_squared = 0.15 # Cohen's f² (medium effect size)
alpha = 0.05 # Significance level
power = 0.8 # Desired power
# Convert f² to f
cohens_f = np.sqrt(f_squared)
# Create power analysis object
analysis = FTestAnovaPower()
# For multiple regression: k_groups = num_predictors + 1
k_groups = num_predictors + 1
# Solve for required sample size
nobs_result = analysis.solve_power(
effect_size=cohens_f,
nobs=None, # This is what we want to solve for
alpha=alpha,
power=power,
k_groups=k_groups
)
# Round up to get total sample size
total_n = int(np.ceil(nobs_result))
print(f"Cohen's f: {cohens_f:.4f}")
print(f"Sample size needed: {total_n}")
print(f"(Minimum 10-15 observations per predictor recommended)")
# Verify the power with this sample size
power_check = analysis.power(
effect_size=cohens_f,
nobs=total_n,
alpha=alpha,
k_groups=k_groups
)
print(f"Achieved power: {power_check:.4f} ({power_check*100:.2f}%)")Output:
Cohen's f: 0.3873 Sample size needed: 92 (Minimum 10-15 observations per predictor recommended) Achieved power: 0.8042 (80.42%)
library(pwr)
library(stringr)
# Parameters
num_predictors <- 5 # Number of independent variables
f_squared <- 0.15 # Cohen's f² (medium effect size)
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
# Convert f² to f
cohens_f <- sqrt(f_squared)
# Calculate sample size using pwr.f2.test
# u = numerator df = number of predictors
# v = denominator df = n - u - 1
result <- pwr.f2.test(
u = num_predictors,
f2 = f_squared,
sig.level = sig_level,
power = power
)
# Total sample size = v + u + 1
total_n <- ceiling(result$v + num_predictors + 1)
print(result)
print(str_glue("Cohen's f: {round(cohens_f, 4)}"))
print(str_glue("Sample size needed: {total_n}"))Output:
Multiple regression power calculation
u = 5
v = 85.21369
f2 = 0.15
sig.level = 0.05
power = 0.8
Cohen's f: 0.3873
Sample size needed: 92Calculate the statistical power with n = 100, 5 predictors, and f² = 0.15:
from statsmodels.stats.power import FTestAnovaPower
import numpy as np
# Given parameters
num_predictors = 5 # Number of predictors
f_squared = 0.15 # Cohen's f² effect size
alpha = 0.05 # Significance level
sample_size = 100 # Total sample size
# Convert f² to f
cohens_f = np.sqrt(f_squared)
# Create power analysis object
analysis = FTestAnovaPower()
# For multiple regression: k_groups = num_predictors + 1
k_groups = num_predictors + 1
# Calculate power
power = analysis.power(
effect_size=cohens_f,
nobs=sample_size,
alpha=alpha,
k_groups=k_groups
)
print(f"With n={sample_size}, k={num_predictors}, and f²={f_squared}:")
print(f"Statistical power: {power:.4f} ({power*100:.2f}%)")Output:
With n=100, k=5, and f²=0.15: Statistical power: 0.8430 (84.30%)
library(pwr)
library(stringr)
# Given parameters
num_predictors <- 5 # Number of predictors
f_squared <- 0.15 # Cohen's f² effect size
sig_level <- 0.05 # Significance level
sample_size <- 100 # Total sample size
# Calculate v (denominator df)
v <- sample_size - num_predictors - 1
# Calculate power
result <- pwr.f2.test(
u = num_predictors,
v = v,
f2 = f_squared,
sig.level = sig_level
)
print(str_glue("With n={sample_size}, k={num_predictors}, and f²={f_squared}:"))
print(str_glue("Statistical power: {round(result$power, 4)} ({round(result$power*100, 2)}%)"))Output:
With n=100, k=5, and f²=0.15: Statistical power: 0.8489 (84.89%)
Calculate the minimum detectable Cohen's f² with n = 92, 5 predictors, and 80% power:
from statsmodels.stats.power import FTestAnovaPower
import numpy as np
# Given parameters
num_predictors = 5 # Number of predictors
alpha = 0.05 # Significance level
power = 0.8 # Desired power
sample_size = 92 # Total sample size
# Create power analysis object
analysis = FTestAnovaPower()
# For multiple regression: k_groups = num_predictors + 1
k_groups = num_predictors + 1
# Solve for Cohen's f (effect size)
cohens_f = analysis.solve_power(
effect_size=None, # This is what we want to solve for
nobs=sample_size,
alpha=alpha,
power=power,
k_groups=k_groups
)
# Handle array conversion (statsmodels may return array)
if isinstance(cohens_f, np.ndarray):
cohens_f = float(cohens_f.item())
else:
cohens_f = float(cohens_f)
# Convert to f²
f_squared = cohens_f ** 2
print(f"With n={sample_size}, k={num_predictors}, and power={power}:")
print(f"Minimum detectable effect size (Cohen's f²): {f_squared:.4f}")
print(f"Minimum detectable effect size (Cohen's f): {cohens_f:.4f}")Output:
With n=92, k=5, and power=0.8: Minimum detectable effect size (Cohen's f²): 0.1486 Minimum detectable effect size (Cohen's f): 0.3855
library(pwr)
library(stringr)
# Given parameters
num_predictors <- 5 # Number of predictors
sig_level <- 0.05 # Significance level
power <- 0.8 # Desired power
sample_size <- 92 # Total sample size
# Calculate v (denominator df)
v <- sample_size - num_predictors - 1
# Calculate minimum detectable effect size
result <- pwr.f2.test(
u = num_predictors,
v = v,
sig.level = sig_level,
power = power
)
# Convert f to f²
cohens_f <- sqrt(result$f2)
print(str_glue("With n={sample_size}, k={num_predictors}, and power={power}:"))
print(str_glue("Minimum detectable effect size (Cohen's f²): {round(result$f2, 4)}"))
print(str_glue("Minimum detectable effect size (Cohen's f): {round(cohens_f, 4)}"))Output:
With n=92, k=5, and power=0.8: Minimum detectable effect size (Cohen's f²): 0.1486 Minimum detectable effect size (Cohen's f): 0.3855