statcpp
C++17 Header-Only Statistics Library
Loading...
Searching...
No Matches
dispersion_spread.hpp
Go to the documentation of this file.
1
18#pragma once
19
20#include <algorithm>
21#include <cmath>
22#include <cstddef>
23#include <functional>
24#include <iterator>
25#include <stdexcept>
26#include <type_traits>
27#include <vector>
28
31
32namespace statcpp {
33
34// ============================================================================
35// Range
36// ============================================================================
37
49template <typename Iterator>
50double range(Iterator first, Iterator last)
51{
52 if (first == last) {
53 throw std::invalid_argument("statcpp::range: empty range");
54 }
55 auto [min_it, max_it] = std::minmax_element(first, last);
56 return static_cast<double>(*max_it) - static_cast<double>(*min_it);
57}
58
72template <typename Iterator, typename Projection>
73double range(Iterator first, Iterator last, Projection proj)
74{
75 if (first == last) {
76 throw std::invalid_argument("statcpp::range: empty range");
77 }
78 auto it = first;
79 double min_val = static_cast<double>(std::invoke(proj, *it));
80 double max_val = min_val;
81 ++it;
82 for (; it != last; ++it) {
83 double val = static_cast<double>(std::invoke(proj, *it));
84 if (val < min_val) {
85 min_val = val;
86 }
87 if (val > max_val) {
88 max_val = val;
89 }
90 }
91 return max_val - min_val;
92}
93
94// ============================================================================
95// Variance with ddof
96// ============================================================================
97
111template <typename Iterator>
112double var(Iterator first, Iterator last, std::size_t ddof = 0)
113{
114 if (ddof > 1) {
115 throw std::invalid_argument("statcpp::var: ddof must be 0 or 1");
116 }
117 auto n = static_cast<std::size_t>(std::distance(first, last));
118 if (n == 0) {
119 throw std::invalid_argument("statcpp::var: empty range");
120 }
121 if (ddof == 1 && n < 2) {
122 throw std::invalid_argument("statcpp::var: need at least 2 elements for ddof=1");
123 }
124 double m = statcpp::mean(first, last);
125 double sum_sq = 0.0;
126 for (auto it = first; it != last; ++it) {
127 double diff = static_cast<double>(*it) - m;
128 sum_sq += diff * diff;
129 }
130 return sum_sq / static_cast<double>(n - ddof);
131}
132
146template <typename Iterator>
147double var(Iterator first, Iterator last, double precomputed_mean, std::size_t ddof)
148{
149 if (ddof > 1) {
150 throw std::invalid_argument("statcpp::var: ddof must be 0 or 1");
151 }
152 auto n = static_cast<std::size_t>(std::distance(first, last));
153 if (n == 0) {
154 throw std::invalid_argument("statcpp::var: empty range");
155 }
156 if (ddof == 1 && n < 2) {
157 throw std::invalid_argument("statcpp::var: need at least 2 elements for ddof=1");
158 }
159 double sum_sq = 0.0;
160 for (auto it = first; it != last; ++it) {
161 double diff = static_cast<double>(*it) - precomputed_mean;
162 sum_sq += diff * diff;
163 }
164 return sum_sq / static_cast<double>(n - ddof);
165}
166
181template <typename Iterator, typename Projection,
182 typename = std::enable_if_t<
183 std::is_invocable_v<Projection,
184 typename std::iterator_traits<Iterator>::value_type>>>
185double var(Iterator first, Iterator last, Projection proj, std::size_t ddof = 0)
186{
187 if (ddof > 1) {
188 throw std::invalid_argument("statcpp::var: ddof must be 0 or 1");
189 }
190 auto n = static_cast<std::size_t>(std::distance(first, last));
191 if (n == 0) {
192 throw std::invalid_argument("statcpp::var: empty range");
193 }
194 if (ddof == 1 && n < 2) {
195 throw std::invalid_argument("statcpp::var: need at least 2 elements for ddof=1");
196 }
197 double m = statcpp::mean(first, last, proj);
198 double sum_sq = 0.0;
199 for (auto it = first; it != last; ++it) {
200 double diff = static_cast<double>(std::invoke(proj, *it)) - m;
201 sum_sq += diff * diff;
202 }
203 return sum_sq / static_cast<double>(n - ddof);
204}
205
221template <typename Iterator, typename Projection>
222double var(Iterator first, Iterator last, Projection proj, double precomputed_mean, std::size_t ddof)
223{
224 if (ddof > 1) {
225 throw std::invalid_argument("statcpp::var: ddof must be 0 or 1");
226 }
227 auto n = static_cast<std::size_t>(std::distance(first, last));
228 if (n == 0) {
229 throw std::invalid_argument("statcpp::var: empty range");
230 }
231 if (ddof == 1 && n < 2) {
232 throw std::invalid_argument("statcpp::var: need at least 2 elements for ddof=1");
233 }
234 double sum_sq = 0.0;
235 for (auto it = first; it != last; ++it) {
236 double diff = static_cast<double>(std::invoke(proj, *it)) - precomputed_mean;
237 sum_sq += diff * diff;
238 }
239 return sum_sq / static_cast<double>(n - ddof);
240}
241
242// ============================================================================
243// Population Variance (alias for ddof=0)
244// ============================================================================
245
257template <typename Iterator>
258double population_variance(Iterator first, Iterator last)
259{
260 return var(first, last, 0);
261}
262
275template <typename Iterator>
276double population_variance(Iterator first, Iterator last, double precomputed_mean)
277{
278 return var(first, last, precomputed_mean, 0);
279}
280
294template <typename Iterator, typename Projection,
295 typename = std::enable_if_t<
296 std::is_invocable_v<Projection,
297 typename std::iterator_traits<Iterator>::value_type>>>
298double population_variance(Iterator first, Iterator last, Projection proj)
299{
300 return var(first, last, proj, 0);
301}
302
317template <typename Iterator, typename Projection>
318double population_variance(Iterator first, Iterator last, Projection proj, double precomputed_mean)
319{
320 return var(first, last, proj, precomputed_mean, 0);
321}
322
323// ============================================================================
324// Sample Variance (alias for ddof=1)
325// ============================================================================
326
338template <typename Iterator>
339double sample_variance(Iterator first, Iterator last)
340{
341 return var(first, last, 1);
342}
343
356template <typename Iterator>
357double sample_variance(Iterator first, Iterator last, double precomputed_mean)
358{
359 return var(first, last, precomputed_mean, 1);
360}
361
375template <typename Iterator, typename Projection,
376 typename = std::enable_if_t<
377 std::is_invocable_v<Projection,
378 typename std::iterator_traits<Iterator>::value_type>>>
379double sample_variance(Iterator first, Iterator last, Projection proj)
380{
381 return var(first, last, proj, 1);
382}
383
398template <typename Iterator, typename Projection>
399double sample_variance(Iterator first, Iterator last, Projection proj, double precomputed_mean)
400{
401 return var(first, last, proj, precomputed_mean, 1);
402}
403
404// ============================================================================
405// Variance (= Sample Variance)
406// ============================================================================
407
419template <typename Iterator>
420double variance(Iterator first, Iterator last)
421{
422 return sample_variance(first, last);
423}
424
437template <typename Iterator>
438double variance(Iterator first, Iterator last, double precomputed_mean)
439{
440 return sample_variance(first, last, precomputed_mean);
441}
442
456template <typename Iterator, typename Projection,
457 typename = std::enable_if_t<
458 std::is_invocable_v<Projection,
459 typename std::iterator_traits<Iterator>::value_type>>>
460double variance(Iterator first, Iterator last, Projection proj)
461{
462 return sample_variance(first, last, proj);
463}
464
479template <typename Iterator, typename Projection>
480double variance(Iterator first, Iterator last, Projection proj, double precomputed_mean)
481{
482 return sample_variance(first, last, proj, precomputed_mean);
483}
484
485// ============================================================================
486// Standard Deviation with ddof (NumPy-style)
487// ============================================================================
488
503template <typename Iterator>
504double stdev(Iterator first, Iterator last, std::size_t ddof = 0)
505{
506 return std::sqrt(var(first, last, ddof));
507}
508
522template <typename Iterator>
523double stdev(Iterator first, Iterator last, double precomputed_mean, std::size_t ddof)
524{
525 return std::sqrt(var(first, last, precomputed_mean, ddof));
526}
527
542template <typename Iterator, typename Projection,
543 typename = std::enable_if_t<
544 std::is_invocable_v<Projection,
545 typename std::iterator_traits<Iterator>::value_type>>>
546double stdev(Iterator first, Iterator last, Projection proj, std::size_t ddof = 0)
547{
548 return std::sqrt(var(first, last, proj, ddof));
549}
550
566template <typename Iterator, typename Projection>
567double stdev(Iterator first, Iterator last, Projection proj, double precomputed_mean, std::size_t ddof)
568{
569 return std::sqrt(var(first, last, proj, precomputed_mean, ddof));
570}
571
572// ============================================================================
573// Population Standard Deviation (alias for ddof=0)
574// ============================================================================
575
587template <typename Iterator>
588double population_stddev(Iterator first, Iterator last)
589{
590 return stdev(first, last, 0);
591}
592
605template <typename Iterator>
606double population_stddev(Iterator first, Iterator last, double precomputed_mean)
607{
608 return stdev(first, last, precomputed_mean, 0);
609}
610
624template <typename Iterator, typename Projection,
625 typename = std::enable_if_t<
626 std::is_invocable_v<Projection,
627 typename std::iterator_traits<Iterator>::value_type>>>
628double population_stddev(Iterator first, Iterator last, Projection proj)
629{
630 return stdev(first, last, proj, 0);
631}
632
647template <typename Iterator, typename Projection>
648double population_stddev(Iterator first, Iterator last, Projection proj, double precomputed_mean)
649{
650 return stdev(first, last, proj, precomputed_mean, 0);
651}
652
653// ============================================================================
654// Sample Standard Deviation (alias for ddof=1)
655// ============================================================================
656
668template <typename Iterator>
669double sample_stddev(Iterator first, Iterator last)
670{
671 return stdev(first, last, 1);
672}
673
686template <typename Iterator>
687double sample_stddev(Iterator first, Iterator last, double precomputed_mean)
688{
689 return stdev(first, last, precomputed_mean, 1);
690}
691
705template <typename Iterator, typename Projection,
706 typename = std::enable_if_t<
707 std::is_invocable_v<Projection,
708 typename std::iterator_traits<Iterator>::value_type>>>
709double sample_stddev(Iterator first, Iterator last, Projection proj)
710{
711 return stdev(first, last, proj, 1);
712}
713
728template <typename Iterator, typename Projection>
729double sample_stddev(Iterator first, Iterator last, Projection proj, double precomputed_mean)
730{
731 return stdev(first, last, proj, precomputed_mean, 1);
732}
733
734// ============================================================================
735// Standard Deviation (= Sample Standard Deviation)
736// ============================================================================
737
749template <typename Iterator>
750double stddev(Iterator first, Iterator last)
751{
752 return sample_stddev(first, last);
753}
754
767template <typename Iterator>
768double stddev(Iterator first, Iterator last, double precomputed_mean)
769{
770 return sample_stddev(first, last, precomputed_mean);
771}
772
786template <typename Iterator, typename Projection,
787 typename = std::enable_if_t<
788 std::is_invocable_v<Projection,
789 typename std::iterator_traits<Iterator>::value_type>>>
790double stddev(Iterator first, Iterator last, Projection proj)
791{
792 return sample_stddev(first, last, proj);
793}
794
809template <typename Iterator, typename Projection>
810double stddev(Iterator first, Iterator last, Projection proj, double precomputed_mean)
811{
812 return sample_stddev(first, last, proj, precomputed_mean);
813}
814
815// ============================================================================
816// Coefficient of Variation
817// ============================================================================
818
831template <typename Iterator>
832double coefficient_of_variation(Iterator first, Iterator last)
833{
834 auto n = static_cast<std::size_t>(std::distance(first, last));
835 if (n == 0) {
836 throw std::invalid_argument("statcpp::coefficient_of_variation: empty range");
837 }
838 if (n < 2) {
839 throw std::invalid_argument("statcpp::coefficient_of_variation: need at least 2 elements");
840 }
841 double m = statcpp::mean(first, last);
842 if (m == 0.0) {
843 throw std::invalid_argument("statcpp::coefficient_of_variation: mean is zero");
844 }
845 return sample_stddev(first, last, m) / std::abs(m);
846}
847
860template <typename Iterator>
861double coefficient_of_variation(Iterator first, Iterator last, double precomputed_mean)
862{
863 auto n = static_cast<std::size_t>(std::distance(first, last));
864 if (n == 0) {
865 throw std::invalid_argument("statcpp::coefficient_of_variation: empty range");
866 }
867 if (n < 2) {
868 throw std::invalid_argument("statcpp::coefficient_of_variation: need at least 2 elements");
869 }
870 if (precomputed_mean == 0.0) {
871 throw std::invalid_argument("statcpp::coefficient_of_variation: mean is zero");
872 }
873 return sample_stddev(first, last, precomputed_mean) / std::abs(precomputed_mean);
874}
875
889template <typename Iterator, typename Projection,
890 typename = std::enable_if_t<
891 std::is_invocable_v<Projection,
892 typename std::iterator_traits<Iterator>::value_type>>>
893double coefficient_of_variation(Iterator first, Iterator last, Projection proj)
894{
895 auto n = static_cast<std::size_t>(std::distance(first, last));
896 if (n == 0) {
897 throw std::invalid_argument("statcpp::coefficient_of_variation: empty range");
898 }
899 if (n < 2) {
900 throw std::invalid_argument("statcpp::coefficient_of_variation: need at least 2 elements");
901 }
902 double m = statcpp::mean(first, last, proj);
903 if (m == 0.0) {
904 throw std::invalid_argument("statcpp::coefficient_of_variation: mean is zero");
905 }
906 return sample_stddev(first, last, proj, m) / std::abs(m);
907}
908
923template <typename Iterator, typename Projection>
924double coefficient_of_variation(Iterator first, Iterator last, Projection proj, double precomputed_mean)
925{
926 auto n = static_cast<std::size_t>(std::distance(first, last));
927 if (n == 0) {
928 throw std::invalid_argument("statcpp::coefficient_of_variation: empty range");
929 }
930 if (n < 2) {
931 throw std::invalid_argument("statcpp::coefficient_of_variation: need at least 2 elements");
932 }
933 if (precomputed_mean == 0.0) {
934 throw std::invalid_argument("statcpp::coefficient_of_variation: mean is zero");
935 }
936 return sample_stddev(first, last, proj, precomputed_mean) / std::abs(precomputed_mean);
937}
938
939// ============================================================================
940// Interquartile Range (IQR)
941// ============================================================================
942
955template <typename Iterator>
956double iqr(Iterator first, Iterator last)
957{
958 auto n = static_cast<std::size_t>(std::distance(first, last));
959 if (n == 0) {
960 throw std::invalid_argument("statcpp::iqr: empty range");
961 }
962 double q1 = statcpp::interpolate_at(first, n, 0.25);
963 double q3 = statcpp::interpolate_at(first, n, 0.75);
964 return q3 - q1;
965}
966
981template <typename Iterator, typename Projection>
982double iqr(Iterator first, Iterator last, Projection proj)
983{
984 auto n = static_cast<std::size_t>(std::distance(first, last));
985 if (n == 0) {
986 throw std::invalid_argument("statcpp::iqr: empty range");
987 }
988 double q1 = statcpp::interpolate_at(first, n, 0.25, proj);
989 double q3 = statcpp::interpolate_at(first, n, 0.75, proj);
990 return q3 - q1;
991}
992
993// ============================================================================
994// Mean Absolute Deviation
995// ============================================================================
996
1009template <typename Iterator>
1010double mean_absolute_deviation(Iterator first, Iterator last)
1011{
1012 auto n = static_cast<std::size_t>(std::distance(first, last));
1013 if (n == 0) {
1014 throw std::invalid_argument("statcpp::mean_absolute_deviation: empty range");
1015 }
1016 double m = statcpp::mean(first, last);
1017 double sum_abs = 0.0;
1018 for (auto it = first; it != last; ++it) {
1019 sum_abs += std::abs(static_cast<double>(*it) - m);
1020 }
1021 return sum_abs / static_cast<double>(n);
1022}
1023
1036template <typename Iterator>
1037double mean_absolute_deviation(Iterator first, Iterator last, double precomputed_mean)
1038{
1039 auto n = static_cast<std::size_t>(std::distance(first, last));
1040 if (n == 0) {
1041 throw std::invalid_argument("statcpp::mean_absolute_deviation: empty range");
1042 }
1043 double sum_abs = 0.0;
1044 for (auto it = first; it != last; ++it) {
1045 sum_abs += std::abs(static_cast<double>(*it) - precomputed_mean);
1046 }
1047 return sum_abs / static_cast<double>(n);
1048}
1049
1063template <typename Iterator, typename Projection,
1064 typename = std::enable_if_t<
1065 std::is_invocable_v<Projection,
1066 typename std::iterator_traits<Iterator>::value_type>>>
1067double mean_absolute_deviation(Iterator first, Iterator last, Projection proj)
1068{
1069 auto n = static_cast<std::size_t>(std::distance(first, last));
1070 if (n == 0) {
1071 throw std::invalid_argument("statcpp::mean_absolute_deviation: empty range");
1072 }
1073 double m = statcpp::mean(first, last, proj);
1074 double sum_abs = 0.0;
1075 for (auto it = first; it != last; ++it) {
1076 sum_abs += std::abs(static_cast<double>(std::invoke(proj, *it)) - m);
1077 }
1078 return sum_abs / static_cast<double>(n);
1079}
1080
1095template <typename Iterator, typename Projection>
1096double mean_absolute_deviation(Iterator first, Iterator last, Projection proj, double precomputed_mean)
1097{
1098 auto n = static_cast<std::size_t>(std::distance(first, last));
1099 if (n == 0) {
1100 throw std::invalid_argument("statcpp::mean_absolute_deviation: empty range");
1101 }
1102 double sum_abs = 0.0;
1103 for (auto it = first; it != last; ++it) {
1104 sum_abs += std::abs(static_cast<double>(std::invoke(proj, *it)) - precomputed_mean);
1105 }
1106 return sum_abs / static_cast<double>(n);
1107}
1108
1109// ============================================================================
1110// Weighted Variance
1111// ============================================================================
1112
1128template <typename Iterator, typename WeightIterator>
1129double weighted_variance(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last)
1130{
1131 if (std::distance(first, last) != std::distance(weight_first, weight_last)) {
1132 throw std::invalid_argument("statcpp::weighted_variance: data and weight ranges differ in length");
1133 }
1134
1135 auto n = statcpp::count(first, last);
1136 if (n == 0) {
1137 throw std::invalid_argument("statcpp::weighted_variance: empty range");
1138 }
1139
1140 // Compute weighted mean
1141 double sum_weighted = 0.0;
1142 double sum_weights = 0.0;
1143 auto weight_it = weight_first;
1144
1145 for (auto it = first; it != last; ++it, ++weight_it) {
1146 double value = static_cast<double>(*it);
1147 double weight = static_cast<double>(*weight_it);
1148
1149 if (weight < 0.0) {
1150 throw std::invalid_argument("statcpp::weighted_variance: negative weight");
1151 }
1152
1153 sum_weighted += value * weight;
1154 sum_weights += weight;
1155 }
1156
1157 if (sum_weights == 0.0) {
1158 throw std::invalid_argument("statcpp::weighted_variance: sum of weights is zero");
1159 }
1160
1161 double mean = sum_weighted / sum_weights;
1162
1163 // Compute weighted variance (with Bessel's correction)
1164 double sum_squared_dev = 0.0;
1165 double sum_weights_squared = 0.0;
1166 weight_it = weight_first;
1167
1168 for (auto it = first; it != last; ++it, ++weight_it) {
1169 double value = static_cast<double>(*it);
1170 double weight = static_cast<double>(*weight_it);
1171 double dev = value - mean;
1172
1173 sum_squared_dev += weight * dev * dev;
1174 sum_weights_squared += weight * weight;
1175 }
1176
1177 // Bessel's correction: V = Σw(x-μ)² / ((Σw)² - Σ(w²)) * Σw
1178 // Simplified: V = Σw(x-μ)² / (Σw - Σw²/Σw)
1179 double correction = sum_weights - (sum_weights_squared / sum_weights);
1180
1181 if (correction <= 0.0) {
1182 throw std::invalid_argument("statcpp::weighted_variance: insufficient effective sample size");
1183 }
1184
1185 return sum_squared_dev / correction;
1186}
1187
1204template <typename Iterator, typename WeightIterator>
1205[[deprecated("Use weighted_variance(first, last, weight_first, weight_last) overload for range safety")]]
1206double weighted_variance(Iterator first, Iterator last, WeightIterator weight_first)
1207{
1208 auto n = statcpp::count(first, last);
1209 if (n == 0) {
1210 throw std::invalid_argument("statcpp::weighted_variance: empty range");
1211 }
1212
1213 // Compute weighted mean
1214 double sum_weighted = 0.0;
1215 double sum_weights = 0.0;
1216 auto weight_it = weight_first;
1217
1218 for (auto it = first; it != last; ++it, ++weight_it) {
1219 double value = static_cast<double>(*it);
1220 double weight = static_cast<double>(*weight_it);
1221
1222 if (weight < 0.0) {
1223 throw std::invalid_argument("statcpp::weighted_variance: negative weight");
1224 }
1225
1226 sum_weighted += value * weight;
1227 sum_weights += weight;
1228 }
1229
1230 if (sum_weights == 0.0) {
1231 throw std::invalid_argument("statcpp::weighted_variance: sum of weights is zero");
1232 }
1233
1234 double mean = sum_weighted / sum_weights;
1235
1236 // Compute weighted variance (with Bessel's correction)
1237 double sum_squared_dev = 0.0;
1238 double sum_weights_squared = 0.0;
1239 weight_it = weight_first;
1240
1241 for (auto it = first; it != last; ++it, ++weight_it) {
1242 double value = static_cast<double>(*it);
1243 double weight = static_cast<double>(*weight_it);
1244 double dev = value - mean;
1245
1246 sum_squared_dev += weight * dev * dev;
1247 sum_weights_squared += weight * weight;
1248 }
1249
1250 // Bessel's correction: V = Σw(x-μ)² / ((Σw)² - Σ(w²)) * Σw
1251 // Simplified: V = Σw(x-μ)² / (Σw - Σw²/Σw)
1252 double correction = sum_weights - (sum_weights_squared / sum_weights);
1253
1254 if (correction <= 0.0) {
1255 throw std::invalid_argument("statcpp::weighted_variance: insufficient effective sample size");
1256 }
1257
1258 return sum_squared_dev / correction;
1259}
1260
1278template <typename Iterator, typename WeightIterator, typename Projection>
1279double weighted_variance(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last, Projection proj)
1280{
1281 if (std::distance(first, last) != std::distance(weight_first, weight_last)) {
1282 throw std::invalid_argument("statcpp::weighted_variance: data and weight ranges differ in length");
1283 }
1284
1285 auto n = statcpp::count(first, last);
1286 if (n == 0) {
1287 throw std::invalid_argument("statcpp::weighted_variance: empty range");
1288 }
1289
1290 // Compute weighted mean
1291 double sum_weighted = 0.0;
1292 double sum_weights = 0.0;
1293 auto weight_it = weight_first;
1294
1295 for (auto it = first; it != last; ++it, ++weight_it) {
1296 double value = static_cast<double>(std::invoke(proj, *it));
1297 double weight = static_cast<double>(*weight_it);
1298
1299 if (weight < 0.0) {
1300 throw std::invalid_argument("statcpp::weighted_variance: negative weight");
1301 }
1302
1303 sum_weighted += value * weight;
1304 sum_weights += weight;
1305 }
1306
1307 if (sum_weights == 0.0) {
1308 throw std::invalid_argument("statcpp::weighted_variance: sum of weights is zero");
1309 }
1310
1311 double mean = sum_weighted / sum_weights;
1312
1313 // Compute weighted variance
1314 double sum_squared_dev = 0.0;
1315 double sum_weights_squared = 0.0;
1316 weight_it = weight_first;
1317
1318 for (auto it = first; it != last; ++it, ++weight_it) {
1319 double value = static_cast<double>(std::invoke(proj, *it));
1320 double weight = static_cast<double>(*weight_it);
1321 double dev = value - mean;
1322
1323 sum_squared_dev += weight * dev * dev;
1324 sum_weights_squared += weight * weight;
1325 }
1326
1327 double correction = sum_weights - (sum_weights_squared / sum_weights);
1328
1329 if (correction <= 0.0) {
1330 throw std::invalid_argument("statcpp::weighted_variance: insufficient effective sample size");
1331 }
1332
1333 return sum_squared_dev / correction;
1334}
1335
1354template <typename Iterator, typename WeightIterator, typename Projection>
1355[[deprecated("Use weighted_variance(first, last, weight_first, weight_last, proj) overload for range safety")]]
1356double weighted_variance(Iterator first, Iterator last, WeightIterator weight_first, Projection proj)
1357{
1358 auto n = statcpp::count(first, last);
1359 if (n == 0) {
1360 throw std::invalid_argument("statcpp::weighted_variance: empty range");
1361 }
1362
1363 // Compute weighted mean
1364 double sum_weighted = 0.0;
1365 double sum_weights = 0.0;
1366 auto weight_it = weight_first;
1367
1368 for (auto it = first; it != last; ++it, ++weight_it) {
1369 double value = static_cast<double>(std::invoke(proj, *it));
1370 double weight = static_cast<double>(*weight_it);
1371
1372 if (weight < 0.0) {
1373 throw std::invalid_argument("statcpp::weighted_variance: negative weight");
1374 }
1375
1376 sum_weighted += value * weight;
1377 sum_weights += weight;
1378 }
1379
1380 if (sum_weights == 0.0) {
1381 throw std::invalid_argument("statcpp::weighted_variance: sum of weights is zero");
1382 }
1383
1384 double mean = sum_weighted / sum_weights;
1385
1386 // Compute weighted variance
1387 double sum_squared_dev = 0.0;
1388 double sum_weights_squared = 0.0;
1389 weight_it = weight_first;
1390
1391 for (auto it = first; it != last; ++it, ++weight_it) {
1392 double value = static_cast<double>(std::invoke(proj, *it));
1393 double weight = static_cast<double>(*weight_it);
1394 double dev = value - mean;
1395
1396 sum_squared_dev += weight * dev * dev;
1397 sum_weights_squared += weight * weight;
1398 }
1399
1400 double correction = sum_weights - (sum_weights_squared / sum_weights);
1401
1402 if (correction <= 0.0) {
1403 throw std::invalid_argument("statcpp::weighted_variance: insufficient effective sample size");
1404 }
1405
1406 return sum_squared_dev / correction;
1407}
1408
1423template <typename Iterator, typename WeightIterator>
1424double weighted_stddev(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last)
1425{
1426 return std::sqrt(weighted_variance(first, last, weight_first, weight_last));
1427}
1428
1444template <typename Iterator, typename WeightIterator>
1445[[deprecated("Use weighted_stddev(first, last, weight_first, weight_last) overload for range safety")]]
1446double weighted_stddev(Iterator first, Iterator last, WeightIterator weight_first)
1447{
1448 return std::sqrt(weighted_variance(first, last, weight_first));
1449}
1450
1467template <typename Iterator, typename WeightIterator, typename Projection>
1468double weighted_stddev(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last, Projection proj)
1469{
1470 return std::sqrt(weighted_variance(first, last, weight_first, weight_last, proj));
1471}
1472
1490template <typename Iterator, typename WeightIterator, typename Projection>
1491[[deprecated("Use weighted_stddev(first, last, weight_first, weight_last, proj) overload for range safety")]]
1492double weighted_stddev(Iterator first, Iterator last, WeightIterator weight_first, Projection proj)
1493{
1494 return std::sqrt(weighted_variance(first, last, weight_first, proj));
1495}
1496
1497// ============================================================================
1498// Geometric Standard Deviation
1499// ============================================================================
1500
1514template <typename Iterator>
1515double geometric_stddev(Iterator first, Iterator last)
1516{
1517 auto n = statcpp::count(first, last);
1518 if (n == 0) {
1519 throw std::invalid_argument("statcpp::geometric_stddev: empty range");
1520 }
1521
1522 // Compute standard deviation of log-transformed data
1523 std::vector<double> log_values;
1524 log_values.reserve(n);
1525
1526 for (auto it = first; it != last; ++it) {
1527 double value = static_cast<double>(*it);
1528 if (value <= 0.0) {
1529 throw std::invalid_argument("statcpp::geometric_stddev: all values must be positive");
1530 }
1531 log_values.push_back(std::log(value));
1532 }
1533
1534 double log_stddev = statcpp::sample_stddev(log_values.begin(), log_values.end());
1535 return std::exp(log_stddev);
1536}
1537
1551template <typename Iterator, typename Projection>
1552double geometric_stddev(Iterator first, Iterator last, Projection proj)
1553{
1554 auto n = statcpp::count(first, last);
1555 if (n == 0) {
1556 throw std::invalid_argument("statcpp::geometric_stddev: empty range");
1557 }
1558
1559 // Compute standard deviation of log-transformed data
1560 std::vector<double> log_values;
1561 log_values.reserve(n);
1562
1563 for (auto it = first; it != last; ++it) {
1564 double value = static_cast<double>(std::invoke(proj, *it));
1565 if (value <= 0.0) {
1566 throw std::invalid_argument("statcpp::geometric_stddev: all values must be positive");
1567 }
1568 log_values.push_back(std::log(value));
1569 }
1570
1571 double log_stddev = statcpp::sample_stddev(log_values.begin(), log_values.end());
1572 return std::exp(log_stddev);
1573}
1574
1575} // namespace statcpp
Basic statistical computation functions.
double stddev(Iterator first, Iterator last)
Standard deviation (alias for sample_stddev)
double population_variance(Iterator first, Iterator last)
Population variance.
double range(Iterator first, Iterator last)
Range (maximum - minimum)
double stdev(Iterator first, Iterator last, std::size_t ddof=0)
Standard deviation (ddof = Delta Degrees of Freedom)
double sample_stddev(Iterator first, Iterator last)
Sample standard deviation.
double sample_variance(Iterator first, Iterator last)
Sample variance (unbiased variance)
double coefficient_of_variation(Iterator first, Iterator last)
Coefficient of variation.
double var(Iterator first, Iterator last, std::size_t ddof=0)
Variance (ddof = Delta Degrees of Freedom)
double interpolate_at(Iterator first, std::size_t n, double p)
Linear interpolation at position.
double geometric_stddev(Iterator first, Iterator last)
Geometric standard deviation.
double population_stddev(Iterator first, Iterator last)
Population standard deviation.
double iqr(Iterator first, Iterator last)
Interquartile range (accepts a sorted range)
double mean_absolute_deviation(Iterator first, Iterator last)
Mean absolute deviation.
double mean(Iterator first, Iterator last)
Arithmetic mean.
std::vector< double > diff(Iterator first, Iterator last, std::size_t order=1)
Difference series (first-order or d-th order differencing)
double variance(Iterator first, Iterator last)
Variance (alias for sample_variance)
double weighted_variance(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last)
Weighted variance (reliability weights / analytic weights)
std::size_t count(Iterator first, Iterator last)
Data count.
double weighted_stddev(Iterator first, Iterator last, WeightIterator weight_first, WeightIterator weight_last)
Weighted standard deviation.
Order statistics implementation.