45template <
typename Iterator1,
typename Iterator2>
47 Iterator2 first2, Iterator2 last2)
49 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
50 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
52 if (n1 == 0 || n2 == 0) {
53 throw std::invalid_argument(
"statcpp::population_covariance: empty range");
56 throw std::invalid_argument(
"statcpp::population_covariance: ranges must have equal length");
65 for (; it1 != last1; ++it1, ++it2) {
66 sum += (
static_cast<double>(*it1) - mean_x) * (
static_cast<double>(*it2) - mean_y);
69 return sum /
static_cast<double>(n1);
86template <
typename Iterator1,
typename Iterator2>
88 Iterator2 first2, Iterator2 last2,
89 double mean_x,
double mean_y)
91 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
92 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
94 if (n1 == 0 || n2 == 0) {
95 throw std::invalid_argument(
"statcpp::population_covariance: empty range");
98 throw std::invalid_argument(
"statcpp::population_covariance: ranges must have equal length");
104 for (; it1 != last1; ++it1, ++it2) {
105 sum += (
static_cast<double>(*it1) - mean_x) * (
static_cast<double>(*it2) - mean_y);
108 return sum /
static_cast<double>(n1);
127template <
typename Iterator1,
typename Iterator2,
typename Projection1,
typename Projection2>
129 Iterator2 first2, Iterator2 last2,
130 Projection1 proj1, Projection2 proj2)
132 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
133 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
135 if (n1 == 0 || n2 == 0) {
136 throw std::invalid_argument(
"statcpp::population_covariance: empty range");
139 throw std::invalid_argument(
"statcpp::population_covariance: ranges must have equal length");
148 for (; it1 != last1; ++it1, ++it2) {
149 sum += (
static_cast<double>(std::invoke(proj1, *it1)) - mean_x)
150 * (
static_cast<double>(std::invoke(proj2, *it2)) - mean_y);
153 return sum /
static_cast<double>(n1);
171template <
typename Iterator1,
typename Iterator2>
173 Iterator2 first2, Iterator2 last2)
175 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
176 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
178 if (n1 == 0 || n2 == 0) {
179 throw std::invalid_argument(
"statcpp::sample_covariance: empty range");
182 throw std::invalid_argument(
"statcpp::sample_covariance: ranges must have equal length");
185 throw std::invalid_argument(
"statcpp::sample_covariance: need at least 2 elements");
194 for (; it1 != last1; ++it1, ++it2) {
195 sum += (
static_cast<double>(*it1) - mean_x) * (
static_cast<double>(*it2) - mean_y);
198 return sum /
static_cast<double>(n1 - 1);
215template <
typename Iterator1,
typename Iterator2>
217 Iterator2 first2, Iterator2 last2,
218 double mean_x,
double mean_y)
220 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
221 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
223 if (n1 == 0 || n2 == 0) {
224 throw std::invalid_argument(
"statcpp::sample_covariance: empty range");
227 throw std::invalid_argument(
"statcpp::sample_covariance: ranges must have equal length");
230 throw std::invalid_argument(
"statcpp::sample_covariance: need at least 2 elements");
236 for (; it1 != last1; ++it1, ++it2) {
237 sum += (
static_cast<double>(*it1) - mean_x) * (
static_cast<double>(*it2) - mean_y);
240 return sum /
static_cast<double>(n1 - 1);
259template <
typename Iterator1,
typename Iterator2,
typename Projection1,
typename Projection2>
261 Iterator2 first2, Iterator2 last2,
262 Projection1 proj1, Projection2 proj2)
264 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
265 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
267 if (n1 == 0 || n2 == 0) {
268 throw std::invalid_argument(
"statcpp::sample_covariance: empty range");
271 throw std::invalid_argument(
"statcpp::sample_covariance: ranges must have equal length");
274 throw std::invalid_argument(
"statcpp::sample_covariance: need at least 2 elements");
283 for (; it1 != last1; ++it1, ++it2) {
284 sum += (
static_cast<double>(std::invoke(proj1, *it1)) - mean_x)
285 * (
static_cast<double>(std::invoke(proj2, *it2)) - mean_y);
288 return sum /
static_cast<double>(n1 - 1);
302template <
typename Iterator1,
typename Iterator2>
304 Iterator2 first2, Iterator2 last2)
322template <
typename Iterator1,
typename Iterator2>
324 Iterator2 first2, Iterator2 last2,
325 double mean_x,
double mean_y)
345template <
typename Iterator1,
typename Iterator2,
typename Projection1,
typename Projection2>
347 Iterator2 first2, Iterator2 last2,
348 Projection1 proj1, Projection2 proj2)
375template <
typename Iterator1,
typename Iterator2>
377 Iterator2 first2, Iterator2 last2)
379 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
380 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
382 if (n1 == 0 || n2 == 0) {
383 throw std::invalid_argument(
"statcpp::pearson_correlation: empty range");
386 throw std::invalid_argument(
"statcpp::pearson_correlation: ranges must have equal length");
389 throw std::invalid_argument(
"statcpp::pearson_correlation: need at least 2 elements");
396 double sum_x_sq = 0.0;
397 double sum_y_sq = 0.0;
401 for (; it1 != last1; ++it1, ++it2) {
402 double dx =
static_cast<double>(*it1) - mean_x;
403 double dy =
static_cast<double>(*it2) - mean_y;
409 if (sum_x_sq == 0.0 || sum_y_sq == 0.0) {
410 throw std::invalid_argument(
"statcpp::pearson_correlation: zero variance in one or both variables");
413 return sum_xy / std::sqrt(sum_x_sq * sum_y_sq);
431template <
typename Iterator1,
typename Iterator2>
433 Iterator2 first2, Iterator2 last2,
434 double mean_x,
double mean_y)
436 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
437 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
439 if (n1 == 0 || n2 == 0) {
440 throw std::invalid_argument(
"statcpp::pearson_correlation: empty range");
443 throw std::invalid_argument(
"statcpp::pearson_correlation: ranges must have equal length");
446 throw std::invalid_argument(
"statcpp::pearson_correlation: need at least 2 elements");
450 double sum_x_sq = 0.0;
451 double sum_y_sq = 0.0;
455 for (; it1 != last1; ++it1, ++it2) {
456 double dx =
static_cast<double>(*it1) - mean_x;
457 double dy =
static_cast<double>(*it2) - mean_y;
463 if (sum_x_sq == 0.0 || sum_y_sq == 0.0) {
464 throw std::invalid_argument(
"statcpp::pearson_correlation: zero variance in one or both variables");
467 return sum_xy / std::sqrt(sum_x_sq * sum_y_sq);
487template <
typename Iterator1,
typename Iterator2,
typename Projection1,
typename Projection2>
489 Iterator2 first2, Iterator2 last2,
490 Projection1 proj1, Projection2 proj2)
492 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
493 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
495 if (n1 == 0 || n2 == 0) {
496 throw std::invalid_argument(
"statcpp::pearson_correlation: empty range");
499 throw std::invalid_argument(
"statcpp::pearson_correlation: ranges must have equal length");
502 throw std::invalid_argument(
"statcpp::pearson_correlation: need at least 2 elements");
509 double sum_x_sq = 0.0;
510 double sum_y_sq = 0.0;
514 for (; it1 != last1; ++it1, ++it2) {
515 double dx =
static_cast<double>(std::invoke(proj1, *it1)) - mean_x;
516 double dy =
static_cast<double>(std::invoke(proj2, *it2)) - mean_y;
522 if (sum_x_sq == 0.0 || sum_y_sq == 0.0) {
523 throw std::invalid_argument(
"statcpp::pearson_correlation: zero variance in one or both variables");
526 return sum_xy / std::sqrt(sum_x_sq * sum_y_sq);
545template <
typename Iterator>
548 auto n =
static_cast<std::size_t
>(std::distance(first, last));
554 std::vector<std::pair<std::size_t, double>> indexed_values;
555 indexed_values.reserve(n);
557 for (
auto it = first; it != last; ++it, ++idx) {
558 indexed_values.emplace_back(idx,
static_cast<double>(*it));
562 std::sort(indexed_values.begin(), indexed_values.end(),
563 [](
const auto& a,
const auto& b) { return a.second < b.second; });
566 std::vector<double> ranks(n);
571 while (j < n && indexed_values[j].second == indexed_values[i].second) {
575 double avg_rank = (
static_cast<double>(i + 1) +
static_cast<double>(j)) / 2.0;
576 for (std::size_t k = i; k < j; ++k) {
577 ranks[indexed_values[k].first] = avg_rank;
595template <
typename Iterator,
typename Projection>
596std::vector<double>
compute_ranks(Iterator first, Iterator last, Projection proj)
598 auto n =
static_cast<std::size_t
>(std::distance(first, last));
603 std::vector<std::pair<std::size_t, double>> indexed_values;
604 indexed_values.reserve(n);
606 for (
auto it = first; it != last; ++it, ++idx) {
607 indexed_values.emplace_back(idx,
static_cast<double>(std::invoke(proj, *it)));
610 std::sort(indexed_values.begin(), indexed_values.end(),
611 [](
const auto& a,
const auto& b) { return a.second < b.second; });
613 std::vector<double> ranks(n);
617 while (j < n && indexed_values[j].second == indexed_values[i].second) {
620 double avg_rank = (
static_cast<double>(i + 1) +
static_cast<double>(j)) / 2.0;
621 for (std::size_t k = i; k < j; ++k) {
622 ranks[indexed_values[k].first] = avg_rank;
649template <
typename Iterator1,
typename Iterator2>
651 Iterator2 first2, Iterator2 last2)
653 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
654 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
656 if (n1 == 0 || n2 == 0) {
657 throw std::invalid_argument(
"statcpp::spearman_correlation: empty range");
660 throw std::invalid_argument(
"statcpp::spearman_correlation: ranges must have equal length");
663 throw std::invalid_argument(
"statcpp::spearman_correlation: need at least 2 elements");
670 ranks_y.begin(), ranks_y.end());
689template <
typename Iterator1,
typename Iterator2,
typename Projection1,
typename Projection2>
691 Iterator2 first2, Iterator2 last2,
692 Projection1 proj1, Projection2 proj2)
694 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
695 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
697 if (n1 == 0 || n2 == 0) {
698 throw std::invalid_argument(
"statcpp::spearman_correlation: empty range");
701 throw std::invalid_argument(
"statcpp::spearman_correlation: ranges must have equal length");
704 throw std::invalid_argument(
"statcpp::spearman_correlation: need at least 2 elements");
711 ranks_y.begin(), ranks_y.end());
734template <
typename Iterator1,
typename Iterator2>
735double kendall_tau(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
737 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
738 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
740 if (n1 == 0 || n2 == 0) {
741 throw std::invalid_argument(
"statcpp::kendall_tau: empty range");
744 throw std::invalid_argument(
"statcpp::kendall_tau: ranges must have equal length");
749 throw std::invalid_argument(
"statcpp::kendall_tau: need at least 2 elements");
753 std::vector<std::pair<double, double>> pairs;
758 for (std::size_t i = 0; i < n; ++i, ++it1, ++it2) {
759 pairs.push_back({
static_cast<double>(*it1),
static_cast<double>(*it2)});
771 std::size_t concordant = 0;
772 std::size_t discordant = 0;
773 std::size_t tie_x = 0;
774 std::size_t tie_y = 0;
775 std::size_t tie_xy = 0;
777 for (std::size_t i = 0; i < n; ++i) {
778 for (std::size_t j = i + 1; j < n; ++j) {
779 double x_diff = pairs[i].first - pairs[j].first;
780 double y_diff = pairs[i].second - pairs[j].second;
782 if (x_diff == 0.0 && y_diff == 0.0) {
784 }
else if (x_diff == 0.0) {
786 }
else if (y_diff == 0.0) {
788 }
else if ((x_diff > 0.0 && y_diff > 0.0) || (x_diff < 0.0 && y_diff < 0.0)) {
797 std::size_t n0 = n * (n - 1) / 2;
798 std::size_t tie_count_x = tie_x + tie_xy;
799 std::size_t tie_count_y = tie_y + tie_xy;
801 if (n0 == tie_count_x || n0 == tie_count_y) {
806 double numerator =
static_cast<double>(concordant) -
static_cast<double>(discordant);
807 double denominator = std::sqrt(
static_cast<double>(n0 - tie_count_x) *
static_cast<double>(n0 - tie_count_y));
809 return numerator / denominator;
828template <
typename Iterator1,
typename Iterator2,
typename Projection1,
typename Projection2>
829double kendall_tau(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2,
830 Projection1 proj1, Projection2 proj2)
832 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
833 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
835 if (n1 == 0 || n2 == 0) {
836 throw std::invalid_argument(
"statcpp::kendall_tau: empty range");
839 throw std::invalid_argument(
"statcpp::kendall_tau: ranges must have equal length");
844 throw std::invalid_argument(
"statcpp::kendall_tau: need at least 2 elements");
848 std::vector<std::pair<double, double>> pairs;
853 for (std::size_t i = 0; i < n; ++i, ++it1, ++it2) {
854 pairs.push_back({
static_cast<double>(std::invoke(proj1, *it1)),
855 static_cast<double>(std::invoke(proj2, *it2))});
864 std::size_t concordant = 0;
865 std::size_t discordant = 0;
866 std::size_t tie_x = 0;
867 std::size_t tie_y = 0;
868 std::size_t tie_xy = 0;
870 for (std::size_t i = 0; i < n; ++i) {
871 for (std::size_t j = i + 1; j < n; ++j) {
872 double x_diff = pairs[i].first - pairs[j].first;
873 double y_diff = pairs[i].second - pairs[j].second;
875 if (x_diff == 0.0 && y_diff == 0.0) {
877 }
else if (x_diff == 0.0) {
879 }
else if (y_diff == 0.0) {
881 }
else if ((x_diff > 0.0 && y_diff > 0.0) || (x_diff < 0.0 && y_diff < 0.0)) {
889 std::size_t n0 = n * (n - 1) / 2;
890 std::size_t tie_count_x = tie_x + tie_xy;
891 std::size_t tie_count_y = tie_y + tie_xy;
893 if (n0 == tie_count_x || n0 == tie_count_y) {
897 double numerator =
static_cast<double>(concordant) -
static_cast<double>(discordant);
898 double denominator = std::sqrt(
static_cast<double>(n0 - tie_count_x) *
static_cast<double>(n0 - tie_count_y));
900 return numerator / denominator;
924template <
typename Iterator1,
typename Iterator2,
typename WeightIterator>
926 Iterator2 first2, Iterator2 last2,
927 WeightIterator weight_first)
929 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
930 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
932 if (n1 == 0 || n2 == 0) {
933 throw std::invalid_argument(
"statcpp::weighted_covariance: empty range");
936 throw std::invalid_argument(
"statcpp::weighted_covariance: ranges must have the same size");
940 double sum_weights = 0.0;
945 auto weight_it = weight_first;
947 for (; it1 != last1; ++it1, ++it2, ++weight_it) {
948 double x =
static_cast<double>(*it1);
949 double y =
static_cast<double>(*it2);
950 double w =
static_cast<double>(*weight_it);
953 throw std::invalid_argument(
"statcpp::weighted_covariance: negative weight");
961 if (sum_weights == 0.0) {
962 throw std::invalid_argument(
"statcpp::weighted_covariance: sum of weights is zero");
965 double mean_x = sum_wx / sum_weights;
966 double mean_y = sum_wy / sum_weights;
969 double sum_w_sq = 0.0;
973 weight_it = weight_first;
975 for (; it1 != last1; ++it1, ++it2, ++weight_it) {
976 double x =
static_cast<double>(*it1);
977 double y =
static_cast<double>(*it2);
978 double w =
static_cast<double>(*weight_it);
980 cov += w * (x - mean_x) * (y - mean_y);
989 double denominator = sum_weights * sum_weights - sum_w_sq;
990 if (denominator <= 0.0) {
991 throw std::invalid_argument(
992 "statcpp::weighted_covariance: insufficient effective sample size for Bessel correction");
994 double correction = sum_weights / denominator;
995 return cov * correction;
1017template <
typename Iterator1,
typename Iterator2,
typename WeightIterator,
1018 typename Projection1,
typename Projection2>
1020 Iterator2 first2, Iterator2 last2,
1021 WeightIterator weight_first,
1022 Projection1 proj1, Projection2 proj2)
1024 auto n1 =
static_cast<std::size_t
>(std::distance(first1, last1));
1025 auto n2 =
static_cast<std::size_t
>(std::distance(first2, last2));
1027 if (n1 == 0 || n2 == 0) {
1028 throw std::invalid_argument(
"statcpp::weighted_covariance: empty range");
1031 throw std::invalid_argument(
"statcpp::weighted_covariance: ranges must have the same size");
1035 double sum_weights = 0.0;
1036 double sum_wx = 0.0;
1037 double sum_wy = 0.0;
1040 auto weight_it = weight_first;
1042 for (; it1 != last1; ++it1, ++it2, ++weight_it) {
1043 double x =
static_cast<double>(std::invoke(proj1, *it1));
1044 double y =
static_cast<double>(std::invoke(proj2, *it2));
1045 double w =
static_cast<double>(*weight_it);
1048 throw std::invalid_argument(
"statcpp::weighted_covariance: negative weight");
1056 if (sum_weights == 0.0) {
1057 throw std::invalid_argument(
"statcpp::weighted_covariance: sum of weights is zero");
1060 double mean_x = sum_wx / sum_weights;
1061 double mean_y = sum_wy / sum_weights;
1064 double sum_w_sq = 0.0;
1068 weight_it = weight_first;
1070 for (; it1 != last1; ++it1, ++it2, ++weight_it) {
1071 double x =
static_cast<double>(std::invoke(proj1, *it1));
1072 double y =
static_cast<double>(std::invoke(proj2, *it2));
1073 double w =
static_cast<double>(*weight_it);
1075 cov += w * (x - mean_x) * (y - mean_y);
1084 double denominator = sum_weights * sum_weights - sum_w_sq;
1085 if (denominator <= 0.0) {
1086 throw std::invalid_argument(
1087 "statcpp::weighted_covariance: insufficient effective sample size for Bessel correction");
1089 double correction = sum_weights / denominator;
1090 return cov * correction;
Basic statistical computation functions.
std::vector< double > compute_ranks(Iterator first, Iterator last)
Helper function to compute ranks.
double spearman_correlation(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Spearman's rank correlation coefficient.
double sample_covariance(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Sample covariance (unbiased covariance)
auto sum(Iterator first, Iterator last)
Sum.
double weighted_covariance(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2, WeightIterator weight_first)
Weighted covariance.
double mean(Iterator first, Iterator last)
Arithmetic mean.
double covariance(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Covariance (alias for sample_covariance)
double pearson_correlation(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Pearson correlation coefficient.
double population_covariance(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Population covariance.
double kendall_tau(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Kendall's rank correlation coefficient (tau-b)