BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlsta_moment.h
Go to the documentation of this file.
1/// @file bdlsta_moment.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlsta_moment.h -*-C++-*-
8#ifndef INCLUDED_BDLSTA_MOMENT
9#define INCLUDED_BDLSTA_MOMENT
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14// BDE_VERIFY pragma: -LL01 // Link is just too long
15
16/// @defgroup bdlsta_moment bdlsta_moment
17/// @brief Online algorithm for mean, variance, skew, and kurtosis.
18/// @addtogroup bdl
19/// @{
20/// @addtogroup bdlsta
21/// @{
22/// @addtogroup bdlsta_moment
23/// @{
24///
25/// <h1> Outline </h1>
26/// * <a href="#bdlsta_moment-purpose"> Purpose</a>
27/// * <a href="#bdlsta_moment-classes"> Classes </a>
28/// * <a href="#bdlsta_moment-description"> Description </a>
29/// * <a href="#bdlsta_moment-usage"> Usage </a>
30/// * <a href="#bdlsta_moment-example-1-calculating-skew-variance-and-mean"> Example 1: Calculating skew, variance, and mean </a>
31///
32/// # Purpose {#bdlsta_moment-purpose}
33/// Online algorithm for mean, variance, skew, and kurtosis.
34///
35/// # Classes {#bdlsta_moment-classes}
36///
37/// - bdlsta::Moment: online calculation of mean, variance, skew, and kurtosis
38///
39/// # Description {#bdlsta_moment-description}
40/// This component provides a mechanism, `bdlsta::Moment`, that
41/// provides online calculation of basic statistics: mean, variance, skew, and
42/// kurtosis while maintaining accuracy. Online algorithms process the data in
43/// one pass, while keeping good accuracy. The online algorithms used are
44/// Welford for variance, and the stable skew and kurtosis algorithms taken
45/// from:
46/// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics
47///
48/// The implementation uses template specialization so the user can choose the
49/// statistics necessary, and not calculate or allocate memory for those
50/// statistics that are not needed.
51///
52/// The template parameter is a value from the provided enum and having the
53/// following interpretation:
54/// @code
55/// M1 - mean
56/// M2 - variance+mean
57/// M3 - skew+variance+mean
58/// M4 - kurtosis+skew+variance+mean
59/// @endcode
60///
61/// ## Usage {#bdlsta_moment-usage}
62///
63///
64/// This section illustrates intended use of this component.
65///
66/// ### Example 1: Calculating skew, variance, and mean {#bdlsta_moment-example-1-calculating-skew-variance-and-mean}
67///
68///
69/// This example shows how to accumulate a set of values, and calculate the
70/// skew, variance, and kurtosis.
71///
72/// First, we create example input and instantiate the appropriate mechanism:
73/// @code
74/// double input[] = { 1.0, 2.0, 4.0, 5.0 };
75///
76/// bdlsta::Moment<bdlsta::MomentLevel::e_M3> m3;
77/// @endcode
78/// Then, we invoke the `add` routine to accumulate the data:
79/// @code
80/// for(int i = 0; i < 4; ++i) {
81/// m3.add(input[i]);
82/// }
83/// @endcode
84/// Finally, we assert that the mean, variance, and skew are what we expect:
85/// @code
86/// ASSERT(4 == m3.count());
87/// ASSERT(3.0 == m3.mean());
88/// ASSERT(1e-5 > fabs(3.33333 - m3.variance()));
89/// ASSERT(1e-5 > fabs(0.0 - m3.skew()));
90/// @endcode
91/// @}
92/** @} */
93/** @} */
94
95/** @addtogroup bdl
96 * @{
97 */
98/** @addtogroup bdlsta
99 * @{
100 */
101/** @addtogroup bdlsta_moment
102 * @{
103 */
104
105// BDE_VERIFY pragma: +LL01
106
107#include <bdlscm_version.h>
108
109#include <bsl_cmath.h>
110
111#include <bsls_assert.h>
112#include <bsls_review.h>
113
114
115namespace bdlsta {
116
118 // TYPES
119 enum Enum {
120 // Enumeration of moment level of data desired.
121
122 e_M1, // mean
123 e_M2, // variance+mean
124 e_M3, // skew+variance+mean
125 e_M4 // kurtosis+skew+variance+mean
126 };
127};
128
129 // ==========================
130 // private struct Moment_Data
131 // ==========================
132
133template <MomentLevel::Enum ML>
135
136/// Data members for Mean only.
137template<>
139
140 // PUBLIC DATA
141 int d_count; // Number of entries.
142 double d_sum; // Sum of entries.
143
144 // CREATORS
145
146 /// Constructor initializes all members to zero.
147 Moment_Data();
148};
149
150/// Data members for Variance and below.
151template<>
153
154 // PUBLIC DATA
155 int d_count; // Number of entries.
156 double d_sum; // Sum of entries.
157 double d_mean; // Mean of entries.
158 double d_M2; // 2nd moment, for variance.
159
160 // CREATORS
161
162 /// Constructor initializes all members to zero.
163 Moment_Data();
164};
165
166/// Data members for Skew and below
167template<>
169
170 // PUBLIC DATA
171 int d_count; // Number of entries.
172 double d_sum; // Sum of entries.
173 double d_mean; // Mean of entries.
174 double d_M2; // 2nd moment, for variance.
175 double d_M3; // 3rd moment, for skew
176
177 // CREATORS
178
179 /// Constructor initializes all members to zero.
180 Moment_Data();
181};
182
183/// Data members for Kurtosis and below
184template<>
186
187 // PUBLIC DATA
188 int d_count; // Number of entries.
189 double d_sum; // Sum of entries.
190 double d_mean; // Mean of entries.
191 double d_M2; // 2nd moment, for variance.
192 double d_M3; // 3rd moment, for skew
193 double d_M4; // 4th moment, for kurtosis
194
195 // CREATORS
196
197 /// Constructor initializes all members to zero.
198 Moment_Data();
199};
200
201 // ============
202 // class Moment
203 // ============
204
205// BDE_VERIFY pragma: -LL01 // Link is just too long
206
207/// This class provides efficient and accurate online algorithms for
208/// calculating mean, variance, skew, and kurtosis. The class provides
209/// template specializations, so that no unnecessary data members will be
210/// kept or unnecessary calculations done. The online algorithms used are
211/// Welford for variance, and the stable M3 and M4 are taken from:
212/// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics
213///
214/// The formula for sample skewness is taken from:
215/// http://www.macroption.com/skewness-formula/
216///
217/// The formula for sample excess kurtosis is taken from:
218/// http://www.macroption.com/kurtosis-formula/
219///
220/// See @ref bdlsta_moment
221template <MomentLevel::Enum ML>
222class Moment {
223
224 // BDE_VERIFY pragma: +LL01
225
226 private:
227 // PRIVATE TYPES
228 typedef struct Moment_Data<ML> Moment_Data_t;
229
230 // DATA
231 Moment_Data_t d_data;
232
233 public:
234 // CONSTANTS
235 enum {
238 };
239
240 // MANIPULATORS
241
242 /// Add the specified `value` to the data set.
243 void add(double value);
244
245 // ACCESSORS
246
247 /// Returns the number of elements in the data set.
248 int count() const;
249
250 /// Return the kurtosis of the data set.
251 ///
252 /// \pre The behavior is undefined unless `4 <= count` and the variance is not zero.
253 double kurtosis() const;
254
255 /// Load into the specified `result`, the kurtosis of the data set.
256 /// Return 0 on success, and a non-zero value otherwise. Specifically,
257 /// `e_INADEQUATE_DATA` is returned if `4 > count` or the variance is
258 /// zero.
259 int kurtosisIfValid(double *result) const;
260
261 /// Return the mean of the data set.
262 /// \pre The behavior is undefined unless
263 /// `1 <= count`.
264 double mean() const;
265
266 /// Load into the specified `result`, the mean of the data set. Return
267 /// 0 on success, and a non-zero value otherwise. Specifically,
268 /// `e_INADEQUATE_DATA` is returned if `1 > count`.
269 int meanIfValid(double *result) const;
270
271 /// Return skew of the data set.
272 /// \pre The behavior is undefined unless
273 /// `3 <= count` or the variance is zero.
274 double skew() const;
275
276 /// Load into the specified `result`, the skew of the data set. Return
277 /// 0 on success, and a non-zero value otherwise. Specifically,
278 /// `e_INADEQUATE_DATA` is returned if `3 > count` or the variance is
279 /// zero.
280 int skewIfValid(double *result) const;
281
282 /// Return the variance of the data set.
283 ///
284 /// \pre The behavior is undefined unless `2 <= count`.
285 double variance() const;
286
287 /// Load into the specified `result`, the variance of the data set.
288 /// Return 0 on success, and a non-zero value otherwise. Specifically,
289 /// `e_INADEQUATE_DATA` is returned if `2 > count`.
290 int varianceIfValid(double *result) const;
291};
292
293// ============================================================================
294// INLINE DEFINITIONS
295// ============================================================================
296
297 // --------------------------
298 // struct bdlsta::Moment_Data
299 // --------------------------
300
301// CREATORS
302inline
304: d_count(0)
305, d_sum(0.0)
306{
307}
308
309inline
311: d_count(0)
312, d_sum(0.0)
313, d_mean(0.0)
314, d_M2(0.0)
315{
316}
317
318inline
320: d_count(0)
321, d_sum(0.0)
322, d_mean(0.0)
323, d_M2(0.0)
324, d_M3(0.0)
325{
326}
327
328inline
330: d_count(0)
331, d_sum(0.0)
332, d_mean(0.0)
333, d_M2(0.0)
334, d_M3(0.0)
335, d_M4(0.0)
336{
337}
338
339 // --------------------
340 // class bdlsta::Moment
341 // --------------------
342
343// MANIPULATORS
344template<>
345inline
347{
348 ++d_data.d_count;
349 d_data.d_sum += value;
350}
351
352template<>
353inline
355{
356 // Modified Welford algorithm for variance.
357 const double delta = value - d_data.d_mean;
358 d_data.d_sum += value;
359 ++d_data.d_count;
360 d_data.d_mean = d_data.d_sum / static_cast<double>(d_data.d_count);
361 const double delta2 = value - d_data.d_mean;
362 d_data.d_M2 += delta * delta2;
363}
364
365template<>
366inline
368{
369 // Modified Welford algorithm for variance, and similar algorithm for skew.
370 const double delta = value - d_data.d_mean;
371 const double nm1 = d_data.d_count;
372 d_data.d_sum += value;
373 ++d_data.d_count;
374 const double n = d_data.d_count;
375 const double deltaN = delta / n;
376 d_data.d_mean = d_data.d_sum / n;
377 const double term1 = delta * deltaN * nm1;
378 d_data.d_M3 += term1 * deltaN * (n - 2.0) - 3.0 * deltaN * d_data.d_M2;
379 d_data.d_M2 += term1;
380}
381
382template<>
383inline
385{
386 // Modified Welford algorithm for variance, and similar algorithms for skew
387 // and kurtosis.
388 const double delta = value - d_data.d_mean;
389 const double nm1 = d_data.d_count;
390 d_data.d_sum += value;
391 ++d_data.d_count;
392 const double n = d_data.d_count;
393 const double n2 = n * n;
394 const double deltaN = delta / n;
395 d_data.d_mean = d_data.d_sum / n;
396 const double term1 = delta * deltaN * nm1;
397 const double deltaN2 = deltaN * deltaN;
398 d_data.d_M4 += term1 * deltaN2 * (n2 - 3.0 * n + 3.0) +
399 6 * deltaN2 * d_data.d_M2 - 4.0 * deltaN * d_data.d_M3;
400 d_data.d_M3 += term1 * deltaN * (n - 2.0) - 3.0 * deltaN * d_data.d_M2;
401 d_data.d_M2 += term1;
402}
403
404// ACCESSORS
405template <MomentLevel::Enum ML>
406inline
408{
409 return d_data.d_count;
410}
411
412template<>
413inline
415{
416 BSLS_ASSERT(4 <= d_data.d_count && 0.0 != d_data.d_M2);
417
418 const double n = static_cast<double>(d_data.d_count);
419 const double n1 = (n - 1.0);
420 const double n2n3 = (n - 2.0) * (n - 3.0);
421 return n * (n + 1.0) * n1 / n2n3 * d_data.d_M4 / d_data.d_M2 / d_data.d_M2
422 - 3.0 * n1 * n1 / n2n3;
423}
424
425template<>
426inline
428{
429 if (4 > d_data.d_count || 0.0 == d_data.d_M2) {
430 return e_INADEQUATE_DATA; // RETURN
431 }
432 *result = kurtosis();
433 return 0;
434}
435
436template <MomentLevel::Enum ML>
437inline
438double Moment<ML>::mean() const
439{
440 BSLS_ASSERT(1 <= d_data.d_count);
441
442 return d_data.d_sum / static_cast<double>(d_data.d_count);
443}
444
445template <MomentLevel::Enum ML>
446inline
447int Moment<ML>::meanIfValid(double *result) const
448{
449 if (1 > d_data.d_count) {
450 return e_INADEQUATE_DATA; // RETURN
451 }
452 *result = mean();
453 return 0;
454}
455
456template <MomentLevel::Enum ML>
457inline
458double Moment<ML>::skew() const
459{
460 BSLS_ASSERT(3 <= d_data.d_count && 0.0 != d_data.d_M2);
461
462 const double n = static_cast<double>(d_data.d_count);
463 return bsl::sqrt(n - 1.0) * n / (n- 2.0) * d_data.d_M3
464 / bsl::pow(d_data.d_M2, 1.5);
465}
466
467template <MomentLevel::Enum ML>
468inline int Moment<ML>::skewIfValid(double *result) const
469{
470 if (3 > d_data.d_count || 0.0 == d_data.d_M2) {
471 return e_INADEQUATE_DATA; // RETURN
472 }
473 *result = skew();
474 return 0;
475}
476
477template <MomentLevel::Enum ML>
478inline
480{
481 BSLS_ASSERT(2 <= d_data.d_count);
482
483 return d_data.d_M2 / (d_data.d_count - 1);
484}
485
486template <MomentLevel::Enum ML>
487inline
488int Moment<ML>::varianceIfValid(double *result) const
489{
490 if (2 > d_data.d_count) {
491 return e_INADEQUATE_DATA; // RETURN
492 }
493 *result = variance();
494 return 0;
495}
496
497} // close package namespace
498
499
500#endif
501
502// ----------------------------------------------------------------------------
503// Copyright 2017 Bloomberg Finance L.P.
504//
505// Licensed under the Apache License, Version 2.0 (the "License");
506// you may not use this file except in compliance with the License.
507// You may obtain a copy of the License at
508//
509// http://www.apache.org/licenses/LICENSE-2.0
510//
511// Unless required by applicable law or agreed to in writing, software
512// distributed under the License is distributed on an "AS IS" BASIS,
513// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
514// See the License for the specific language governing permissions and
515// limitations under the License.
516// ----------------------------- END-OF-FILE ----------------------------------
517
518/** @} */
519/** @} */
520/** @} */
Definition bdlsta_moment.h:222
double variance() const
Definition bdlsta_moment.h:479
double skew() const
Definition bdlsta_moment.h:458
int count() const
Returns the number of elements in the data set.
Definition bdlsta_moment.h:407
int kurtosisIfValid(double *result) const
double kurtosis() const
@ e_INADEQUATE_DATA
Definition bdlsta_moment.h:237
@ e_SUCCESS
Definition bdlsta_moment.h:236
int varianceIfValid(double *result) const
Definition bdlsta_moment.h:488
double mean() const
Definition bdlsta_moment.h:438
int skewIfValid(double *result) const
Definition bdlsta_moment.h:468
int meanIfValid(double *result) const
Definition bdlsta_moment.h:447
void add(double value)
Add the specified value to the data set.
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bdlsta_linefit.h:110
Definition bdlsta_moment.h:117
Enum
Definition bdlsta_moment.h:119
@ e_M4
Definition bdlsta_moment.h:125
@ e_M3
Definition bdlsta_moment.h:124
@ e_M1
Definition bdlsta_moment.h:122
@ e_M2
Definition bdlsta_moment.h:123
int d_count
Definition bdlsta_moment.h:141
double d_sum
Definition bdlsta_moment.h:142
double d_sum
Definition bdlsta_moment.h:156
double d_mean
Definition bdlsta_moment.h:157
int d_count
Definition bdlsta_moment.h:155
double d_M2
Definition bdlsta_moment.h:158
double d_mean
Definition bdlsta_moment.h:173
double d_M2
Definition bdlsta_moment.h:174
int d_count
Definition bdlsta_moment.h:171
double d_M3
Definition bdlsta_moment.h:175
double d_sum
Definition bdlsta_moment.h:172
int d_count
Definition bdlsta_moment.h:188
double d_M2
Definition bdlsta_moment.h:191
double d_mean
Definition bdlsta_moment.h:190
double d_M3
Definition bdlsta_moment.h:192
double d_M4
Definition bdlsta_moment.h:193
double d_sum
Definition bdlsta_moment.h:189
Definition bdlsta_moment.h:134