1 /**
2   Range-based statistic computations.
3 
4   If you need real statistics, consider using the $(WEB github.com/dsimcha/dstats,Dstats) library.
5  */
6 module gfm.math.statistics;
7 
8 import std.range,
9        std.math;
10 
11 /// Arithmetic mean.
12 double average(R)(R r) if (isInputRange!R)
13 {
14     if (r.empty)
15         return double.nan;
16 
17     typeof(r.front()) sum = 0;
18     size_t count = 0;
19     foreach(e; r)
20     {
21         sum += e;
22         ++count;
23     }
24     return sum / count;
25 }
26 
27 /// Minimum of a range.
28 double minElement(R)(R r) if (isInputRange!R)
29 {
30     // do like Javascript for an empty range
31     if (r.empty)
32         return double.infinity;
33 
34     return minmax!("<", R)(r);
35 }
36 
37 /// Maximum of a range.
38 double maxElement(R)(R r) if (isInputRange!R)
39 {
40     // do like Javascript for an empty range
41     if (r.empty)
42         return -double.infinity;
43 
44     return minmax!(">", R)(r);
45 }
46 
47 /// Variance of a range.
48 double variance(R)(R r) if (isForwardRange!R)
49 {
50     if (r.empty)
51         return double.nan;
52 
53     auto avg = average(r.save); // getting the average
54 
55     typeof(avg) sum = 0;
56     size_t count = 0;
57     foreach(e; r)
58     {
59         sum += (e - avg) ^^ 2;
60         ++count;
61     }
62     if (count <= 1)
63         return 0.0;
64     else
65         return (sum / (count - 1.0)); // using sample std deviation as estimator
66 }
67 
68 /// Standard deviation of a range.
69 double standardDeviation(R)(R r) if (isForwardRange!R)
70 {
71     return sqrt(variance(r));
72 }
73 
74 private
75 {
76     typeof(R.front()) minmax(string op, R)(R r) if (isInputRange!R)
77     {
78         assert(!r.empty);
79         auto best = r.front();
80         r.popFront();
81         foreach(e; r)
82         {
83             mixin("if (e " ~ op ~ " best) best = e;");
84         }
85         return best;
86     }
87 }