67template <
typename Iterator,
typename Engine = default_random_engine>
68std::vector<typename std::iterator_traits<Iterator>::value_type>
73 throw std::invalid_argument(
"statcpp::bootstrap_sample: empty range");
76 using value_type =
typename std::iterator_traits<Iterator>::value_type;
77 std::vector<value_type> original(first, last);
78 std::vector<value_type> sample(n);
80 std::uniform_int_distribution<std::size_t> dist(0, n - 1);
82 for (std::size_t i = 0; i < n; ++i) {
83 sample[i] = original[dist(engine)];
100template <
typename Iterator>
101std::vector<typename std::iterator_traits<Iterator>::value_type>
138template <
typename Iterator,
typename Statistic,
typename Engine = default_random_engine>
140 std::size_t n_bootstrap = 1000,
double confidence = 0.95,
143 if (confidence <= 0.0 || confidence >= 1.0) {
144 throw std::invalid_argument(
"statcpp::bootstrap: confidence must be in (0, 1)");
147 if (n_bootstrap < 2) {
148 throw std::invalid_argument(
"statcpp::bootstrap: n_bootstrap must be at least 2");
153 throw std::invalid_argument(
"statcpp::bootstrap: need at least 2 elements");
156 using value_type =
typename std::iterator_traits<Iterator>::value_type;
157 std::vector<value_type> original(first, last);
160 double theta_hat = stat_func(original.begin(), original.end());
163 std::vector<double> replicates(n_bootstrap);
165 for (std::size_t b = 0; b < n_bootstrap; ++b) {
167 replicates[b] = stat_func(sample.begin(), sample.end());
171 std::vector<double> sorted_replicates = replicates;
172 std::sort(sorted_replicates.begin(), sorted_replicates.end());
175 double mean_rep = std::accumulate(replicates.begin(), replicates.end(), 0.0) / n_bootstrap;
177 for (
double rep : replicates) {
178 se += (rep - mean_rep) * (rep - mean_rep);
180 se = std::sqrt(se / (n_bootstrap - 1));
183 double bias = mean_rep - theta_hat;
188 double alpha = 1.0 - confidence;
189 std::size_t lower_idx =
static_cast<std::size_t
>(std::floor(alpha / 2.0 *
static_cast<double>(n_bootstrap)));
190 std::size_t upper_idx =
static_cast<std::size_t
>(std::floor((1.0 - alpha / 2.0) *
static_cast<double>(n_bootstrap)));
191 if (upper_idx > 0) upper_idx -= 1;
194 if (lower_idx >= n_bootstrap) lower_idx = n_bootstrap - 1;
195 if (upper_idx >= n_bootstrap) upper_idx = n_bootstrap - 1;
197 double ci_lower = sorted_replicates[lower_idx];
198 double ci_upper = sorted_replicates[upper_idx];
200 return {theta_hat, se, ci_lower, ci_upper, bias, std::move(replicates)};
220template <
typename Iterator,
typename Engine = default_random_engine>
222 std::size_t n_bootstrap = 1000,
double confidence = 0.95,
225 auto stat_func = [](
auto f,
auto l) {
return statcpp::mean(f, l); };
226 return bootstrap(first, last, stat_func, n_bootstrap, confidence, engine);
246template <
typename Iterator,
typename Engine = default_random_engine>
248 std::size_t n_bootstrap = 1000,
double confidence = 0.95,
251 auto stat_func = [](
auto f,
auto l) {
252 std::vector<
typename std::iterator_traits<
decltype(f)>::value_type> sorted(f, l);
253 std::sort(sorted.begin(), sorted.end());
256 return bootstrap(first, last, stat_func, n_bootstrap, confidence, engine);
276template <
typename Iterator,
typename Engine = default_random_engine>
278 std::size_t n_bootstrap = 1000,
double confidence = 0.95,
282 return bootstrap(first, last, stat_func, n_bootstrap, confidence, engine);
309template <
typename Iterator,
typename Statistic,
typename Engine = default_random_engine>
311 std::size_t n_bootstrap = 1000,
double confidence = 0.95,
314 if (confidence <= 0.0 || confidence >= 1.0) {
315 throw std::invalid_argument(
"statcpp::bootstrap_bca: confidence must be in (0, 1)");
318 if (n_bootstrap < 2) {
319 throw std::invalid_argument(
"statcpp::bootstrap_bca: n_bootstrap must be at least 2");
324 throw std::invalid_argument(
"statcpp::bootstrap_bca: need at least 3 elements");
327 using value_type =
typename std::iterator_traits<Iterator>::value_type;
328 std::vector<value_type> original(first, last);
331 double theta_hat = stat_func(original.begin(), original.end());
334 std::vector<double> replicates(n_bootstrap);
336 for (std::size_t b = 0; b < n_bootstrap; ++b) {
338 replicates[b] = stat_func(sample.begin(), sample.end());
342 std::size_t count_less = 0;
343 for (
double rep : replicates) {
344 if (rep < theta_hat) ++count_less;
347 if (count_less == 0) count_less = 1;
348 if (count_less >= n_bootstrap) count_less = n_bootstrap - 1;
349 double z0 =
norm_quantile(
static_cast<double>(count_less) / n_bootstrap);
352 std::vector<double> jackknife_estimates(n);
353 for (std::size_t i = 0; i < n; ++i) {
354 std::vector<value_type> jackknife_sample;
355 jackknife_sample.reserve(n - 1);
356 for (std::size_t j = 0; j < n; ++j) {
358 jackknife_sample.push_back(original[j]);
361 jackknife_estimates[i] = stat_func(jackknife_sample.begin(), jackknife_sample.end());
364 double jack_mean = std::accumulate(jackknife_estimates.begin(), jackknife_estimates.end(), 0.0) / n;
365 double sum_cubed = 0.0;
366 double sum_squared = 0.0;
367 for (
double j : jackknife_estimates) {
368 double d = jack_mean - j;
369 sum_squared += d * d;
370 sum_cubed += d * d * d;
373 double a = (sum_squared == 0.0) ? 0.0 : sum_cubed / (6.0 * std::pow(sum_squared, 1.5));
376 double alpha = 1.0 - confidence;
380 double alpha1 =
norm_cdf(z0 + (z0 + z_alpha_lower) / (1.0 - a * (z0 + z_alpha_lower)));
381 double alpha2 =
norm_cdf(z0 + (z0 + z_alpha_upper) / (1.0 - a * (z0 + z_alpha_upper)));
384 std::vector<double> sorted_replicates = replicates;
385 std::sort(sorted_replicates.begin(), sorted_replicates.end());
387 auto clamp_index = [&](
double a_val) -> std::size_t {
388 double idx = a_val *
static_cast<double>(n_bootstrap);
389 if (idx < 0.0)
return 0;
390 if (idx >=
static_cast<double>(n_bootstrap))
return n_bootstrap - 1;
391 return static_cast<std::size_t
>(idx);
393 std::size_t lower_idx = clamp_index(alpha1);
394 std::size_t upper_idx = clamp_index(alpha2);
396 double ci_lower = sorted_replicates[lower_idx];
397 double ci_upper = sorted_replicates[upper_idx];
400 double mean_rep = std::accumulate(replicates.begin(), replicates.end(), 0.0) / n_bootstrap;
402 for (
double rep : replicates) {
403 se += (rep - mean_rep) * (rep - mean_rep);
405 se = std::sqrt(se / (n_bootstrap - 1));
407 double bias = mean_rep - theta_hat;
409 return {theta_hat, se, ci_lower, ci_upper, bias, std::move(replicates)};
456template <
typename Iterator1,
typename Iterator2,
typename Engine = default_random_engine>
458 Iterator2 first2, Iterator2 last2,
459 std::size_t n_permutations = 10000,
465 if (n1 == 0 || n2 == 0) {
466 throw std::invalid_argument(
"statcpp::permutation_test_two_sample: empty sample");
470 std::vector<double> combined;
471 combined.reserve(n1 + n2);
472 for (
auto it = first1; it != last1; ++it) {
473 combined.push_back(
static_cast<double>(*it));
475 for (
auto it = first2; it != last2; ++it) {
476 combined.push_back(
static_cast<double>(*it));
482 double observed = mean1 - mean2;
485 std::vector<double> perm_stats(n_permutations);
487 for (std::size_t p = 0; p < n_permutations; ++p) {
488 std::shuffle(combined.begin(), combined.end(), engine);
490 double perm_mean1 = std::accumulate(combined.begin(), combined.begin() + n1, 0.0) / n1;
491 double perm_mean2 = std::accumulate(combined.begin() + n1, combined.end(), 0.0) / n2;
492 perm_stats[p] = perm_mean1 - perm_mean2;
497 double abs_observed = std::abs(observed);
498 std::size_t count_extreme = 1;
499 for (
double stat : perm_stats) {
500 if (std::abs(stat) >= abs_observed) {
505 double p_value =
static_cast<double>(count_extreme) /
static_cast<double>(n_permutations + 1);
507 return {observed, p_value, n_permutations, std::move(perm_stats)};
537template <
typename Iterator1,
typename Iterator2,
typename Engine = default_random_engine>
539 Iterator2 first2, Iterator2 last2,
540 std::size_t n_permutations = 10000,
547 throw std::invalid_argument(
"statcpp::permutation_test_paired: samples must have equal length");
550 throw std::invalid_argument(
"statcpp::permutation_test_paired: empty samples");
554 std::vector<double> diffs;
559 while (it1 != last1) {
560 diffs.push_back(
static_cast<double>(*it1) -
static_cast<double>(*it2));
569 std::vector<double> perm_stats(n_permutations);
570 std::uniform_int_distribution<int> coin(0, 1);
572 for (std::size_t p = 0; p < n_permutations; ++p) {
574 for (
double d : diffs) {
575 sum += (coin(engine) == 0) ? d : -d;
577 perm_stats[p] =
sum /
static_cast<double>(n1);
581 double abs_observed = std::abs(observed);
582 std::size_t count_extreme = 1;
583 for (
double stat : perm_stats) {
584 if (std::abs(stat) >= abs_observed) {
589 double p_value =
static_cast<double>(count_extreme) /
static_cast<double>(n_permutations + 1);
591 return {observed, p_value, n_permutations, std::move(perm_stats)};
624template <
typename Iterator1,
typename Iterator2,
typename Engine = default_random_engine>
626 Iterator2 first2, Iterator2 last2,
627 std::size_t n_permutations = 10000,
634 throw std::invalid_argument(
"statcpp::permutation_test_correlation: samples must have equal length");
637 throw std::invalid_argument(
"statcpp::permutation_test_correlation: need at least 3 pairs");
641 std::vector<double> x, y;
645 for (
auto it = first1; it != last1; ++it) {
646 x.push_back(
static_cast<double>(*it));
648 for (
auto it = first2; it != last2; ++it) {
649 y.push_back(
static_cast<double>(*it));
653 auto compute_corr = [n1](
const std::vector<double>& a,
const std::vector<double>& b) {
654 double mean_a = std::accumulate(a.begin(), a.end(), 0.0) / n1;
655 double mean_b = std::accumulate(b.begin(), b.end(), 0.0) / n1;
657 double cov = 0.0, var_a = 0.0, var_b = 0.0;
658 for (std::size_t i = 0; i < n1; ++i) {
659 double da = a[i] - mean_a;
660 double db = b[i] - mean_b;
666 return cov / std::sqrt(var_a * var_b);
670 double observed = compute_corr(x, y);
673 std::vector<double> perm_stats(n_permutations);
674 std::vector<double> y_perm = y;
676 for (std::size_t p = 0; p < n_permutations; ++p) {
677 std::shuffle(y_perm.begin(), y_perm.end(), engine);
678 perm_stats[p] = compute_corr(x, y_perm);
683 double abs_observed = std::abs(observed);
684 std::size_t count_extreme = 1;
685 for (
double stat : perm_stats) {
686 if (std::abs(stat) >= abs_observed) {
691 double p_value =
static_cast<double>(count_extreme) /
static_cast<double>(n_permutations + 1);
693 return {observed, p_value, n_permutations, std::move(perm_stats)};
Basic statistical computation functions.
Continuous distribution functions.
Dispersion and variance calculation functions.
permutation_result permutation_test_paired(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2, std::size_t n_permutations=10000, Engine &engine=get_random_engine())
Perform paired permutation test.
double sample_stddev(Iterator first, Iterator last)
Sample standard deviation.
auto sum(Iterator first, Iterator last)
Sum.
double norm_cdf(double x)
Standard normal CDF.
bootstrap_result bootstrap(Iterator first, Iterator last, Statistic stat_func, std::size_t n_bootstrap=1000, double confidence=0.95, Engine &engine=get_random_engine())
Perform general bootstrap estimation.
permutation_result permutation_test_correlation(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2, std::size_t n_permutations=10000, Engine &engine=get_random_engine())
Perform permutation test for correlation.
double norm_quantile(double p)
Standard normal quantile function.
bootstrap_result bootstrap_stddev(Iterator first, Iterator last, std::size_t n_bootstrap=1000, double confidence=0.95, Engine &engine=get_random_engine())
Perform bootstrap estimation of the standard deviation.
bootstrap_result bootstrap_median(Iterator first, Iterator last, std::size_t n_bootstrap=1000, double confidence=0.95, Engine &engine=get_random_engine())
Perform bootstrap estimation of the median.
bootstrap_result bootstrap_mean(Iterator first, Iterator last, std::size_t n_bootstrap=1000, double confidence=0.95, Engine &engine=get_random_engine())
Perform bootstrap estimation of the mean.
bootstrap_result bootstrap_bca(Iterator first, Iterator last, Statistic stat_func, std::size_t n_bootstrap=1000, double confidence=0.95, Engine &engine=get_random_engine())
Compute BCa (bias-corrected and accelerated) bootstrap confidence interval.
double mean(Iterator first, Iterator last)
Arithmetic mean.
default_random_engine & get_random_engine()
Singleton accessor for global random engine.
permutation_result permutation_test_two_sample(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2, std::size_t n_permutations=10000, Engine &engine=get_random_engine())
Perform two-sample permutation test (test of difference in means)
double median(Iterator first, Iterator last)
Median (accepts a sorted range)
std::vector< typename std::iterator_traits< Iterator >::value_type > bootstrap_sample(Iterator first, Iterator last, Engine &engine)
Generate a single bootstrap sample.
std::size_t count(Iterator first, Iterator last)
Data count.
Order statistics implementation.
Random engine wrapper and utilities.
Structure to store bootstrap estimation results.
double ci_lower
Lower bound of confidence interval.
double ci_upper
Upper bound of confidence interval.
double estimate
Estimated statistic computed from original data.
std::vector< double > replicates
All bootstrap replicate statistics.
double standard_error
Bootstrap standard error.
double bias
Bias (replicate mean - estimate)
Structure to store permutation test results.
std::vector< double > permutation_distribution
Distribution of permutation statistics.
double p_value
Two-sided p-value.
double observed_statistic
Observed test statistic.
std::size_t n_permutations
Number of permutations performed.