@cryptotaxi247 / netdata-1 / commits / f857aa35a

optimized ses and added des (#4470)

* optimized ses and added des * added coefficient of variation * fix bug identified by @vlvkobal: use all available points when resampling is required and the timeframe is not enough for a single point

Costa Tsaousis committed Oct 24, 2018 at 03:03 UTC f857aa35aed99b97b9b123e35544cf94260b108d
15 files changed +428 -52
CMakeLists.txt
+2
@@ -393,6 +393,8 @@ set(API_PLUGIN_FILES
393 web/api/queries/stddev/stddev.h
394 web/api/queries/ses/ses.c
395 web/api/queries/ses/ses.h
396 + web/api/queries/des/des.c
397 + web/api/queries/des/des.h
398 )
399
400 set(STREAMING_PLUGIN_FILES
Makefile.am
+2
@@ -291,6 +291,8 @@ API_PLUGIN_FILES = \
291 web/api/exporters/shell/allmetrics_shell.h \
292 web/api/queries/average/average.c \
293 web/api/queries/average/average.h \
294 + web/api/queries/des/des.c \
295 + web/api/queries/des/des.h \
296 web/api/queries/incremental_sum/incremental_sum.c \
297 web/api/queries/incremental_sum/incremental_sum.h \
298 web/api/queries/max/max.c \
configure.ac
+1
@@ -609,6 +609,7 @@ AC_CONFIG_FILES([
609 web/api/exporters/prometheus/Makefile
610 web/api/queries/Makefile
611 web/api/queries/average/Makefile
612 + web/api/queries/des/Makefile
613 web/api/queries/incremental_sum/Makefile
614 web/api/queries/max/Makefile
615 web/api/queries/median/Makefile
cppcheck.sh
+2 -2
@@ -12,14 +12,14 @@ processors=$(grep -c ^processor /proc/cpuinfo)
12 base="$(dirname "${0}")"
13 [ "${base}" = "." ] && base="${PWD}"
14
15 -cd "${base}/src" || exit 1
15 +cd "${base}" || exit 1
16
17 [ ! -d "cppcheck-build" ] && mkdir "cppcheck-build"
18
19 file="${1}"
20 shift
21 # shellcheck disable=SC2235
22 -([ "${file}" = "${base}" ] || [ -z "${file}" ]) && file="${base}/src"
22 +([ "${file}" = "${base}" ] || [ -z "${file}" ]) && file="${base}"
23
24 "${cppcheck}" \
25 -j ${processors} \
web/api/queries/des/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/queries/des/README.md new
+1
@@ -0,0 +1 @@
1 +# double exponential smoothing
web/api/queries/des/des.c new
+112
@@ -0,0 +1,112 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "des.h"
4 +
5 +
6 +// ----------------------------------------------------------------------------
7 +// single exponential smoothing
8 +
9 +struct grouping_des {
10 + calculated_number alpha;
11 + calculated_number alpha_other;
12 + calculated_number beta;
13 + calculated_number beta_other;
14 +
15 + calculated_number level;
16 + calculated_number trend;
17 +
18 + size_t count;
19 +};
20 +
21 +#define MAX_WINDOW_SIZE 10
22 +
23 +static inline void set_alpha(RRDR *r, struct grouping_des *g) {
24 + // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
25 + // A commonly used value for alpha is 2 / (N + 1)
26 + calculated_number window = (r->group > MAX_WINDOW_SIZE) ? MAX_WINDOW_SIZE : r->group;
27 +
28 + g->alpha = 2.0 / ((calculated_number)window + 1.0);
29 + g->alpha_other = 1.0 - g->alpha;
30 +
31 + //info("alpha for chart '%s' is " CALCULATED_NUMBER_FORMAT, r->st->name, g->alpha);
32 +}
33 +
34 +static inline void set_beta(RRDR *r, struct grouping_des *g) {
35 + // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
36 + // A commonly used value for alpha is 2 / (N + 1)
37 + calculated_number window = (r->group > MAX_WINDOW_SIZE) ? MAX_WINDOW_SIZE : r->group;
38 +
39 + g->beta = 2.0 / ((calculated_number)window + 1.0);
40 + g->beta_other = 1.0 - g->beta;
41 +
42 + //info("beta for chart '%s' is " CALCULATED_NUMBER_FORMAT, r->st->name, g->beta);
43 +}
44 +
45 +void *grouping_init_des(RRDR *r) {
46 + struct grouping_des *g = (struct grouping_des *)malloc(sizeof(struct grouping_des));
47 + set_alpha(r, g);
48 + set_beta(r, g);
49 + g->level = 0.0;
50 + g->trend = 0.0;
51 + g->count = 0;
52 + return g;
53 +}
54 +
55 +// resets when switches dimensions
56 +// so, clear everything to restart
57 +void grouping_reset_des(RRDR *r) {
58 + struct grouping_des *g = (struct grouping_des *)r->grouping_data;
59 + g->level = 0.0;
60 + g->trend = 0.0;
61 + g->count = 0;
62 +
63 + // fprintf(stderr, "\nDES: ");
64 +
65 +}
66 +
67 +void grouping_free_des(RRDR *r) {
68 + freez(r->grouping_data);
69 + r->grouping_data = NULL;
70 +}
71 +
72 +void grouping_add_des(RRDR *r, calculated_number value) {
73 + struct grouping_des *g = (struct grouping_des *)r->grouping_data;
74 +
75 + if(isnormal(value)) {
76 + if(likely(g->count > 0)) {
77 + // we have at least a number so far
78 +
79 + if(unlikely(g->count == 1)) {
80 + // the second value we got
81 + g->trend = value - g->trend;
82 + g->level = value;
83 + }
84 +
85 + // for the values, except the first
86 + calculated_number last_level = g->level;
87 + g->level = (g->alpha * value) + (g->alpha_other * (g->level + g->trend));
88 + g->trend = (g->beta * (g->level - last_level)) + (g->beta_other * g->trend);
89 + }
90 + else {
91 + // the first value we got
92 + g->level = g->trend = value;
93 + }
94 +
95 + g->count++;
96 + }
97 +
98 + //fprintf(stderr, "value: " CALCULATED_NUMBER_FORMAT ", level: " CALCULATED_NUMBER_FORMAT ", trend: " CALCULATED_NUMBER_FORMAT "\n", value, g->level, g->trend);
99 +}
100 +
101 +calculated_number grouping_flush_des(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
102 + struct grouping_des *g = (struct grouping_des *)r->grouping_data;
103 +
104 + if(unlikely(!g->count || !isnormal(g->level))) {
105 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
106 + return 0.0;
107 + }
108 +
109 + //fprintf(stderr, " RESULT for %zu values = " CALCULATED_NUMBER_FORMAT " \n", g->count, g->level);
110 +
111 + return g->level;
112 +}
web/api/queries/des/des.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_QUERIES_DES_H
4 +#define NETDATA_API_QUERIES_DES_H
5 +
6 +#include "../query.h"
7 +#include "../rrdr.h"
8 +
9 +extern void *grouping_init_des(RRDR *r);
10 +extern void grouping_reset_des(RRDR *r);
11 +extern void grouping_free_des(RRDR *r);
12 +extern void grouping_add_des(RRDR *r, calculated_number value);
13 +extern calculated_number grouping_flush_des(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
14 +
15 +#endif //NETDATA_API_QUERIES_DES_H
web/api/queries/query.c
+18 -3
@@ -12,6 +12,7 @@
12 #include "sum/sum.h"
13 #include "stddev/stddev.h"
14 #include "ses/ses.h"
15 +#include "des/des.h"
16
17 // ----------------------------------------------------------------------------
18
@@ -31,9 +32,23 @@ static struct {
32 , { "median" , 0, RRDR_GROUPING_MEDIAN , grouping_init_median , grouping_reset_median , grouping_free_median , grouping_add_median , grouping_flush_median }
33 , { "min" , 0, RRDR_GROUPING_MIN , grouping_init_min , grouping_reset_min , grouping_free_min , grouping_add_min , grouping_flush_min }
34 , { "max" , 0, RRDR_GROUPING_MAX , grouping_init_max , grouping_reset_max , grouping_free_max , grouping_add_max , grouping_flush_max }
34 - , { "ses" , 0, RRDR_GROUPING_SES , grouping_init_ses , grouping_reset_ses , grouping_free_ses , grouping_add_ses , grouping_flush_ses }
35 - , { "stddev" , 0, RRDR_GROUPING_STDDEV , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_stddev }
35 , { "sum" , 0, RRDR_GROUPING_SUM , grouping_init_sum , grouping_reset_sum , grouping_free_sum , grouping_add_sum , grouping_flush_sum }
36 +
37 + // stddev module provides mean, variance and coefficient of variation
38 + , { "stddev" , 0, RRDR_GROUPING_STDDEV , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_stddev }
39 + , { "cv" , 0, RRDR_GROUPING_CV , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_coefficient_of_variation }
40 + //, { "mean" , 0, RRDR_GROUPING_MEAN , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_mean }
41 + //, { "variance" , 0, RRDR_GROUPING_VARIANCE , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_variance }
42 +
43 + // single exponential smoothing or exponential weighted moving average
44 + , { "ses" , 0, RRDR_GROUPING_SES , grouping_init_ses , grouping_reset_ses , grouping_free_ses , grouping_add_ses , grouping_flush_ses }
45 + , { "ema" , 0, RRDR_GROUPING_SES , grouping_init_ses , grouping_reset_ses , grouping_free_ses , grouping_add_ses , grouping_flush_ses }
46 + , { "ewma" , 0, RRDR_GROUPING_SES , grouping_init_ses , grouping_reset_ses , grouping_free_ses , grouping_add_ses , grouping_flush_ses }
47 +
48 + // double exponential smoothing
49 + , { "des" , 0, RRDR_GROUPING_DES , grouping_init_des , grouping_reset_des , grouping_free_des , grouping_add_des , grouping_flush_des }
50 +
51 + // terminator
52 , { NULL , 0, RRDR_GROUPING_UNDEFINED , grouping_init_average , grouping_reset_average , grouping_free_average , grouping_add_average , grouping_flush_average }
53 };
54
@@ -431,7 +446,7 @@ RRDR *rrd2rrdr(
446 info("INTERNAL CHECK: %s: requested gtime %ld secs, is greater than the desired duration %ld secs", st->id, group_time_requested, duration);
447 #endif
448
434 - group = points_requested; // use all the points
449 + group = available_points; // use all the points
450 }
451 else {
452 // the points we should group to satisfy gtime
web/api/queries/query.h
+11 -9
@@ -4,15 +4,17 @@
4 #define NETDATA_API_DATA_QUERY_H
5
6 typedef enum rrdr_grouping {
7 - RRDR_GROUPING_UNDEFINED = 0,
8 - RRDR_GROUPING_AVERAGE = 1,
9 - RRDR_GROUPING_MIN = 2,
10 - RRDR_GROUPING_MAX = 3,
11 - RRDR_GROUPING_SUM = 4,
12 - RRDR_GROUPING_INCREMENTAL_SUM = 5,
13 - RRDR_GROUPING_MEDIAN = 6,
14 - RRDR_GROUPING_STDDEV = 7,
15 - RRDR_GROUPING_SES = 8,
7 + RRDR_GROUPING_UNDEFINED = 0,
8 + RRDR_GROUPING_AVERAGE,
9 + RRDR_GROUPING_MIN,
10 + RRDR_GROUPING_MAX,
11 + RRDR_GROUPING_SUM,
12 + RRDR_GROUPING_INCREMENTAL_SUM,
13 + RRDR_GROUPING_MEDIAN,
14 + RRDR_GROUPING_STDDEV,
15 + RRDR_GROUPING_CV,
16 + RRDR_GROUPING_SES,
17 + RRDR_GROUPING_DES,
18 } RRDR_GROUPING;
19
20 extern const char *group_method2string(RRDR_GROUPING group);
web/api/queries/ses/README.md
+45 -1
@@ -1 +1,45 @@
1 -# single exponential smoothing
1 +# Single (or Simple) Exponential Smoothing (`ses`)
2 +
3 +> This query is also available as `ema` and `ewma`.
4 +
5 +An exponential moving average (`ema`), also known as an exponentially weighted moving average (`ewma`)
6 +is a first-order infinite impulse response filter that applies weighting factors which decrease
7 +exponentially. The weighting for each older datum decreases exponentially, never reaching zero.
8 +
9 +In simple terms, this is like an average value, but more recent values are given more weight.
10 +
11 +Netdata automatically adjusts the weight based on the number of values processed, using the formula:
12 +
13 +```
14 +alpha = 2 / (number_of_values + 1)
15 +```
16 +
17 +## how to use
18 +
19 +Use it in alarms like this:
20 +
21 +```
22 + alarm: my_alarm
23 + on: my_chart
24 +lookup: ses -1m unaligned of my_dimension
25 + warn: $this > 1000
26 +```
27 +
28 +`ses` does not change the units. For example, if the chart units is `requests/sec`, the exponential
29 +moving average will be again expressed in the same units.
30 +
31 +It can also be used in APIs and badges as `&group=ses` in the URL.
32 +
33 +## Examples
34 +
35 +Examining last 1 minute `successful` web server responses:
36 +
37 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=min&after=-60&label=min)
38 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=average&after=-60&label=average&value_color=yellow)
39 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=ses&after=-60&label=single+exponential+smoothing&value_color=orange)
40 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=max&after=-60&label=max)
41 +
42 +## References
43 +
44 +- [https://en.wikipedia.org/wiki/Moving_average#exponential-moving-average](https://en.wikipedia.org/wiki/Moving_average#exponential-moving-average)
45 +- [https://en.wikipedia.org/wiki/Exponential_smoothing](https://en.wikipedia.org/wiki/Exponential_smoothing).
web/api/queries/ses/ses.c
+9 -19
@@ -8,15 +8,16 @@
8
9 struct grouping_ses {
10 calculated_number alpha;
11 - calculated_number alpha_older;
11 + calculated_number alpha_other;
12 calculated_number level;
13 size_t count;
14 - size_t has_data;
14 };
15
16 static inline void set_alpha(RRDR *r, struct grouping_ses *g) {
18 - g->alpha = 1.0 / r->group;
19 - g->alpha_older = 1 - g->alpha;
17 + // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
18 + // A commonly used value for alpha is 2 / (N + 1)
19 + g->alpha = 2.0 / ((calculated_number)r->group + 1.0);
20 + g->alpha_other = 1 - g->alpha;
21 }
22
23 void *grouping_init_ses(RRDR *r) {
@@ -32,7 +33,6 @@ void grouping_reset_ses(RRDR *r) {
33 struct grouping_ses *g = (struct grouping_ses *)r->grouping_data;
34 g->level = 0.0;
35 g->count = 0;
35 - g->has_data = 0;
36 }
37
38 void grouping_free_ses(RRDR *r) {
@@ -44,13 +44,10 @@ void grouping_add_ses(RRDR *r, calculated_number value) {
44 struct grouping_ses *g = (struct grouping_ses *)r->grouping_data;
45
46 if(isnormal(value)) {
47 - if(unlikely(!g->has_data)) {
47 + if(unlikely(!g->count))
48 g->level = value;
49 - g->has_data = 1;
50 - }
51 -
52 - g->level = g->alpha * value + g->alpha_older * g->level;
49
50 + g->level = g->alpha * value + g->alpha_other * g->level;
51 g->count++;
52 }
53 }
@@ -58,17 +55,10 @@ void grouping_add_ses(RRDR *r, calculated_number value) {
55 calculated_number grouping_flush_ses(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
56 struct grouping_ses *g = (struct grouping_ses *)r->grouping_data;
57
61 - calculated_number value;
62 -
58 if(unlikely(!g->count || !isnormal(g->level))) {
64 - value = 0.0;
59 *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
60 + return 0.0;
61 }
67 - else {
68 - value = g->level;
69 - }
70 -
71 - g->count = 0;
62
73 - return value;
63 + return g->level;
64 }
web/api/queries/stddev/README.md
+85
@@ -0,0 +1,85 @@
1 +
2 +# standard deviation (`stddev`)
3 +
4 +The standard deviation is a measure that is used to quantify the amount of variation or dispersion
5 +of a set of data values.
6 +
7 +A low standard deviation indicates that the data points tend to be close to the mean (also called the
8 +expected value) of the set, while a high standard deviation indicates that the data points are spread
9 +out over a wider range of values.
10 +
11 +## how to use
12 +
13 +Use it in alarms like this:
14 +
15 +```
16 + alarm: my_alarm
17 + on: my_chart
18 +lookup: stddev -1m unaligned of my_dimension
19 + warn: $this > 1000
20 +```
21 +
22 +`stdev` does not change the units. For example, if the chart units is `requests/sec`, the standard
23 +deviation will be again expressed in the same units.
24 +
25 +It can also be used in APIs and badges as `&group=stddev` in the URL.
26 +
27 +## Examples
28 +
29 +Examining last 1 minute `successful` web server responses:
30 +
31 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=min&after=-60&label=min)
32 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=average&after=-60&label=average&value_color=yellow)
33 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=stddev&after=-60&label=standard+deviation&value_color=orange)
34 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=max&after=-60&label=max)
35 +
36 +## References
37 +
38 +Check [https://en.wikipedia.org/wiki/Standard_deviation](https://en.wikipedia.org/wiki/Standard_deviation).
39 +
40 +---
41 +
42 +# Coefficient of variation (`cv`)
43 +
44 +The coefficient of variation (`cv``), also known as relative standard deviation (RSD),
45 +is a standardized measure of dispersion of a probability distribution or frequency distribution.
46 +
47 +It is defined as the ratio of the **standard deviation** to the **mean**.
48 +
49 +In simple terms, it gives the percentage of change. So, if the average value of a metric is 1000
50 +and its standard deviation is 100 (meaning that it variates from 900 to 1100), then `cv` is 10%.
51 +
52 +This is an easy way to check the % variation, without using absolute values.
53 +
54 +For example, you may trigger an alarm if your web server requests/sec `cv` is above 20 (`%`)
55 +over the last minute. So if your web server was serving 1000 reqs/sec over the last minute,
56 +it will trigger the alarm if had spikes below 800/sec or above 1200/sec.
57 +
58 +## how to use
59 +
60 +Use it in alarms like this:
61 +
62 +```
63 + alarm: my_alarm
64 + on: my_chart
65 +lookup: cv -1m unaligned of my_dimension
66 + units: %
67 + warn: $this > 20
68 +```
69 +
70 +The units reported by `cv` is always `%`.
71 +
72 +It can also be used in APIs and badges as `&group=cv` in the URL.
73 +
74 +## Examples
75 +
76 +Examining last 1 minute `successful` web server responses:
77 +
78 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=min&after=-60&label=min)
79 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=average&after=-60&label=average&value_color=yellow)
80 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=cv&after=-60&label=coefficient+of+variation&value_color=orange&units=pcent)
81 +- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=max&after=-60&label=max)
82 +
83 +## References
84 +
85 +Check [https://en.wikipedia.org/wiki/Coefficient_of_variation](https://en.wikipedia.org/wiki/Coefficient_of_variation).
web/api/queries/stddev/stddev.c
+114 -18
@@ -6,28 +6,26 @@
6 // ----------------------------------------------------------------------------
7 // stddev
8
9 -struct grouping_stddev {
10 - size_t series_size;
11 - size_t next_pos;
9 +// this implementation comes from:
10 +// https://www.johndcook.com/blog/standard_deviation/
11
13 - LONG_DOUBLE series[];
12 +struct grouping_stddev {
13 + long count;
14 + calculated_number m_oldM, m_newM, m_oldS, m_newS;
15 };
16
17 void *grouping_init_stddev(RRDR *r) {
18 long entries = (r->group > r->group_points) ? r->group : r->group_points;
19 if(entries < 0) entries = 0;
20
20 - struct grouping_stddev *g = (struct grouping_stddev *)callocz(1, sizeof(struct grouping_stddev) + entries * sizeof(LONG_DOUBLE));
21 - g->series_size = (size_t)entries;
22 -
23 - return g;
21 + return callocz(1, sizeof(struct grouping_stddev) + entries * sizeof(LONG_DOUBLE));
22 }
23
24 // resets when switches dimensions
25 // so, clear everything to restart
26 void grouping_reset_stddev(RRDR *r) {
27 struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
30 - g->next_pos = 0;
28 + g->count = 0;
29 }
30
31 void grouping_free_stddev(RRDR *r) {
@@ -37,37 +35,135 @@ void grouping_free_stddev(RRDR *r) {
35 void grouping_add_stddev(RRDR *r, calculated_number value) {
36 struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
37
40 - if(unlikely(g->next_pos >= g->series_size)) {
41 - error("INTERNAL ERROR: stddev buffer overflow on chart '%s' - next_pos = %zu, series_size = %zu, r->group = %ld, r->group_points = %ld.", r->st->name, g->next_pos, g->series_size, r->group, r->group_points);
38 + if(isnormal(value)) {
39 + g->count++;
40 +
41 + // See Knuth TAOCP vol 2, 3rd edition, page 232
42 + if (g->count == 1) {
43 + g->m_oldM = g->m_newM = value;
44 + g->m_oldS = 0.0;
45 + }
46 + else {
47 + g->m_newM = g->m_oldM + (value - g->m_oldM) / g->count;
48 + g->m_newS = g->m_oldS + (value - g->m_oldM) * (value - g->m_newM);
49 +
50 + // set up for next iteration
51 + g->m_oldM = g->m_newM;
52 + g->m_oldS = g->m_newS;
53 + }
54 + }
55 +}
56 +
57 +static inline calculated_number mean(struct grouping_stddev *g) {
58 + return (g->count > 0) ? g->m_newM : 0.0;
59 +}
60 +
61 +static inline calculated_number variance(struct grouping_stddev *g) {
62 + return ( (g->count > 1) ? g->m_newS/(g->count - 1) : 0.0 );
63 +}
64 +static inline calculated_number stddev(struct grouping_stddev *g) {
65 + return sqrtl(variance(g));
66 +}
67 +
68 +calculated_number grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
69 + struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
70 +
71 + calculated_number value;
72 +
73 + if(unlikely(!g->count)) {
74 + value = 0.0;
75 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
76 }
77 else {
44 - if(isnormal(value))
45 - g->series[g->next_pos++] = (LONG_DOUBLE)value;
78 + value = stddev(g);
79 +
80 + if(!isnormal(value)) {
81 + value = 0.0;
82 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
83 + }
84 }
85 +
86 + grouping_reset_stddev(r);
87 +
88 + return value;
89 }
90
49 -calculated_number grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
91 +// https://en.wikipedia.org/wiki/Coefficient_of_variation
92 +calculated_number grouping_flush_coefficient_of_variation(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
93 struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
94
95 calculated_number value;
96
54 - if(unlikely(!g->next_pos)) {
97 + if(unlikely(!g->count)) {
98 value = 0.0;
99 *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
100 }
101 else {
59 - value = standard_deviation(g->series, g->next_pos);
102 + calculated_number m = mean(g);
103 + value = 100.0 * stddev(g) / ((m < 0)? -m : m);
104
105 if(!isnormal(value)) {
106 value = 0.0;
107 *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
108 }
109 + }
110 +
111 + grouping_reset_stddev(r);
112 +
113 + return value;
114 +}
115 +
116 +
117 +/*
118 + * Mean = average
119 + *
120 +calculated_number grouping_flush_mean(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
121 + struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
122
66 - //log_series_to_stderr(g->series, g->next_pos, value, "stddev");
123 + calculated_number value;
124 +
125 + if(unlikely(!g->count)) {
126 + value = 0.0;
127 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
128 + }
129 + else {
130 + value = mean(g);
131 +
132 + if(!isnormal(value)) {
133 + value = 0.0;
134 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
135 + }
136 }
137
69 - g->next_pos = 0;
138 + grouping_reset_stddev(r);
139
140 return value;
141 }
142 + */
143 +
144 +/*
145 + * It is not advised to use this version of variance directly
146 + *
147 +calculated_number grouping_flush_variance(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
148 + struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
149 +
150 + calculated_number value;
151
152 + if(unlikely(!g->count)) {
153 + value = 0.0;
154 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
155 + }
156 + else {
157 + value = variance(g);
158 +
159 + if(!isnormal(value)) {
160 + value = 0.0;
161 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
162 + }
163 + }
164 +
165 + grouping_reset_stddev(r);
166 +
167 + return value;
168 +}
169 +*/
\ No newline at end of file
web/api/queries/stddev/stddev.h
+3
@@ -11,5 +11,8 @@ extern void grouping_reset_stddev(RRDR *r);
11 extern void grouping_free_stddev(RRDR *r);
12 extern void grouping_add_stddev(RRDR *r, calculated_number value);
13 extern calculated_number grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
14 +extern calculated_number grouping_flush_coefficient_of_variation(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
15 +// extern calculated_number grouping_flush_mean(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
16 +// extern calculated_number grouping_flush_variance(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
17
18 #endif //NETDATA_API_QUERIES_STDDEV_H