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
+- 
38
+- 
39
+- 
40
+- 
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
+- 
32
+- 
33
+- 
34
+- 
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
+- 
79
+- 
80
+- 
81
+- 
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