The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
Determining the minimum sample size needed to achieve a target performance in Machine Learning models is a fundamental challenge in applied research. Unlike traditional statistical analysis where tools like G*Power provide closed-form solutions, machine learning lacks analytical formulas for sample-size planning.
The ml_sample_size() function in PsyML implements a
Monte Carlo simulation approach that:
This vignette demonstrates the complete workflow for sample size estimation.
A learning curve plots model performance as a function of training sample size. It typically shows:
The learning curve is modeled as:
\[\text{Performance}(n) = a - b \cdot n^{-c}\]
Where:
The recommended sample size \(n^*\) is the smallest \(n\) satisfying:
Let’s estimate the sample size needed to achieve AUC ≥ 0.80 with Random Forest.
library(PsyML)
# Simulate a classification problem
result <- ml_sample_size(
task = "classification",
metric = "auc",
target = 0.80,
n_grid = c(100, 200, 300, 400, 600, 800, 1000),
reps = 100, # 100 Monte Carlo repetitions
sim_params = list(
p = 20, # 20 predictors
prevalence = 0.20, # 20% positive class
signal = 2.5 # Moderate signal (AUC ~0.85-0.90 achievable)
),
seed = 123
)# View full results
print(result)
# Recommended sample size
result$recommend_n
# [1] 600
# Summary table with SE of success proportion
result$summary[, c("n", "mean", "sd", "p_meet", "se_p_meet")]
# n mean sd p_meet se_p_meet
# 1 100 0.760 0.025 0.12 0.032
# 2 200 0.795 0.018 0.45 0.050
# 3 300 0.815 0.015 0.78 0.041
# 4 400 0.825 0.012 0.92 0.027
# 5 600 0.840 0.010 0.98 0.014
# ...| Component | Description |
|---|---|
mean |
Average metric across R repetitions |
sd |
Standard deviation (variability) |
p_meet |
Proportion of runs meeting target (\(p_k\)) |
se_p_meet |
Standard error of \(p_k\) |
The precision of \(p_k\) depends on R. The standard error follows:
\[SE(p_k) \approx \sqrt{\frac{p_k(1-p_k)}{R}}\]
| R | SE at p=0.80 | Use Case |
|---|---|---|
| 50 | 0.057 | Exploratory |
| 100 | 0.040 | Standard |
| 200 | 0.028 | Publication |
# For exploratory analysis
result_quick <- ml_sample_size(
task = "classification",
target = 0.80,
seed = 2026,
reps = 50 # Quick exploration
)
# For publication-quality results
result_precise <- ml_sample_size(
task = "classification",
target = 0.80,
seed = 2026,
reps = 200 # High precision
)The function automatically displays SE guidance:
Monte Carlo settings:
- Repetitions (R): 100
- Approx SE(p_k) at p=0.80: 0.04
When your dataset is small, a single test partition may yield unstable estimates. The robustness protocol uses multiple independent partitions.
# Load a small dataset
data(PimaIndiansDiabetes, package = "mlbench")
# Use 5 independent test partitions
result <- ml_sample_size(
data = PimaIndiansDiabetes,
formula = diabetes ~ .,
task = "classification",
metric = "auc",
target = 0.75,
positive = "pos",
seed = 2026,
reps = 100,
n_outer_splits = 5, # 5 independent partitions
verbose = TRUE
)# View results across partitions
result$outer_splits_results
# $median: 50 # Median n* (use this as recommendation)
# $range: [50, 150] # Range of estimates
# $cv: 0.559 # Coefficient of variation
# If CV > 50%, there's high uncertainty
# Report: "n* = 50 (range: 50-150, CV = 56%)"Guideline: If the range exceeds 50% of the median, report the full range and acknowledge high uncertainty.
For formal inference about \(n^*\), use bootstrap CIs:
result <- ml_sample_size(
task = "classification",
target = 0.80,
seed = 2026,
reps = 100,
bootstrap_ci = TRUE, # Enable bootstrap
n_boot = 1000 # 1000 resamples
)
# View 95% CI (values will vary)
result$recommend_n_ci
# $ci_lower: 400 # Lower bound of 95% CI
# $ci_upper: 800 # Upper bound of 95% CI
# $ci_median: 600 # Bootstrap medianInterpretation: “The recommended sample size is n* = 600 (95% CI: 400-800).”
Compare sample requirements across algorithms:
result <- ml_sample_size(
task = "classification",
model = c("rf", "xgboost", "glm"), # Compare 3 models
metric = "auc",
target = 0.80,
seed = 2026,
reps = 50
)
# View recommendations by model (values will vary)
result$recommend_n
# $rf: 600 # or higher depending on data
# $xgboost: 600 # similar to RF
# $glm: 200 # typically needs less data
# GLM needs less data but may have lower asymptotic performance
# RF/XGBoost need more data but achieve higher maximum AUCTrade-off: Simpler models (GLM) reach targets with less data but have lower ceilings. Complex models (RF, XGBoost) need more data but achieve better asymptotic performance.
Always verify the power-law fit before extrapolating:
# Check fit diagnostics
result$curve_diagnostics
# $pseudo_r2: 0.967 # Good: > 0.95
# $rmse_fit: 0.008 # Low fitting error
# $fit_quality: "good" # Automatic classification
# Fit quality interpretation:
# - "good": R² ≥ 0.95 (safe to extrapolate)
# - "acceptable": R² ≥ 0.80 (extrapolate with caution)
# - "poor": R² < 0.80 (do not extrapolate)# Where does performance plateau?
# Absolute delta (within 0.01 of asymptote)
estimate_n_plateau(result, delta = 0.01)
# [1] 8500
# Relative delta (within 1% of target)
estimate_n_plateau(result, delta = 0.01, relative = TRUE)
# Using relative delta: 0.01 x 0.8 = 0.008
# [1] 12000Interpretation: Beyond n = 8,500, additional data yields < 0.01 AUC improvement.
The method works for regression with appropriate metrics:
# Quick exploration with low R
result_explore <- ml_sample_size(
task = "classification",
target = 0.80,
seed = 2026,
reps = 30,
n_grid = seq(100, 1000, by = 100)
)
# Refine with higher R after identifying the region
result_final <- ml_sample_size(
task = "classification",
target = 0.80,
seed = 2026,
reps = 200,
n_grid = c(300, 400, 500, 600, 700, 800) # Focus on relevant range
)Good reporting practice:
“Using Monte Carlo simulation with R = 100 repetitions, we estimated n* = 600 (95% CI: 400-800) to achieve AUC ≥ 0.80 with probability ≥ 0.80. The power-law fit quality was good (pseudo-R² = 0.97).”
# If recommend_n is NA or Inf:
# 1. Check if target exceeds the asymptote
result$curve_fit # Look at coefficient 'a'
# 2. Lower your target or increase signal strength
result <- ml_sample_size(..., target = 0.75) # Lower target
# 3. Use a different algorithm
result <- ml_sample_size(..., model = "glm") # Simpler model| Scenario | Recommendation |
|---|---|
| Exploratory | R = 50, single partition |
| Standard | R = 100, check diagnostics |
| Publication | R ≥ 200, bootstrap CI |
| Small N (<1000) | n_outer_splits = 5-10 |
| Extrapolation | Verify R² > 0.95, n < 2× max observed |
| With tuning | Add 20-30% safety margin |
Ventura-Leon, J. (2026). Sample size estimation for machine learning via Monte Carlo simulation: Learning curves and power laws. PsyArXiv. https://doi.org/10.31234/osf.io/6xrtu_v1
Figueroa, R. L., et al. (2012). Predicting sample size required for classification performance. BMC Medical Informatics and Decision Making, 12, 8. https://doi.org/10.1186/1472-6947-12-8
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.