-
-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathStatistic.h
292 lines (239 loc) · 8.25 KB
/
Statistic.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#pragma once
//
// FILE: Statistic.h
// AUTHOR: Rob Tillaart
// modified at 0.3 by Gil Ross at physics dot org
// template version 1.0.0 by Glen Cornell
// VERSION: 1.0.8
// PURPOSE: Recursive Statistical library for Arduino
// HISTORY: See CHANGELOG.md
//
// NOTE: 2011-01-07 Gill Ross
// Rob Tillaart's Statistic library uses one-pass of the data (allowing
// each value to be discarded), but expands the Sum of Squares Differences to
// difference the Sum of Squares and the Average Squared. This is susceptible
// to bit length precision errors with the float type (only 5 or 6 digits
// absolute precision) so for long runs and high ratios of
// the average value to standard deviation the estimate of the
// standard error (deviation) becomes the difference of two large
// numbers and will tend to zero.
//
// For small numbers of iterations and small Average/SE the original code is
// likely to work fine.
// It should also be recognised that for very large samples, questions
// of stability of the sample assume greater importance than the
// correctness of the asymptotic estimators.
//
// This recursive algorithm, which takes slightly more computation per
// iteration is numerically stable.
// It updates the number, mean, max, min and SumOfSquaresDiff each step to
// deliver max min average, population standard error (standard deviation) and
// unbiased SE.
// Toolchains for some platforms don't have the stdc++ library, so
// we'll make substitutions for features that we use. If your
// platform isn't specified here and fails to compile, then you can
// explicitly override the HAVE_STDCXX_TYPE_TRAITS, HAVE_STDCXX_CMATH,
// and HAVE_STDCXX_CSTDINT feature macros in your build environment.
#define STATISTIC_LIB_VERSION (F("1.0.8"))
#if defined(__AVR__)
#define HAVE_STDCXX_TYPE_TRAITS 0
#define HAVE_STDCXX_CMATH 0
#define HAVE_STDCXX_CSTDINT 0
#else
#ifndef HAVE_STDCXX_TYPE_TRAITS
#define HAVE_STDCXX_TYPE_TRAITS 1
#endif
#ifndef HAVE_STDCXX_CMATH
#define HAVE_STDCXX_CMATH 1
#endif
#ifndef HAVE_STDCXX_CSTDINT
#define HAVE_STDCXX_CSTDINT 1
#endif
#endif
#if HAVE_STDCXX_TYPE_TRAITS || defined(_GLIBCXX_TYPE_TRAITS)
#include <type_traits>
#else
namespace std {
// substitute for std::conditional if not in your tool chain
template<bool B, class T, class F>
struct conditional { typedef T type; };
template<class T, class F>
struct conditional<false, T, F> { typedef F type; };
};
#endif /* HAVE_STDCXX_TYPE_TRAITS */
#if HAVE_STDCXX_CMATH || defined(_GLIBCXX_CMATH)
#include <cmath>
#if not defined(__cplusplus) || __cplusplus < 201703L
// substitute for std::sqrtf function, patch for issue #13
#undef sqrtf
namespace std {
inline float sqrtf(float n) { return __builtin_sqrtf(n); }
};
#endif
#else
#include <math.h>
// substitute for std::sqrt functions if not in your tool chain
#undef sqrt
namespace std {
inline float sqrt(float n) { return __builtin_sqrtf(n); }
inline double sqrt(double n) { return __builtin_sqrt(n); }
inline long double sqrt(long double n) { return __builtin_sqrtl(n); }
};
#endif /* HAVE_STDCXX_CMATH */
#if HAVE_STDCXX_CSTDINT || defined(_GLIBCXX_CSTDINT)
#include <cstdint>
#else
#include <stdint.h> // uint32_t, etc.
#endif /* HAVE_STDCXX_CSTDINT */
#if HAVE_STDCXX_LIMITS || defined(_GLIBCXX_NUMERIC_LIMITS)
#include <limits>
#else
namespace std {
template<typename T>
struct numeric_limits {
static constexpr T
quiet_NaN() { return T(); }
};
template<>
struct numeric_limits<float> {
static constexpr float
quiet_NaN() { return __builtin_nanf(""); }
};
template<>
struct numeric_limits<double> {
static constexpr double
quiet_NaN() { return __builtin_nan(""); }
};
template<>
struct numeric_limits<long double> {
static constexpr long double
quiet_NaN() { return __builtin_nanl(""); }
};
};
#endif /* HAVE_STDCXX_LIMITS */
///////////////////////////////////////////////////////////////////////////////
//
// STATISTICS CLASS
//
namespace statistic {
template <typename T = float, typename C = uint32_t, bool _useStdDev = true>
class Statistic
{
public:
typedef T value_type;
typedef C count_type;
static constexpr value_type NaN { std::numeric_limits<value_type>::quiet_NaN() };
Statistic() = default;
void clear() {
_cnt = 0;
_sum = 0; // NaN;
_min = 0; // NaN;
_max = 0; // NaN;
_extra.clear();
// NOTE: _extra "guards" the conditional code e.g. ssqdiff
// NOTE: ssqdiff = not _ssq but sum of square differences
// which is SUM(from i = 1 to N) of f(i)-_ave_N)**2
}
// returns value actually added
value_type add(const value_type value) {
value_type previousSum = _sum;
if (_cnt == 0)
{
_min = value;
_max = value;
} else {
if (value < _min) _min = value;
else if (value > _max) _max = value;
}
_sum += value;
_cnt++;
if (_useStdDev && (_cnt > 1))
{
value_type _store = (_sum / _cnt - value);
_extra.ssqdif(_extra.ssqdif() + _cnt * _store * _store / (_cnt - 1));
// NOTE: pre 1.0.0 code
// ~10% faster but limits the amount of samples to 65K as _cnt*_cnt overflows
// value_type _store = _sum - _cnt * value;
// _ssqdif = _ssqdif + _store * _store / (_cnt*_cnt - _cnt);
//
// solution: TODO verify
// _ssqdif = _ssqdif + (_store * _store / _cnt) / (_cnt - 1);
}
return _sum - previousSum;
}
// returns the number of values added
count_type count() const { return _cnt; }; // zero if count == zero
value_type sum() const { return _sum; }; // zero if count == zero
value_type minimum() const { return _min; }; // zero if count == zero
value_type maximum() const { return _max; }; // zero if count == zero
value_type range() const { return _max - _min; }; // zero if count == zero
value_type middle() const
{
// prevent over- or underflow if value_type is an int type
double mid = _max * 0.5 + _min * 0.5;
return (value_type) mid;
}; // zero if count == zero
// NAN if count == zero
value_type average() const {
if (_cnt == 0) return NaN; // prevent DIV0 error
return _sum / _cnt;
}
// useStdDev must be true to use next three
// all return NAN if count == zero
value_type variance() const {
if (!_useStdDev) return NaN;
if (_cnt == 0) return NaN; // prevent DIV0 error
return _extra.ssqdif() / _cnt;
}
// Population standard deviation
value_type pop_stdev() const {
if (!_useStdDev) return NaN;
if (_cnt == 0) return NaN; // prevent DIV0 error
return std::sqrt( _extra.ssqdif() / _cnt);
}
value_type unbiased_stdev() const {
if (!_useStdDev) return NaN;
if (_cnt < 2) return NaN; // prevent DIV0 error
return std::sqrt( _extra.ssqdif() / (_cnt - 1));
}
value_type getCoefficientOfVariation() const {
if (_sum == 0) return NaN;
value_type temp = pop_stdev(); // as all samples are available
if (temp == NaN) return NaN;
// return standard deviation / average
value_type cv = temp * _cnt / _sum;
return cv;
}
// deprecated methods:
Statistic(bool) {
} __attribute__ ((deprecated ("use default constructor instead")));
void clear(bool) {
clear();
} __attribute__ ((deprecated ("use Statistic::clear(void) instead")));
protected:
count_type _cnt { 0 };
value_type _sum { 0.0 }; // NaN;
value_type _min { 0.0 }; // NaN;
value_type _max { 0.0 }; // NaN;
// Conditionally compile to reduce dead code if not used
struct Empty {
void clear() { }
value_type ssqdif() const { return NaN; }
void ssqdif(value_type v) { }
};
struct StdDev {
value_type _ssqdif { 0.0 }; // sum of squares difference
void clear() { _ssqdif = 0.0; }
value_type ssqdif() const { return _ssqdif; }
void ssqdif(value_type v) { _ssqdif = v; }
};
typename std::conditional<_useStdDev, StdDev, Empty>::type _extra;
};
} // namespace statistic
// This typedef maintains backwards API compatibility with library
// versions <= 0.4.4.
typedef statistic::Statistic<float, uint32_t, true> Statistic;
// NOTE: Do not issue 'using statistic;' in your code because the
// compiler will not be able to distinguish between the template
// '::Statistic' and the typedef 'statistic::Statistic'
// -- END OF FILE --