statcpp
C++17 Header-Only Statistics Library
Loading...
Searching...
No Matches
shape_of_distribution.hpp
Go to the documentation of this file.
1
9#pragma once
10
11#include <cmath>
12#include <cstddef>
13#include <functional>
14#include <iterator>
15#include <stdexcept>
16#include <type_traits>
17
19
20namespace statcpp {
21
22// ============================================================================
23// Skewness
24// ============================================================================
25
41template <typename Iterator>
42double population_skewness(Iterator first, Iterator last)
43{
44 auto n = static_cast<std::size_t>(std::distance(first, last));
45 if (n == 0) {
46 throw std::invalid_argument("statcpp::population_skewness: empty range");
47 }
48
49 double m = statcpp::mean(first, last);
50 double sum_cubed = 0.0;
51 double sum_sq = 0.0;
52
53 for (auto it = first; it != last; ++it) {
54 double diff = static_cast<double>(*it) - m;
55 sum_sq += diff * diff;
56 sum_cubed += diff * diff * diff;
57 }
58
59 double var = sum_sq / static_cast<double>(n);
60 if (var == 0.0) {
61 throw std::invalid_argument("statcpp::population_skewness: zero variance");
62 }
63
64 double stddev = std::sqrt(var);
65 return (sum_cubed / static_cast<double>(n)) / (stddev * stddev * stddev);
66}
67
80template <typename Iterator>
81double population_skewness(Iterator first, Iterator last, double precomputed_mean)
82{
83 auto n = static_cast<std::size_t>(std::distance(first, last));
84 if (n == 0) {
85 throw std::invalid_argument("statcpp::population_skewness: empty range");
86 }
87
88 double sum_cubed = 0.0;
89 double sum_sq = 0.0;
90
91 for (auto it = first; it != last; ++it) {
92 double diff = static_cast<double>(*it) - precomputed_mean;
93 sum_sq += diff * diff;
94 sum_cubed += diff * diff * diff;
95 }
96
97 double var = sum_sq / static_cast<double>(n);
98 if (var == 0.0) {
99 throw std::invalid_argument("statcpp::population_skewness: zero variance");
100 }
101
102 double stddev = std::sqrt(var);
103 return (sum_cubed / static_cast<double>(n)) / (stddev * stddev * stddev);
104}
105
119template <typename Iterator, typename Projection,
120 typename = std::enable_if_t<
121 std::is_invocable_v<Projection,
122 typename std::iterator_traits<Iterator>::value_type>>>
123double population_skewness(Iterator first, Iterator last, Projection proj)
124{
125 auto n = static_cast<std::size_t>(std::distance(first, last));
126 if (n == 0) {
127 throw std::invalid_argument("statcpp::population_skewness: empty range");
128 }
129
130 double m = statcpp::mean(first, last, proj);
131 double sum_cubed = 0.0;
132 double sum_sq = 0.0;
133
134 for (auto it = first; it != last; ++it) {
135 double diff = static_cast<double>(std::invoke(proj, *it)) - m;
136 sum_sq += diff * diff;
137 sum_cubed += diff * diff * diff;
138 }
139
140 double var = sum_sq / static_cast<double>(n);
141 if (var == 0.0) {
142 throw std::invalid_argument("statcpp::population_skewness: zero variance");
143 }
144
145 double stddev = std::sqrt(var);
146 return (sum_cubed / static_cast<double>(n)) / (stddev * stddev * stddev);
147}
148
161template <typename Iterator, typename Projection>
162double population_skewness(Iterator first, Iterator last, Projection proj, double precomputed_mean)
163{
164 auto n = static_cast<std::size_t>(std::distance(first, last));
165 if (n == 0) {
166 throw std::invalid_argument("statcpp::population_skewness: empty range");
167 }
168
169 double sum_cubed = 0.0;
170 double sum_sq = 0.0;
171
172 for (auto it = first; it != last; ++it) {
173 double diff = static_cast<double>(std::invoke(proj, *it)) - precomputed_mean;
174 sum_sq += diff * diff;
175 sum_cubed += diff * diff * diff;
176 }
177
178 double var = sum_sq / static_cast<double>(n);
179 if (var == 0.0) {
180 throw std::invalid_argument("statcpp::population_skewness: zero variance");
181 }
182
183 double stddev = std::sqrt(var);
184 return (sum_cubed / static_cast<double>(n)) / (stddev * stddev * stddev);
185}
186
201template <typename Iterator>
202double sample_skewness(Iterator first, Iterator last)
203{
204 auto n = static_cast<std::size_t>(std::distance(first, last));
205 if (n < 3) {
206 throw std::invalid_argument("statcpp::sample_skewness: need at least 3 elements");
207 }
208
209 double g1 = population_skewness(first, last);
210 double correction = std::sqrt(static_cast<double>(n * (n - 1))) / static_cast<double>(n - 2);
211 return correction * g1;
212}
213
224template <typename Iterator>
225double sample_skewness(Iterator first, Iterator last, double precomputed_mean)
226{
227 auto n = static_cast<std::size_t>(std::distance(first, last));
228 if (n < 3) {
229 throw std::invalid_argument("statcpp::sample_skewness: need at least 3 elements");
230 }
231
232 double g1 = population_skewness(first, last, precomputed_mean);
233 double correction = std::sqrt(static_cast<double>(n * (n - 1))) / static_cast<double>(n - 2);
234 return correction * g1;
235}
236
248template <typename Iterator, typename Projection,
249 typename = std::enable_if_t<
250 std::is_invocable_v<Projection,
251 typename std::iterator_traits<Iterator>::value_type>>>
252double sample_skewness(Iterator first, Iterator last, Projection proj)
253{
254 auto n = static_cast<std::size_t>(std::distance(first, last));
255 if (n < 3) {
256 throw std::invalid_argument("statcpp::sample_skewness: need at least 3 elements");
257 }
258
259 double g1 = population_skewness(first, last, proj);
260 double correction = std::sqrt(static_cast<double>(n * (n - 1))) / static_cast<double>(n - 2);
261 return correction * g1;
262}
263
276template <typename Iterator, typename Projection>
277double sample_skewness(Iterator first, Iterator last, Projection proj, double precomputed_mean)
278{
279 auto n = static_cast<std::size_t>(std::distance(first, last));
280 if (n < 3) {
281 throw std::invalid_argument("statcpp::sample_skewness: need at least 3 elements");
282 }
283
284 double g1 = population_skewness(first, last, proj, precomputed_mean);
285 double correction = std::sqrt(static_cast<double>(n * (n - 1))) / static_cast<double>(n - 2);
286 return correction * g1;
287}
288
297template <typename Iterator>
298double skewness(Iterator first, Iterator last)
299{
300 return sample_skewness(first, last);
301}
302
312template <typename Iterator>
313double skewness(Iterator first, Iterator last, double precomputed_mean)
314{
315 return sample_skewness(first, last, precomputed_mean);
316}
317
328template <typename Iterator, typename Projection,
329 typename = std::enable_if_t<
330 std::is_invocable_v<Projection,
331 typename std::iterator_traits<Iterator>::value_type>>>
332double skewness(Iterator first, Iterator last, Projection proj)
333{
334 return sample_skewness(first, last, proj);
335}
336
348template <typename Iterator, typename Projection>
349double skewness(Iterator first, Iterator last, Projection proj, double precomputed_mean)
350{
351 return sample_skewness(first, last, proj, precomputed_mean);
352}
353
354// ============================================================================
355// Kurtosis
356// ============================================================================
357
374template <typename Iterator>
375double population_kurtosis(Iterator first, Iterator last)
376{
377 auto n = static_cast<std::size_t>(std::distance(first, last));
378 if (n == 0) {
379 throw std::invalid_argument("statcpp::population_kurtosis: empty range");
380 }
381
382 double m = statcpp::mean(first, last);
383 double sum_fourth = 0.0;
384 double sum_sq = 0.0;
385
386 for (auto it = first; it != last; ++it) {
387 double diff = static_cast<double>(*it) - m;
388 double diff_sq = diff * diff;
389 sum_sq += diff_sq;
390 sum_fourth += diff_sq * diff_sq;
391 }
392
393 double var = sum_sq / static_cast<double>(n);
394 if (var == 0.0) {
395 throw std::invalid_argument("statcpp::population_kurtosis: zero variance");
396 }
397
398 double fourth_moment = sum_fourth / static_cast<double>(n);
399 return (fourth_moment / (var * var)) - 3.0;
400}
401
412template <typename Iterator>
413double population_kurtosis(Iterator first, Iterator last, double precomputed_mean)
414{
415 auto n = static_cast<std::size_t>(std::distance(first, last));
416 if (n == 0) {
417 throw std::invalid_argument("statcpp::population_kurtosis: empty range");
418 }
419
420 double sum_fourth = 0.0;
421 double sum_sq = 0.0;
422
423 for (auto it = first; it != last; ++it) {
424 double diff = static_cast<double>(*it) - precomputed_mean;
425 double diff_sq = diff * diff;
426 sum_sq += diff_sq;
427 sum_fourth += diff_sq * diff_sq;
428 }
429
430 double var = sum_sq / static_cast<double>(n);
431 if (var == 0.0) {
432 throw std::invalid_argument("statcpp::population_kurtosis: zero variance");
433 }
434
435 double fourth_moment = sum_fourth / static_cast<double>(n);
436 return (fourth_moment / (var * var)) - 3.0;
437}
438
450template <typename Iterator, typename Projection,
451 typename = std::enable_if_t<
452 std::is_invocable_v<Projection,
453 typename std::iterator_traits<Iterator>::value_type>>>
454double population_kurtosis(Iterator first, Iterator last, Projection proj)
455{
456 auto n = static_cast<std::size_t>(std::distance(first, last));
457 if (n == 0) {
458 throw std::invalid_argument("statcpp::population_kurtosis: empty range");
459 }
460
461 double m = statcpp::mean(first, last, proj);
462 double sum_fourth = 0.0;
463 double sum_sq = 0.0;
464
465 for (auto it = first; it != last; ++it) {
466 double diff = static_cast<double>(std::invoke(proj, *it)) - m;
467 double diff_sq = diff * diff;
468 sum_sq += diff_sq;
469 sum_fourth += diff_sq * diff_sq;
470 }
471
472 double var = sum_sq / static_cast<double>(n);
473 if (var == 0.0) {
474 throw std::invalid_argument("statcpp::population_kurtosis: zero variance");
475 }
476
477 double fourth_moment = sum_fourth / static_cast<double>(n);
478 return (fourth_moment / (var * var)) - 3.0;
479}
480
493template <typename Iterator, typename Projection>
494double population_kurtosis(Iterator first, Iterator last, Projection proj, double precomputed_mean)
495{
496 auto n = static_cast<std::size_t>(std::distance(first, last));
497 if (n == 0) {
498 throw std::invalid_argument("statcpp::population_kurtosis: empty range");
499 }
500
501 double sum_fourth = 0.0;
502 double sum_sq = 0.0;
503
504 for (auto it = first; it != last; ++it) {
505 double diff = static_cast<double>(std::invoke(proj, *it)) - precomputed_mean;
506 double diff_sq = diff * diff;
507 sum_sq += diff_sq;
508 sum_fourth += diff_sq * diff_sq;
509 }
510
511 double var = sum_sq / static_cast<double>(n);
512 if (var == 0.0) {
513 throw std::invalid_argument("statcpp::population_kurtosis: zero variance");
514 }
515
516 double fourth_moment = sum_fourth / static_cast<double>(n);
517 return (fourth_moment / (var * var)) - 3.0;
518}
519
534template <typename Iterator>
535double sample_kurtosis(Iterator first, Iterator last)
536{
537 auto n = static_cast<std::size_t>(std::distance(first, last));
538 if (n < 4) {
539 throw std::invalid_argument("statcpp::sample_kurtosis: need at least 4 elements");
540 }
541
542 double g2 = population_kurtosis(first, last);
543 double nd = static_cast<double>(n);
544 double correction = ((nd + 1.0) * g2 + 6.0) * (nd - 1.0) / ((nd - 2.0) * (nd - 3.0));
545 return correction;
546}
547
558template <typename Iterator>
559double sample_kurtosis(Iterator first, Iterator last, double precomputed_mean)
560{
561 auto n = static_cast<std::size_t>(std::distance(first, last));
562 if (n < 4) {
563 throw std::invalid_argument("statcpp::sample_kurtosis: need at least 4 elements");
564 }
565
566 double g2 = population_kurtosis(first, last, precomputed_mean);
567 double nd = static_cast<double>(n);
568 double correction = ((nd + 1.0) * g2 + 6.0) * (nd - 1.0) / ((nd - 2.0) * (nd - 3.0));
569 return correction;
570}
571
583template <typename Iterator, typename Projection,
584 typename = std::enable_if_t<
585 std::is_invocable_v<Projection,
586 typename std::iterator_traits<Iterator>::value_type>>>
587double sample_kurtosis(Iterator first, Iterator last, Projection proj)
588{
589 auto n = static_cast<std::size_t>(std::distance(first, last));
590 if (n < 4) {
591 throw std::invalid_argument("statcpp::sample_kurtosis: need at least 4 elements");
592 }
593
594 double g2 = population_kurtosis(first, last, proj);
595 double nd = static_cast<double>(n);
596 double correction = ((nd + 1.0) * g2 + 6.0) * (nd - 1.0) / ((nd - 2.0) * (nd - 3.0));
597 return correction;
598}
599
612template <typename Iterator, typename Projection>
613double sample_kurtosis(Iterator first, Iterator last, Projection proj, double precomputed_mean)
614{
615 auto n = static_cast<std::size_t>(std::distance(first, last));
616 if (n < 4) {
617 throw std::invalid_argument("statcpp::sample_kurtosis: need at least 4 elements");
618 }
619
620 double g2 = population_kurtosis(first, last, proj, precomputed_mean);
621 double nd = static_cast<double>(n);
622 double correction = ((nd + 1.0) * g2 + 6.0) * (nd - 1.0) / ((nd - 2.0) * (nd - 3.0));
623 return correction;
624}
625
634template <typename Iterator>
635double kurtosis(Iterator first, Iterator last)
636{
637 return sample_kurtosis(first, last);
638}
639
649template <typename Iterator>
650double kurtosis(Iterator first, Iterator last, double precomputed_mean)
651{
652 return sample_kurtosis(first, last, precomputed_mean);
653}
654
665template <typename Iterator, typename Projection,
666 typename = std::enable_if_t<
667 std::is_invocable_v<Projection,
668 typename std::iterator_traits<Iterator>::value_type>>>
669double kurtosis(Iterator first, Iterator last, Projection proj)
670{
671 return sample_kurtosis(first, last, proj);
672}
673
685template <typename Iterator, typename Projection>
686double kurtosis(Iterator first, Iterator last, Projection proj, double precomputed_mean)
687{
688 return sample_kurtosis(first, last, proj, precomputed_mean);
689}
690
691} // namespace statcpp
Basic statistical computation functions.
double stddev(Iterator first, Iterator last)
Standard deviation (alias for sample_stddev)
double skewness(Iterator first, Iterator last)
Calculate skewness (alias for sample_skewness)
double kurtosis(Iterator first, Iterator last)
Calculate kurtosis (alias for sample_kurtosis)
double var(Iterator first, Iterator last, std::size_t ddof=0)
Variance (ddof = Delta Degrees of Freedom)
double population_skewness(Iterator first, Iterator last)
Calculate population skewness (Fisher's definition)
double population_kurtosis(Iterator first, Iterator last)
Calculate population kurtosis (Excess Kurtosis)
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 sample_kurtosis(Iterator first, Iterator last)
Calculate sample kurtosis (bias-corrected version)
double sample_skewness(Iterator first, Iterator last)
Calculate sample skewness (bias-corrected version)