45template <
typename Iterator>
46auto sum(Iterator first, Iterator last)
48 using value_type =
typename std::iterator_traits<Iterator>::value_type;
49 return std::accumulate(first, last, value_type{});
64template <
typename Iterator,
typename Projection>
65auto sum(Iterator first, Iterator last, Projection proj)
67 using result_type = std::invoke_result_t<Projection,
68 typename std::iterator_traits<Iterator>::value_type>;
70 for (
auto it = first; it != last; ++it) {
71 total += std::invoke(proj, *it);
90template <
typename Iterator>
91std::size_t
count(Iterator first, Iterator last)
93 return static_cast<std::size_t
>(std::distance(first, last));
111template <
typename Iterator>
112double mean(Iterator first, Iterator last)
116 throw std::invalid_argument(
"statcpp::mean: empty range");
119 for (
auto it = first; it != last; ++it) {
120 total +=
static_cast<double>(*it);
122 return total /
static_cast<double>(n);
138template <
typename Iterator,
typename Projection>
139double mean(Iterator first, Iterator last, Projection proj)
143 throw std::invalid_argument(
"statcpp::mean: empty range");
146 for (
auto it = first; it != last; ++it) {
147 total +=
static_cast<double>(std::invoke(proj, *it));
149 return total /
static_cast<double>(n);
168template <
typename Iterator>
169double median(Iterator first, Iterator last)
173 std::random_access_iterator_tag,
174 typename std::iterator_traits<Iterator>::iterator_category>,
175 "statcpp::median requires random access iterators");
179 throw std::invalid_argument(
"statcpp::median: empty range");
184 return (
static_cast<double>(*(first + (mid - 1))) +
static_cast<double>(*(first + mid))) / 2.0;
186 return static_cast<double>(*(first + mid));
203template <
typename Iterator,
typename Projection>
204double median(Iterator first, Iterator last, Projection proj)
208 std::random_access_iterator_tag,
209 typename std::iterator_traits<Iterator>::iterator_category>,
210 "statcpp::median requires random access iterators");
214 throw std::invalid_argument(
"statcpp::median: empty range");
219 return (
static_cast<double>(std::invoke(proj, *(first + (mid - 1))))
220 +
static_cast<double>(std::invoke(proj, *(first + mid)))) / 2.0;
222 return static_cast<double>(std::invoke(proj, *(first + mid)));
241template <
typename Iterator>
242auto mode(Iterator first, Iterator last)
244 using value_type =
typename std::iterator_traits<Iterator>::value_type;
246 throw std::invalid_argument(
"statcpp::mode: empty range");
249 std::map<value_type, std::size_t> freq;
250 for (
auto it = first; it != last; ++it) {
252 if constexpr (std::is_floating_point_v<value_type>) {
253 if (std::isnan(*it))
continue;
259 throw std::invalid_argument(
"statcpp::mode: all values are NaN");
262 auto best = freq.begin();
263 for (
auto it = freq.begin(); it != freq.end(); ++it) {
264 if (it->second > best->second) {
285template <
typename Iterator,
typename Projection>
286auto mode(Iterator first, Iterator last, Projection proj)
288 using result_type = std::invoke_result_t<Projection,
289 typename std::iterator_traits<Iterator>::value_type>;
291 throw std::invalid_argument(
"statcpp::mode: empty range");
294 std::map<result_type, std::size_t> freq;
295 for (
auto it = first; it != last; ++it) {
297 auto val = std::invoke(proj, *it);
298 if constexpr (std::is_floating_point_v<result_type>) {
299 if (std::isnan(val))
continue;
305 throw std::invalid_argument(
"statcpp::mode: all values are NaN");
308 auto best = freq.begin();
309 for (
auto it = freq.begin(); it != freq.end(); ++it) {
310 if (it->second > best->second) {
333template <
typename Iterator>
334auto modes(Iterator first, Iterator last)
335 -> std::vector<typename std::iterator_traits<Iterator>::value_type>
337 using value_type =
typename std::iterator_traits<Iterator>::value_type;
339 throw std::invalid_argument(
"statcpp::modes: empty range");
342 std::map<value_type, std::size_t> freq;
343 for (
auto it = first; it != last; ++it) {
345 if constexpr (std::is_floating_point_v<value_type>) {
346 if (std::isnan(*it))
continue;
352 throw std::invalid_argument(
"statcpp::modes: all values are NaN");
356 std::size_t max_freq = 0;
357 for (
const auto& pair : freq) {
358 if (pair.second > max_freq) {
359 max_freq = pair.second;
364 std::vector<value_type> result;
365 for (
const auto& pair : freq) {
366 if (pair.second == max_freq) {
367 result.push_back(pair.first);
388template <
typename Iterator,
typename Projection>
389auto modes(Iterator first, Iterator last, Projection proj)
390 -> std::vector<std::invoke_result_t<Projection,
391 typename std::iterator_traits<Iterator>::value_type>>
393 using result_type = std::invoke_result_t<Projection,
394 typename std::iterator_traits<Iterator>::value_type>;
396 throw std::invalid_argument(
"statcpp::modes: empty range");
399 std::map<result_type, std::size_t> freq;
400 for (
auto it = first; it != last; ++it) {
402 auto val = std::invoke(proj, *it);
403 if constexpr (std::is_floating_point_v<result_type>) {
404 if (std::isnan(val))
continue;
410 throw std::invalid_argument(
"statcpp::modes: all values are NaN");
414 std::size_t max_freq = 0;
415 for (
const auto& pair : freq) {
416 if (pair.second > max_freq) {
417 max_freq = pair.second;
422 std::vector<result_type> result;
423 for (
const auto& pair : freq) {
424 if (pair.second == max_freq) {
425 result.push_back(pair.first);
448template <
typename Iterator>
453 throw std::invalid_argument(
"statcpp::geometric_mean: empty range");
455 double log_sum = 0.0;
456 for (
auto it = first; it != last; ++it) {
457 if (
static_cast<double>(*it) <= 0.0) {
458 throw std::invalid_argument(
"statcpp::geometric_mean: all values must be positive");
460 log_sum += std::log(
static_cast<double>(*it));
462 return std::exp(log_sum /
static_cast<double>(n));
479template <
typename Iterator,
typename Projection>
484 throw std::invalid_argument(
"statcpp::geometric_mean: empty range");
486 double log_sum = 0.0;
487 for (
auto it = first; it != last; ++it) {
488 double val =
static_cast<double>(std::invoke(proj, *it));
490 throw std::invalid_argument(
"statcpp::geometric_mean: all values must be positive");
492 log_sum += std::log(val);
494 return std::exp(log_sum /
static_cast<double>(n));
518template <
typename Iterator>
523 throw std::invalid_argument(
"statcpp::harmonic_mean: empty range");
525 double reciprocal_sum = 0.0;
526 for (
auto it = first; it != last; ++it) {
527 double val =
static_cast<double>(*it);
532 if (std::abs(val) < std::numeric_limits<double>::min()) {
533 throw std::invalid_argument(
"statcpp::harmonic_mean: zero or near-zero value encountered");
535 reciprocal_sum += 1.0 / val;
537 return static_cast<double>(n) / reciprocal_sum;
554template <
typename Iterator,
typename Projection>
559 throw std::invalid_argument(
"statcpp::harmonic_mean: empty range");
561 double reciprocal_sum = 0.0;
562 for (
auto it = first; it != last; ++it) {
563 double val =
static_cast<double>(std::invoke(proj, *it));
564 if (std::abs(val) < std::numeric_limits<double>::min()) {
565 throw std::invalid_argument(
"statcpp::harmonic_mean: zero or near-zero value encountered");
567 reciprocal_sum += 1.0 / val;
569 return static_cast<double>(n) / reciprocal_sum;
589template <
typename Iterator>
594 std::random_access_iterator_tag,
595 typename std::iterator_traits<Iterator>::iterator_category>,
596 "statcpp::trimmed_mean requires random access iterators");
598 if (proportion < 0.0 || proportion >= 0.5) {
599 throw std::invalid_argument(
"statcpp::trimmed_mean: proportion must be in [0.0, 0.5)");
603 throw std::invalid_argument(
"statcpp::trimmed_mean: empty range");
606 auto trim_count =
static_cast<std::size_t
>(
static_cast<double>(n) * proportion);
607 if (n - 2 * trim_count == 0) {
608 throw std::invalid_argument(
"statcpp::trimmed_mean: all elements trimmed");
612 for (std::size_t i = trim_count; i < n - trim_count; ++i) {
613 total +=
static_cast<double>(*(first + i));
615 return total /
static_cast<double>(n - 2 * trim_count);
633template <
typename Iterator,
typename Projection>
634double trimmed_mean(Iterator first, Iterator last,
double proportion, Projection proj)
638 std::random_access_iterator_tag,
639 typename std::iterator_traits<Iterator>::iterator_category>,
640 "statcpp::trimmed_mean requires random access iterators");
642 if (proportion < 0.0 || proportion >= 0.5) {
643 throw std::invalid_argument(
"statcpp::trimmed_mean: proportion must be in [0.0, 0.5)");
647 throw std::invalid_argument(
"statcpp::trimmed_mean: empty range");
650 auto trim_count =
static_cast<std::size_t
>(
static_cast<double>(n) * proportion);
651 if (n - 2 * trim_count == 0) {
652 throw std::invalid_argument(
"statcpp::trimmed_mean: all elements trimmed");
656 for (std::size_t i = trim_count; i < n - trim_count; ++i) {
657 total +=
static_cast<double>(std::invoke(proj, *(first + i)));
659 return total /
static_cast<double>(n - 2 * trim_count);
679template <
typename Iterator,
typename WeightIterator>
680double weighted_mean(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last)
682 if (std::distance(first, last) != std::distance(weight_first, weight_last)) {
683 throw std::invalid_argument(
"statcpp::weighted_mean: data and weight ranges must have the same size");
687 throw std::invalid_argument(
"statcpp::weighted_mean: empty range");
690 double sum_weighted = 0.0;
691 double sum_weights = 0.0;
692 auto weight_it = weight_first;
694 for (
auto it = first; it != last; ++it, ++weight_it) {
695 double value =
static_cast<double>(*it);
696 double weight =
static_cast<double>(*weight_it);
699 throw std::invalid_argument(
"statcpp::weighted_mean: negative weight");
702 sum_weighted += value * weight;
703 sum_weights += weight;
706 if (sum_weights == 0.0) {
707 throw std::invalid_argument(
"statcpp::weighted_mean: sum of weights is zero");
710 return sum_weighted / sum_weights;
713template <
typename Iterator,
typename WeightIterator>
714[[deprecated(
"Use weighted_mean(first, last, weight_first, weight_last) overload for range safety")]]
715double weighted_mean(Iterator first, Iterator last, WeightIterator weight_first)
719 throw std::invalid_argument(
"statcpp::weighted_mean: empty range");
722 double sum_weighted = 0.0;
723 double sum_weights = 0.0;
724 auto weight_it = weight_first;
726 for (
auto it = first; it != last; ++it, ++weight_it) {
727 double value =
static_cast<double>(*it);
728 double weight =
static_cast<double>(*weight_it);
731 throw std::invalid_argument(
"statcpp::weighted_mean: negative weight");
734 sum_weighted += value * weight;
735 sum_weights += weight;
738 if (sum_weights == 0.0) {
739 throw std::invalid_argument(
"statcpp::weighted_mean: sum of weights is zero");
742 return sum_weighted / sum_weights;
760template <
typename Iterator,
typename WeightIterator,
typename Projection>
761double weighted_mean(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last, Projection proj)
763 if (std::distance(first, last) != std::distance(weight_first, weight_last)) {
764 throw std::invalid_argument(
"statcpp::weighted_mean: data and weight ranges must have the same size");
768 throw std::invalid_argument(
"statcpp::weighted_mean: empty range");
771 double sum_weighted = 0.0;
772 double sum_weights = 0.0;
773 auto weight_it = weight_first;
775 for (
auto it = first; it != last; ++it, ++weight_it) {
776 double value =
static_cast<double>(std::invoke(proj, *it));
777 double weight =
static_cast<double>(*weight_it);
780 throw std::invalid_argument(
"statcpp::weighted_mean: negative weight");
783 sum_weighted += value * weight;
784 sum_weights += weight;
787 if (sum_weights == 0.0) {
788 throw std::invalid_argument(
"statcpp::weighted_mean: sum of weights is zero");
791 return sum_weighted / sum_weights;
794template <
typename Iterator,
typename WeightIterator,
typename Projection>
795[[deprecated(
"Use weighted_mean(first, last, weight_first, weight_last, proj) overload for range safety")]]
796double weighted_mean(Iterator first, Iterator last, WeightIterator weight_first, Projection proj)
800 throw std::invalid_argument(
"statcpp::weighted_mean: empty range");
803 double sum_weighted = 0.0;
804 double sum_weights = 0.0;
805 auto weight_it = weight_first;
807 for (
auto it = first; it != last; ++it, ++weight_it) {
808 double value =
static_cast<double>(std::invoke(proj, *it));
809 double weight =
static_cast<double>(*weight_it);
812 throw std::invalid_argument(
"statcpp::weighted_mean: negative weight");
815 sum_weighted += value * weight;
816 sum_weights += weight;
819 if (sum_weights == 0.0) {
820 throw std::invalid_argument(
"statcpp::weighted_mean: sum of weights is zero");
823 return sum_weighted / sum_weights;
844template <
typename T1,
typename T2>
847 double x =
static_cast<double>(a);
848 double y =
static_cast<double>(b);
850 if (x <= 0.0 || y <= 0.0) {
851 throw std::invalid_argument(
"statcpp::logarithmic_mean: arguments must be positive");
857 if (std::abs(x - y) <= 1e-10 * std::max(x, y)) {
862 return (y - x) / (std::log(y) - std::log(x));
884template <
typename Iterator,
typename WeightIterator>
887 if (std::distance(first, last) != std::distance(weight_first, weight_last)) {
888 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: data and weight ranges must have the same size");
893 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: empty range");
896 double sum_weighted = 0.0;
897 double sum_weights = 0.0;
898 auto weight_it = weight_first;
900 for (
auto it = first; it != last; ++it, ++weight_it) {
901 double value =
static_cast<double>(*it);
902 double weight =
static_cast<double>(*weight_it);
905 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: negative weight");
908 if (std::abs(value) < std::numeric_limits<double>::min()) {
909 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: zero value");
912 sum_weighted += weight / value;
913 sum_weights += weight;
916 if (sum_weights == 0.0) {
917 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: sum of weights is zero");
920 return sum_weights / sum_weighted;
937template <
typename Iterator,
typename WeightIterator>
938[[deprecated(
"Use weighted_harmonic_mean(first, last, weight_first, weight_last) overload for range safety")]]
943 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: empty range");
946 double sum_weighted = 0.0;
947 double sum_weights = 0.0;
948 auto weight_it = weight_first;
950 for (
auto it = first; it != last; ++it, ++weight_it) {
951 double value =
static_cast<double>(*it);
952 double weight =
static_cast<double>(*weight_it);
955 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: negative weight");
958 if (std::abs(value) < std::numeric_limits<double>::min()) {
959 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: zero value");
962 sum_weighted += weight / value;
963 sum_weights += weight;
966 if (sum_weights == 0.0) {
967 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: sum of weights is zero");
970 return sum_weights / sum_weighted;
990template <
typename Iterator,
typename WeightIterator,
typename Projection>
991double weighted_harmonic_mean(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last, Projection proj)
993 if (std::distance(first, last) != std::distance(weight_first, weight_last)) {
994 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: data and weight ranges must have the same size");
999 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: empty range");
1002 double sum_weighted = 0.0;
1003 double sum_weights = 0.0;
1004 auto weight_it = weight_first;
1006 for (
auto it = first; it != last; ++it, ++weight_it) {
1007 double value =
static_cast<double>(std::invoke(proj, *it));
1008 double weight =
static_cast<double>(*weight_it);
1011 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: negative weight");
1014 if (std::abs(value) < std::numeric_limits<double>::min()) {
1015 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: zero value");
1018 sum_weighted += weight / value;
1019 sum_weights += weight;
1022 if (sum_weights == 0.0) {
1023 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: sum of weights is zero");
1026 return sum_weights / sum_weighted;
1045template <
typename Iterator,
typename WeightIterator,
typename Projection>
1046[[deprecated(
"Use weighted_harmonic_mean(first, last, weight_first, weight_last, proj) overload for range safety")]]
1051 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: empty range");
1054 double sum_weighted = 0.0;
1055 double sum_weights = 0.0;
1056 auto weight_it = weight_first;
1058 for (
auto it = first; it != last; ++it, ++weight_it) {
1059 double value =
static_cast<double>(std::invoke(proj, *it));
1060 double weight =
static_cast<double>(*weight_it);
1063 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: negative weight");
1066 if (std::abs(value) < std::numeric_limits<double>::min()) {
1067 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: zero value");
1070 sum_weighted += weight / value;
1071 sum_weights += weight;
1074 if (sum_weights == 0.0) {
1075 throw std::invalid_argument(
"statcpp::weighted_harmonic_mean: sum of weights is zero");
1078 return sum_weights / sum_weighted;
1096template <
typename Iterator>
1097std::size_t
argmin(Iterator first, Iterator last)
1101 throw std::invalid_argument(
"statcpp::argmin: empty range");
1104 auto min_it = std::min_element(first, last);
1105 return static_cast<std::size_t
>(std::distance(first, min_it));
1121template <
typename Iterator,
typename Projection>
1122std::size_t
argmin(Iterator first, Iterator last, Projection proj)
1126 throw std::invalid_argument(
"statcpp::argmin: empty range");
1129 auto min_it = std::min_element(first, last,
1130 [&proj](
const auto& a,
const auto& b) {
1131 return std::invoke(proj, a) < std::invoke(proj, b);
1134 return static_cast<std::size_t
>(std::distance(first, min_it));
1148template <
typename Iterator>
1149std::size_t
argmax(Iterator first, Iterator last)
1153 throw std::invalid_argument(
"statcpp::argmax: empty range");
1156 auto max_it = std::max_element(first, last);
1157 return static_cast<std::size_t
>(std::distance(first, max_it));
1173template <
typename Iterator,
typename Projection>
1174std::size_t
argmax(Iterator first, Iterator last, Projection proj)
1178 throw std::invalid_argument(
"statcpp::argmax: empty range");
1181 auto max_it = std::max_element(first, last,
1182 [&proj](
const auto& a,
const auto& b) {
1183 return std::invoke(proj, a) < std::invoke(proj, b);
1186 return static_cast<std::size_t
>(std::distance(first, max_it));
auto sum(Iterator first, Iterator last)
Sum.
std::size_t argmin(Iterator first, Iterator last)
Returns the index of the minimum value.
double harmonic_mean(Iterator first, Iterator last)
Harmonic mean.
double trimmed_mean(Iterator first, Iterator last, double proportion)
Trimmed mean (accepts a sorted range. proportion: exclusion ratio per side, 0.0 to less than 0....
auto mode(Iterator first, Iterator last)
Mode (returns the smallest value when there are multiple modes: guarantees deterministic behavior)
double mean(Iterator first, Iterator last)
Arithmetic mean.
auto modes(Iterator first, Iterator last) -> std::vector< typename std::iterator_traits< Iterator >::value_type >
Returns all modes (returns a vector sorted in ascending order)
std::size_t argmax(Iterator first, Iterator last)
Returns the index of the maximum value.
double weighted_harmonic_mean(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last)
Weighted harmonic mean (safe range version)
double median(Iterator first, Iterator last)
Median (accepts a sorted range)
double weighted_mean(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last)
Weighted mean.
double geometric_mean(Iterator first, Iterator last)
Geometric mean.
std::size_t count(Iterator first, Iterator last)
Data count.
double logarithmic_mean(T1 a, T2 b)
Logarithmic Mean.