Skip to content

Commit b56ccc7

Browse files
authored
Add mir.math.stat (#217)
* add mir.math.stat * update meson and docs
1 parent 619e7f5 commit b56ccc7

File tree

5 files changed

+177
-9
lines changed

5 files changed

+177
-9
lines changed

doc/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ PACKAGE_mir_combinatorics = package
3737
PACKAGE_mir_container = binaryheap
3838
PACKAGE_mir_graph = tarjan package
3939
PACKAGE_mir_interpolate = package constant linear spline utility polynomial
40-
PACKAGE_mir_math = sum numeric
40+
PACKAGE_mir_math = sum numeric stat
4141
PACKAGE_mir_math_func = expdigamma
4242
PACKAGE_mir_ndslice_connect = cpython
4343
PACKAGE_mir_rc = package ptr array context

index.d

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ $(BOOKTABLE ,
1818
$(LEADINGROW Math)
1919
$(TR $(TDNW $(MREF mir,numeric)) $(TD Basic numeric optimisations))
2020
$(TR $(TDNW $(MREF mir,math,numeric)) $(TD Simple numeric algorithms))
21-
$(TR $(TDNW $(MREF mir,math,sum)★) $(TD Various precise summation algorithms))
21+
$(TR $(TDNW $(MREF mir,math,sum)) $(TD Various precise summation algorithms))
22+
$(TR $(TDNW $(MREF mir,math,stat)) $(TD Basic API for statistics))
2223
$(TR $(TDNW $(MREF mir,math,constant)) $(TD Math constants))
2324
$(TR $(TDNW $(MREF mir,polynomial)) $(TD Polynomial ref-counted structure))
2425
$(LEADINGROW Reference counting)

meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ mir_algorithm_src = [
3232
'source/mir/interpolate/utility.d',
3333
'source/mir/math/func/expdigamma.d',
3434
'source/mir/math/numeric.d',
35+
'source/mir/math/stat.d',
3536
'source/mir/math/sum.d',
3637
'source/mir/ndslice/allocation.d',
3738
'source/mir/ndslice/chunks.d',
@@ -50,6 +51,7 @@ mir_algorithm_src = [
5051
'source/mir/ndslice/topology.d',
5152
'source/mir/ndslice/traits.d',
5253
'source/mir/numeric.d',
54+
'source/mir/polinomial.d',
5355
'source/mir/range.d',
5456
'source/mir/rc/array.d',
5557
'source/mir/rc/context.d',

source/mir/math/stat.d

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/++
2+
This module contains base statistical algorithms.
3+
4+
Note that used specialized summing algorithms execute more primitive operations
5+
than vanilla summation. Therefore, if in certain cases maximum speed is required
6+
at expense of precision, one can use $(SUBREF, sum, Summation.fast).
7+
8+
License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0).
9+
10+
Authors: Ilya Yaroshenko
11+
12+
Copyright: 2019 Symmetry Investments Group and Kaleidic Associates Advisory Limited.
13+
14+
Macros:
15+
SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
16+
T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
17+
T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
18+
+/
19+
module mir.math.stat;
20+
21+
import core.lifetime: move;
22+
import mir.math.common: optmath;
23+
import mir.math.sum;
24+
import mir.primitives;
25+
import std.range.primitives: isInputRange;
26+
import std.traits: isArray, isFloatingPoint;
27+
28+
/++
29+
Computes the average of `r`, which must be a finite iterable.
30+
31+
Returns:
32+
The average of all the elements in the range r.
33+
+/
34+
template mean(Summation summation = Summation.appropriate)
35+
{
36+
///
37+
@safe @optmath sumType!Range
38+
mean(Range)(Range r)
39+
if (hasLength!Range
40+
|| summation == Summation.appropriate
41+
|| summation == Summation.fast
42+
|| summation == Summation.naive)
43+
{
44+
static if (hasLength!Range)
45+
{
46+
auto n = r.length;
47+
return sum!summation(r.move) / cast(sumType!Range) n;
48+
}
49+
else
50+
{
51+
auto s = cast(typeof(return)) 0;
52+
size_t length;
53+
foreach (e; r)
54+
{
55+
length++;
56+
s += e;
57+
}
58+
return s / cast(sumType!Range) length;
59+
}
60+
}
61+
}
62+
63+
///ditto
64+
template mean(string summation)
65+
{
66+
mixin("alias mean = .mean!(Summation." ~ summation ~ ");");
67+
}
68+
69+
///
70+
version(mir_test) @safe pure nothrow unittest
71+
{
72+
assert(mean([1.0, 2, 3]) == 2);
73+
}
74+
75+
/++
76+
A linear regression model with a single explanatory variable.
77+
+/
78+
template simpleLinearRegression(Summation summation = Summation.kbn)
79+
{
80+
import mir.ndslice.slice;
81+
82+
/++
83+
Params:
84+
x = `x[i]` points
85+
y = `f(x[i])` values
86+
Returns:
87+
The pair of shift and slope of the linear curve.
88+
+/
89+
@optmath
90+
sumType!YRange[2]
91+
simpleLinearRegression(XRange, YRange)(XRange x, YRange y) @safe
92+
if (isInputRange!XRange && isInputRange!YRange && !(isArray!XRange && isArray!YRange) && isFloatingPoint!(sumType!YRange))
93+
in {
94+
static if (hasLength!XRange && hasLength!YRange)
95+
assert(x.length == y.length);
96+
}
97+
body {
98+
alias X = typeof(sumType!XRange.init * sumType!XRange.init);
99+
alias Y = sumType!YRange;
100+
enum summationX = !__traits(isIntegral, X) ? summation : Summation.naive;
101+
Summator!(X, summationX) xms = 0;
102+
Summator!(Y, summation) yms = 0;
103+
Summator!(X, summationX) xxms = 0;
104+
Summator!(Y, summation) xyms = 0;
105+
106+
static if (hasLength!XRange)
107+
sizediff_t n = x.length;
108+
else
109+
sizediff_t n = 0;
110+
111+
while (!x.empty)
112+
{
113+
static if (!(hasLength!XRange && hasLength!YRange))
114+
assert(!y.empty);
115+
116+
static if (!hasLength!XRange)
117+
n++;
118+
119+
auto xi = x.front;
120+
auto yi = y.front;
121+
xms.put(xi);
122+
yms.put(yi);
123+
xxms.put(xi * xi);
124+
xyms.put(xi * yi);
125+
126+
y.popFront;
127+
x.popFront;
128+
}
129+
130+
static if (!(hasLength!XRange && hasLength!YRange))
131+
assert(y.empty);
132+
133+
auto xm = xms.sum;
134+
auto ym = yms.sum;
135+
auto xxm = xxms.sum;
136+
auto xym = xyms.sum;
137+
138+
auto slope = (xym * n - xm * ym) / (xxm * n - xm * xm);
139+
140+
return [(ym - slope * xm) / n, slope];
141+
}
142+
143+
/// ditto
144+
@optmath
145+
typeof(X.init * Y.init)[2]
146+
simpleLinearRegression(X, Y)(scope const X[] x, scope const Y[] y) @safe
147+
{
148+
return .simpleLinearRegression!summation(x.sliced, y.sliced);
149+
}
150+
}
151+
152+
/// ditto
153+
template simpleLinearRegression(string summation)
154+
{
155+
mixin("alias simpleLinearRegression = .simpleLinearRegression!(Summation." ~ summation ~ ");");
156+
}
157+
158+
///
159+
version(mir_test) @safe pure nothrow @nogc unittest
160+
{
161+
import mir.math.common: approxEqual;
162+
static immutable x = [0, 1, 2, 3];
163+
static immutable y = [-1, 0.2, 0.9, 2.1];
164+
auto params = x.simpleLinearRegression(y);
165+
assert(params[0].approxEqual(-0.95)); // shift
166+
assert(params[1].approxEqual(1)); // slope
167+
}

source/mir/math/sum.d

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,7 @@ public:
964964
else
965965
static if (isPointer!Iterator && kind == Contiguous)
966966
{
967-
this.put(r.iterator[0 .. r.length]);
967+
this.put(r.field);
968968
}
969969
else
970970
static if (summation == Summation.fast && N == 1)
@@ -1652,7 +1652,7 @@ unittest
16521652
}
16531653
}
16541654

1655-
/**
1655+
/++
16561656
Sums elements of `r`, which must be a finite
16571657
iterable.
16581658
@@ -1661,13 +1661,11 @@ value, but its type will be used if it is not specified.
16611661
16621662
Note that these specialized summing algorithms execute more primitive operations
16631663
than vanilla summation. Therefore, if in certain cases maximum speed is required
1664-
at expense of precision, one can use $(XREF, numeric_summation, Summation.fast).
1664+
at expense of precision, one can use $(LREF, Summation.fast).
16651665
16661666
Returns:
16671667
The sum of all the elements in the range r.
1668-
1669-
See_Also: $(XREFMODULE, numeric_summation) contains detailed documentation and examples about available summation algorithms.
1670-
*/
1668+
+/
16711669
template sum(F, Summation summation = Summation.appropriate)
16721670
if (isFloatingPoint!F && isMutable!F)
16731671
{
@@ -1964,7 +1962,7 @@ private T summationInitValue(T)()
19641962
}
19651963
}
19661964

1967-
private template sumType(Range)
1965+
package template sumType(Range)
19681966
{
19691967
import mir.ndslice.slice: isSlice, DeepElementType;
19701968
static if (isSlice!Range)

0 commit comments

Comments
 (0)