70 std::random_access_iterator_tag,
71 typename std::iterator_traits<Iterator>::iterator_category>,
72 "statcpp::interpolate_at requires random access iterators");
74 double index = p *
static_cast<double>(n - 1);
75 auto lo =
static_cast<std::size_t
>(std::floor(index));
76 double frac = index -
static_cast<double>(lo);
78 return static_cast<double>(*(first + lo));
80 return static_cast<double>(*(first + lo)) * (1.0 - frac)
81 +
static_cast<double>(*(first + lo + 1)) * frac;
96double interpolate_at(Iterator first, std::size_t n,
double p, Projection proj)
100 std::random_access_iterator_tag,
101 typename std::iterator_traits<Iterator>::iterator_category>,
102 "statcpp::interpolate_at requires random access iterators");
104 double index = p *
static_cast<double>(n - 1);
105 auto lo =
static_cast<std::size_t
>(std::floor(index));
106 double frac = index -
static_cast<double>(lo);
108 return static_cast<double>(std::invoke(proj, *(first + lo)));
110 return static_cast<double>(std::invoke(proj, *(first + lo))) * (1.0 - frac)
111 +
static_cast<double>(std::invoke(proj, *(first + lo + 1))) * frac;
148auto minimum(Iterator first, Iterator last, Projection proj)
151 throw std::invalid_argument(
"statcpp::minimum: empty range");
153 auto min_val = std::invoke(proj, *first);
154 for (
auto it = std::next(first); it != last; ++it) {
155 auto val = std::invoke(proj, *it);
197auto maximum(Iterator first, Iterator last, Projection proj)
200 throw std::invalid_argument(
"statcpp::maximum: empty range");
202 auto max_val = std::invoke(proj, *first);
203 for (
auto it = std::next(first); it != last; ++it) {
204 auto val = std::invoke(proj, *it);
344 std::random_access_iterator_tag,
345 typename std::iterator_traits<Iterator>::iterator_category>,
346 "statcpp::five_number_summary requires random access iterators");
348 auto n =
static_cast<std::size_t
>(std::distance(first, last));
350 throw std::invalid_argument(
"statcpp::five_number_summary: empty range");
353 static_cast<double>(*first),
357 static_cast<double>(*(first + (n - 1)))
377 std::random_access_iterator_tag,
378 typename std::iterator_traits<Iterator>::iterator_category>,
379 "statcpp::five_number_summary requires random access iterators");
381 auto n =
static_cast<std::size_t
>(std::distance(first, last));
383 throw std::invalid_argument(
"statcpp::five_number_summary: empty range");
386 static_cast<double>(std::invoke(proj, *first)),
390 static_cast<double>(std::invoke(proj, *(first + (n - 1))))
413double weighted_median(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last)
415 if (std::distance(first, last) != std::distance(weight_first, weight_last)) {
416 throw std::invalid_argument(
"statcpp::weighted_median: data and weight ranges must have the same size");
419 auto n =
static_cast<std::size_t
>(std::distance(first, last));
421 throw std::invalid_argument(
"statcpp::weighted_median: empty range");
425 std::vector<std::pair<double, double>> pairs;
427 auto weight_it = weight_first;
428 for (
auto it = first; it != last; ++it, ++weight_it) {
429 double value =
static_cast<double>(*it);
430 double weight =
static_cast<double>(*weight_it);
432 throw std::invalid_argument(
"statcpp::weighted_median: negative weight");
434 pairs.emplace_back(value, weight);
438 std::sort(pairs.begin(), pairs.end(),
439 [](
const auto& a,
const auto& b) { return a.first < b.first; });
442 double total_weight = 0.0;
443 for (
const auto& p : pairs) {
444 total_weight += p.second;
447 if (total_weight == 0.0) {
448 throw std::invalid_argument(
"statcpp::weighted_median: sum of weights is zero");
456 double cumulative = 0.0;
457 double half_weight = total_weight / 2.0;
458 const double tol = std::numeric_limits<double>::epsilon() * half_weight;
460 for (std::size_t i = 0; i < pairs.size(); ++i) {
461 cumulative += pairs[i].second;
462 if (cumulative >= half_weight) {
464 if (std::abs(cumulative - half_weight) <= tol) {
465 for (std::size_t j = i + 1; j < pairs.size(); ++j) {
466 if (pairs[j].second > 0.0) {
467 return (pairs[i].first + pairs[j].first) / 2.0;
471 return pairs[i].first;
475 return pairs.back().first;
495 auto n =
static_cast<std::size_t
>(std::distance(first, last));
497 throw std::invalid_argument(
"statcpp::weighted_median: empty range");
501 std::vector<std::pair<double, double>> pairs;
503 auto weight_it = weight_first;
504 for (
auto it = first; it != last; ++it, ++weight_it) {
505 double value =
static_cast<double>(*it);
506 double weight =
static_cast<double>(*weight_it);
508 throw std::invalid_argument(
"statcpp::weighted_median: negative weight");
510 pairs.emplace_back(value, weight);
514 std::sort(pairs.begin(), pairs.end(),
515 [](
const auto& a,
const auto& b) { return a.first < b.first; });
518 double total_weight = 0.0;
519 for (
const auto& p : pairs) {
520 total_weight += p.second;
523 if (total_weight == 0.0) {
524 throw std::invalid_argument(
"statcpp::weighted_median: sum of weights is zero");
532 double cumulative = 0.0;
533 double half_weight = total_weight / 2.0;
534 const double tol = std::numeric_limits<double>::epsilon() * half_weight;
536 for (std::size_t i = 0; i < pairs.size(); ++i) {
537 cumulative += pairs[i].second;
538 if (cumulative >= half_weight) {
540 if (std::abs(cumulative - half_weight) <= tol) {
541 for (std::size_t j = i + 1; j < pairs.size(); ++j) {
542 if (pairs[j].second > 0.0) {
543 return (pairs[i].first + pairs[j].first) / 2.0;
547 return pairs[i].first;
551 return pairs.back().first;
571double weighted_median(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last, Projection proj)
573 if (std::distance(first, last) != std::distance(weight_first, weight_last)) {
574 throw std::invalid_argument(
"statcpp::weighted_median: data and weight ranges must have the same size");
577 auto n =
static_cast<std::size_t
>(std::distance(first, last));
579 throw std::invalid_argument(
"statcpp::weighted_median: empty range");
583 std::vector<std::pair<double, double>> pairs;
585 auto weight_it = weight_first;
586 for (
auto it = first; it != last; ++it, ++weight_it) {
587 double value =
static_cast<double>(std::invoke(proj, *it));
588 double weight =
static_cast<double>(*weight_it);
590 throw std::invalid_argument(
"statcpp::weighted_median: negative weight");
592 pairs.emplace_back(value, weight);
596 std::sort(pairs.begin(), pairs.end(),
597 [](
const auto& a,
const auto& b) { return a.first < b.first; });
600 double total_weight = 0.0;
601 for (
const auto& p : pairs) {
602 total_weight += p.second;
605 if (total_weight == 0.0) {
606 throw std::invalid_argument(
"statcpp::weighted_median: sum of weights is zero");
614 double cumulative = 0.0;
615 double half_weight = total_weight / 2.0;
616 const double tol = std::numeric_limits<double>::epsilon() * half_weight;
618 for (std::size_t i = 0; i < pairs.size(); ++i) {
619 cumulative += pairs[i].second;
620 if (cumulative >= half_weight) {
622 if (std::abs(cumulative - half_weight) <= tol) {
623 for (std::size_t j = i + 1; j < pairs.size(); ++j) {
624 if (pairs[j].second > 0.0) {
625 return (pairs[i].first + pairs[j].first) / 2.0;
629 return pairs[i].first;
633 return pairs.back().first;
651double weighted_median(Iterator first, Iterator last, WeightIterator weight_first, Projection proj)
653 auto n =
static_cast<std::size_t
>(std::distance(first, last));
655 throw std::invalid_argument(
"statcpp::weighted_median: empty range");
659 std::vector<std::pair<double, double>> pairs;
661 auto weight_it = weight_first;
662 for (
auto it = first; it != last; ++it, ++weight_it) {
663 double value =
static_cast<double>(std::invoke(proj, *it));
664 double weight =
static_cast<double>(*weight_it);
666 throw std::invalid_argument(
"statcpp::weighted_median: negative weight");
668 pairs.emplace_back(value, weight);
672 std::sort(pairs.begin(), pairs.end(),
673 [](
const auto& a,
const auto& b) { return a.first < b.first; });
676 double total_weight = 0.0;
677 for (
const auto& p : pairs) {
678 total_weight += p.second;
681 if (total_weight == 0.0) {
682 throw std::invalid_argument(
"statcpp::weighted_median: sum of weights is zero");
690 double cumulative = 0.0;
691 double half_weight = total_weight / 2.0;
692 const double tol = std::numeric_limits<double>::epsilon() * half_weight;
694 for (std::size_t i = 0; i < pairs.size(); ++i) {
695 cumulative += pairs[i].second;
696 if (cumulative >= half_weight) {
698 if (std::abs(cumulative - half_weight) <= tol) {
699 for (std::size_t j = i + 1; j < pairs.size(); ++j) {
700 if (pairs[j].second > 0.0) {
701 return (pairs[i].first + pairs[j].first) / 2.0;
705 return pairs[i].first;
709 return pairs.back().first;
732double weighted_percentile(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last,
double p)
734 if (std::distance(first, last) != std::distance(weight_first, weight_last)) {
735 throw std::invalid_argument(
"statcpp::weighted_percentile: data and weight ranges must have the same size");
738 if (!(0.0 <= p && p <= 1.0)) {
739 throw std::invalid_argument(
"statcpp::weighted_percentile: p must be in [0, 1]");
742 auto n =
static_cast<std::size_t
>(std::distance(first, last));
744 throw std::invalid_argument(
"statcpp::weighted_percentile: empty range");
748 std::vector<std::pair<double, double>> pairs;
750 auto weight_it = weight_first;
751 for (
auto it = first; it != last; ++it, ++weight_it) {
752 double value =
static_cast<double>(*it);
753 double weight =
static_cast<double>(*weight_it);
755 throw std::invalid_argument(
"statcpp::weighted_percentile: negative weight");
757 pairs.emplace_back(value, weight);
761 std::sort(pairs.begin(), pairs.end(),
762 [](
const auto& a,
const auto& b) { return a.first < b.first; });
765 double total_weight = 0.0;
766 for (
const auto& pair : pairs) {
767 total_weight += pair.second;
770 if (total_weight == 0.0) {
771 throw std::invalid_argument(
"statcpp::weighted_percentile: sum of weights is zero");
775 if (p <= 0.0)
return pairs.front().first;
776 if (p >= 1.0)
return pairs.back().first;
780 double target = p * total_weight;
781 const double tol = std::numeric_limits<double>::epsilon() * total_weight;
782 double cumulative = 0.0;
784 for (std::size_t i = 0; i < pairs.size(); ++i) {
785 cumulative += pairs[i].second;
786 if (cumulative >= target) {
788 if (std::abs(cumulative - target) <= tol && i + 1 < pairs.size()) {
789 return (pairs[i].first + pairs[i + 1].first) / 2.0;
791 return pairs[i].first;
795 return pairs.back().first;
816 if (!(0.0 <= p && p <= 1.0)) {
817 throw std::invalid_argument(
"statcpp::weighted_percentile: p must be in [0, 1]");
820 auto n =
static_cast<std::size_t
>(std::distance(first, last));
822 throw std::invalid_argument(
"statcpp::weighted_percentile: empty range");
826 std::vector<std::pair<double, double>> pairs;
828 auto weight_it = weight_first;
829 for (
auto it = first; it != last; ++it, ++weight_it) {
830 double value =
static_cast<double>(*it);
831 double weight =
static_cast<double>(*weight_it);
833 throw std::invalid_argument(
"statcpp::weighted_percentile: negative weight");
835 pairs.emplace_back(value, weight);
839 std::sort(pairs.begin(), pairs.end(),
840 [](
const auto& a,
const auto& b) { return a.first < b.first; });
843 double total_weight = 0.0;
844 for (
const auto& pair : pairs) {
845 total_weight += pair.second;
848 if (total_weight == 0.0) {
849 throw std::invalid_argument(
"statcpp::weighted_percentile: sum of weights is zero");
853 if (p <= 0.0)
return pairs.front().first;
854 if (p >= 1.0)
return pairs.back().first;
858 double target = p * total_weight;
859 const double tol = std::numeric_limits<double>::epsilon() * total_weight;
860 double cumulative = 0.0;
862 for (std::size_t i = 0; i < pairs.size(); ++i) {
863 cumulative += pairs[i].second;
864 if (cumulative >= target) {
866 if (std::abs(cumulative - target) <= tol && i + 1 < pairs.size()) {
867 return (pairs[i].first + pairs[i + 1].first) / 2.0;
869 return pairs[i].first;
873 return pairs.back().first;
894double weighted_percentile(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last,
double p, Projection proj)
896 if (std::distance(first, last) != std::distance(weight_first, weight_last)) {
897 throw std::invalid_argument(
"statcpp::weighted_percentile: data and weight ranges must have the same size");
900 if (!(0.0 <= p && p <= 1.0)) {
901 throw std::invalid_argument(
"statcpp::weighted_percentile: p must be in [0, 1]");
904 auto n =
static_cast<std::size_t
>(std::distance(first, last));
906 throw std::invalid_argument(
"statcpp::weighted_percentile: empty range");
910 std::vector<std::pair<double, double>> pairs;
912 auto weight_it = weight_first;
913 for (
auto it = first; it != last; ++it, ++weight_it) {
914 double value =
static_cast<double>(std::invoke(proj, *it));
915 double weight =
static_cast<double>(*weight_it);
917 throw std::invalid_argument(
"statcpp::weighted_percentile: negative weight");
919 pairs.emplace_back(value, weight);
923 std::sort(pairs.begin(), pairs.end(),
924 [](
const auto& a,
const auto& b) { return a.first < b.first; });
927 double total_weight = 0.0;
928 for (
const auto& pair : pairs) {
929 total_weight += pair.second;
932 if (total_weight == 0.0) {
933 throw std::invalid_argument(
"statcpp::weighted_percentile: sum of weights is zero");
937 if (p <= 0.0)
return pairs.front().first;
938 if (p >= 1.0)
return pairs.back().first;
942 double target = p * total_weight;
943 const double tol = std::numeric_limits<double>::epsilon() * total_weight;
944 double cumulative = 0.0;
946 for (std::size_t i = 0; i < pairs.size(); ++i) {
947 cumulative += pairs[i].second;
948 if (cumulative >= target) {
950 if (std::abs(cumulative - target) <= tol && i + 1 < pairs.size()) {
951 return (pairs[i].first + pairs[i + 1].first) / 2.0;
953 return pairs[i].first;
957 return pairs.back().first;
976double weighted_percentile(Iterator first, Iterator last, WeightIterator weight_first,
double p, Projection proj)
978 if (!(0.0 <= p && p <= 1.0)) {
979 throw std::invalid_argument(
"statcpp::weighted_percentile: p must be in [0, 1]");
982 auto n =
static_cast<std::size_t
>(std::distance(first, last));
984 throw std::invalid_argument(
"statcpp::weighted_percentile: empty range");
988 std::vector<std::pair<double, double>> pairs;
990 auto weight_it = weight_first;
991 for (
auto it = first; it != last; ++it, ++weight_it) {
992 double value =
static_cast<double>(std::invoke(proj, *it));
993 double weight =
static_cast<double>(*weight_it);
995 throw std::invalid_argument(
"statcpp::weighted_percentile: negative weight");
997 pairs.emplace_back(value, weight);
1001 std::sort(pairs.begin(), pairs.end(),
1002 [](
const auto& a,
const auto& b) { return a.first < b.first; });
1005 double total_weight = 0.0;
1006 for (
const auto& pair : pairs) {
1007 total_weight += pair.second;
1010 if (total_weight == 0.0) {
1011 throw std::invalid_argument(
"statcpp::weighted_percentile: sum of weights is zero");
1015 if (p <= 0.0)
return pairs.front().first;
1016 if (p >= 1.0)
return pairs.back().first;
1020 double target = p * total_weight;
1021 const double tol = std::numeric_limits<double>::epsilon() * total_weight;
1022 double cumulative = 0.0;
1024 for (std::size_t i = 0; i < pairs.size(); ++i) {
1025 cumulative += pairs[i].second;
1026 if (cumulative >= target) {
1028 if (std::abs(cumulative - target) <= tol && i + 1 < pairs.size()) {
1029 return (pairs[i].first + pairs[i + 1].first) / 2.0;
1031 return pairs[i].first;
1035 return pairs.back().first;
double weighted_percentile(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last, double p)
Weighted percentile (safe overload)
double weighted_median(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last)
Weighted median (safe overload)