summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xCMakeLists.txt2
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac1
-rwxr-xr-xcppcheck.sh4
-rw-r--r--web/api/queries/des/Makefile.am8
-rw-r--r--web/api/queries/des/README.md1
-rw-r--r--web/api/queries/des/des.c112
-rw-r--r--web/api/queries/des/des.h15
-rw-r--r--web/api/queries/query.c21
-rw-r--r--web/api/queries/query.h20
-rw-r--r--web/api/queries/ses/README.md46
-rw-r--r--web/api/queries/ses/ses.c28
-rw-r--r--web/api/queries/stddev/README.md85
-rw-r--r--web/api/queries/stddev/stddev.c132
-rw-r--r--web/api/queries/stddev/stddev.h3
15 files changed, 428 insertions, 52 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0914e8480a..80e759760a 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -393,6 +393,8 @@ set(API_PLUGIN_FILES
web/api/queries/stddev/stddev.h
web/api/queries/ses/ses.c
web/api/queries/ses/ses.h
+ web/api/queries/des/des.c
+ web/api/queries/des/des.h
)
set(STREAMING_PLUGIN_FILES
diff --git a/Makefile.am b/Makefile.am
index cbe05c5da8..45be88f5ad 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -291,6 +291,8 @@ API_PLUGIN_FILES = \
web/api/exporters/shell/allmetrics_shell.h \
web/api/queries/average/average.c \
web/api/queries/average/average.h \
+ web/api/queries/des/des.c \
+ web/api/queries/des/des.h \
web/api/queries/incremental_sum/incremental_sum.c \
web/api/queries/incremental_sum/incremental_sum.h \
web/api/queries/max/max.c \
diff --git a/configure.ac b/configure.ac
index e0313dcd91..474e5b7295 100644
--- a/configure.ac
+++ b/configure.ac
@@ -609,6 +609,7 @@ AC_CONFIG_FILES([
web/api/exporters/prometheus/Makefile
web/api/queries/Makefile
web/api/queries/average/Makefile
+ web/api/queries/des/Makefile
web/api/queries/incremental_sum/Makefile
web/api/queries/max/Makefile
web/api/queries/median/Makefile
diff --git a/cppcheck.sh b/cppcheck.sh
index aba93c269b..ebbeeaf8f9 100755
--- a/cppcheck.sh
+++ b/cppcheck.sh
@@ -12,14 +12,14 @@ processors=$(grep -c ^processor /proc/cpuinfo)
base="$(dirname "${0}")"
[ "${base}" = "." ] && base="${PWD}"
-cd "${base}/src" || exit 1
+cd "${base}" || exit 1
[ ! -d "cppcheck-build" ] && mkdir "cppcheck-build"
file="${1}"
shift
# shellcheck disable=SC2235
-([ "${file}" = "${base}" ] || [ -z "${file}" ]) && file="${base}/src"
+([ "${file}" = "${base}" ] || [ -z "${file}" ]) && file="${base}"
"${cppcheck}" \
-j ${processors} \
diff --git a/web/api/queries/des/Makefile.am b/web/api/queries/des/Makefile.am
new file mode 100644
index 0000000000..19554bed8e
--- /dev/null
+++ b/web/api/queries/des/Makefile.am
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+AUTOMAKE_OPTIONS = subdir-objects
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+dist_noinst_DATA = \
+ README.md \
+ $(NULL)
diff --git a/web/api/queries/des/README.md b/web/api/queries/des/README.md
new file mode 100644
index 0000000000..d2765ecc03
--- /dev/null
+++ b/web/api/queries/des/README.md
@@ -0,0 +1 @@
+# double exponential smoothing
diff --git a/web/api/queries/des/des.c b/web/api/queries/des/des.c
new file mode 100644
index 0000000000..bc9f6d81bb
--- /dev/null
+++ b/web/api/queries/des/des.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "des.h"
+
+
+// ----------------------------------------------------------------------------
+// single exponential smoothing
+
+struct grouping_des {
+ calculated_number alpha;
+ calculated_number alpha_other;
+ calculated_number beta;
+ calculated_number beta_other;
+
+ calculated_number level;
+ calculated_number trend;
+
+ size_t count;
+};
+
+#define MAX_WINDOW_SIZE 10
+
+static inline void set_alpha(RRDR *r, struct grouping_des *g) {
+ // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
+ // A commonly used value for alpha is 2 / (N + 1)
+ calculated_number window = (r->group > MAX_WINDOW_SIZE) ? MAX_WINDOW_SIZE : r->group;
+
+ g->alpha = 2.0 / ((calculated_number)window + 1.0);
+ g->alpha_other = 1.0 - g->alpha;
+
+ //info("alpha for chart '%s' is " CALCULATED_NUMBER_FORMAT, r->st->name, g->alpha);
+}
+
+static inline void set_beta(RRDR *r, struct grouping_des *g) {
+ // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
+ // A commonly used value for alpha is 2 / (N + 1)
+ calculated_number window = (r->group > MAX_WINDOW_SIZE) ? MAX_WINDOW_SIZE : r->group;
+
+ g->beta = 2.0 / ((calculated_number)window + 1.0);
+ g->beta_other = 1.0 - g->beta;
+
+ //info("beta for chart '%s' is " CALCULATED_NUMBER_FORMAT, r->st->name, g->beta);
+}
+
+void *grouping_init_des(RRDR *r) {
+ struct grouping_des *g = (struct grouping_des *)malloc(sizeof(struct grouping_des));
+ set_alpha(r, g);
+ set_beta(r, g);
+ g->level = 0.0;
+ g->trend = 0.0;
+ g->count = 0;
+ return g;
+}
+
+// resets when switches dimensions
+// so, clear everything to restart
+void grouping_reset_des(RRDR *r) {
+ struct grouping_des *g = (struct grouping_des *)r->grouping_data;
+ g->level = 0.0;
+ g->trend = 0.0;
+ g->count = 0;
+
+ // fprintf(stderr, "\nDES: ");
+
+}
+
+void grouping_free_des(RRDR *r) {
+ freez(r->grouping_data);
+ r->grouping_data = NULL;
+}
+
+void grouping_add_des(RRDR *r, calculated_number value) {
+ struct grouping_des *g = (struct grouping_des *)r->grouping_data;
+
+ if(isnormal(value)) {
+ if(likely(g->count > 0)) {
+ // we have at least a number so far
+
+ if(unlikely(g->count == 1)) {
+ // the second value we got
+ g->trend = value - g->trend;
+ g->level = value;
+ }
+
+ // for the values, except the first
+ calculated_number last_level = g->level;
+ g->level = (g->alpha * value) + (g->alpha_other * (g->level + g->trend));
+ g->trend = (g->beta * (g->level - last_level)) + (g->beta_other * g->trend);
+ }
+ else {
+ // the first value we got
+ g->level = g->trend = value;
+ }
+
+ g->count++;
+ }
+
+ //fprintf(stderr, "value: " CALCULATED_NUMBER_FORMAT ", level: " CALCULATED_NUMBER_FORMAT ", trend: " CALCULATED_NUMBER_FORMAT "\n", value, g->level, g->trend);
+}
+
+calculated_number grouping_flush_des(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
+ struct grouping_des *g = (struct grouping_des *)r->grouping_data;
+
+ if(unlikely(!g->count || !isnormal(g->level))) {
+ *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
+ return 0.0;
+ }
+
+ //fprintf(stderr, " RESULT for %zu values = " CALCULATED_NUMBER_FORMAT " \n", g->count, g->level);
+
+ return g->level;
+}
diff --git a/web/api/queries/des/des.h b/web/api/queries/des/des.h
new file mode 100644
index 0000000000..83edd7ef63
--- /dev/null
+++ b/web/api/queries/des/des.h
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_API_QUERIES_DES_H
+#define NETDATA_API_QUERIES_DES_H
+
+#include "../query.h"
+#include "../rrdr.h"
+
+extern void *grouping_init_des(RRDR *r);
+extern void grouping_reset_des(RRDR *r);
+extern void grouping_free_des(RRDR *r);
+extern void grouping_add_des(RRDR *r, calculated_number value);
+extern calculated_number grouping_flush_des(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
+
+#endif //NETDATA_API_QUERIES_DES_H
diff --git a/web/api/queries/query.c b/web/api/queries/query.c
index 85a06993c9..62d9084b14 100644
--- a/web/api/queries/query.c
+++ b/web/api/queries/query.c
@@ -12,6 +12,7 @@
#include "sum/sum.h"
#include "stddev/stddev.h"
#include "ses/ses.h"
+#include "des/des.h"
// ----------------------------------------------------------------------------
@@ -31,9 +32,23 @@ static struct {
, { "median" , 0, RRDR_GROUPING_MEDIAN , grouping_init_median , grouping_reset_median , grouping_free_median , grouping_add_median , grouping_flush_median }
, { "min" , 0, RRDR_GROUPING_MIN , grouping_init_min , grouping_reset_min , grouping_free_min , grouping_add_min , grouping_flush_min }
, { "max" , 0, RRDR_GROUPING_MAX , grouping_init_max , grouping_reset_max , grouping_free_max , grouping_add_max , grouping_flush_max }
- , { "ses" , 0, RRDR_GROUPING_SES , grouping_init_ses , grouping_reset_ses , grouping_free_ses , grouping_add_ses , grouping_flush_ses }
- , { "stddev" , 0, RRDR_GROUPING_STDDEV , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_stddev }
, { "sum" , 0, RRDR_GROUPING_SUM , grouping_init_sum , grouping_reset_sum , grouping_free_sum , grouping_add_sum , grouping_flush_sum }
+
+ // stddev module provides mean, variance and coefficient of variation
+ , { "stddev" , 0, RRDR_GROUPING_STDDEV , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_stddev }
+ , { "cv" , 0, RRDR_GROUPING_CV , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_coefficient_of_variation }
+ //, { "mean" , 0, RRDR_GROUPING_MEAN , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_mean }
+ //, { "variance" , 0, RRDR_GROUPING_VARIANCE , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_variance }
+
+ // single exponential smoothing or exponential weighted moving average
+ , { "ses" , 0, RRDR_GROUPING_SES , grouping_init_ses , grouping_reset_ses , grouping_free_ses , grouping_add_ses , grouping_flush_ses }
+ , { "ema" , 0, RRDR_GROUPING_SES , grouping_init_ses , grouping_reset_ses , grouping_free_ses , grouping_add_ses , grouping_flush_ses }
+ , { "ewma" , 0, RRDR_GROUPING_SES , grouping_init_ses , grouping_reset_ses , grouping_free_ses , grouping_add_ses , grouping_flush_ses }
+
+ // double exponential smoothing
+ , { "des" , 0, RRDR_GROUPING_DES , grouping_init_des , grouping_reset_des , grouping_free_des , grouping_add_des , grouping_flush_des }
+
+ // terminator
, { NULL , 0, RRDR_GROUPING_UNDEFINED , grouping_init_average , grouping_reset_average , grouping_free_average , grouping_add_average , grouping_flush_average }
};
@@ -431,7 +446,7 @@ RRDR *rrd2rrdr(
info("INTERNAL CHECK: %s: requested gtime %ld secs, is greater than the desired duration %ld secs", st->id, group_time_requested, duration);
#endif
- group = points_requested; // use all the points
+ group = available_points; // use all the points
}
else {
// the points we should group to satisfy gtime
diff --git a/web/api/queries/query.h b/web/api/queries/query.h
index c4cbe347c6..6b8a51c583 100644
--- a/web/api/queries/query.h
+++ b/web/api/queries/query.h
@@ -4,15 +4,17 @@
#define NETDATA_API_DATA_QUERY_H
typedef enum rrdr_grouping {
- RRDR_GROUPING_UNDEFINED = 0,
- RRDR_GROUPING_AVERAGE = 1,
- RRDR_GROUPING_MIN = 2,
- RRDR_GROUPING_MAX = 3,
- RRDR_GROUPING_SUM = 4,
- RRDR_GROUPING_INCREMENTAL_SUM = 5,
- RRDR_GROUPING_MEDIAN = 6,
- RRDR_GROUPING_STDDEV = 7,
- RRDR_GROUPING_SES = 8,
+ RRDR_GROUPING_UNDEFINED = 0,
+ RRDR_GROUPING_AVERAGE,
+ RRDR_GROUPING_MIN,
+ RRDR_GROUPING_MAX,
+ RRDR_GROUPING_SUM,
+ RRDR_GROUPING_INCREMENTAL_SUM,
+ RRDR_GROUPING_MEDIAN,
+ RRDR_GROUPING_STDDEV,
+ RRDR_GROUPING_CV,
+ RRDR_GROUPING_SES,
+ RRDR_GROUPING_DES,
} RRDR_GROUPING;
extern const char *group_method2string(RRDR_GROUPING group);
diff --git a/web/api/queries/ses/README.md b/web/api/queries/ses/README.md
index 740b18e443..3e8be70c98 100644
--- a/web/api/queries/ses/README.md
+++ b/web/api/queries/ses/README.md
@@ -1 +1,45 @@
-# single exponential smoothing
+# Single (or Simple) Exponential Smoothing (`ses`)
+
+> This query is also available as `ema` and `ewma`.
+
+An exponential moving average (`ema`), also known as an exponentially weighted moving average (`ewma`)
+is a first-order infinite impulse response filter that applies weighting factors which decrease
+exponentially. The weighting for each older datum decreases exponentially, never reaching zero.
+
+In simple terms, this is like an average value, but more recent values are given more weight.
+
+Netdata automatically adjusts the weight based on the number of values processed, using the formula:
+
+```
+alpha = 2 / (number_of_values + 1)
+```
+
+## how to use
+
+Use it in alarms like this:
+
+```
+ alarm: my_alarm
+ on: my_chart
+lookup: ses -1m unaligned of my_dimension
+ warn: $this > 1000
+```
+
+`ses` does not change the units. For example, if the chart units is `requests/sec`, the exponential
+moving average will be again expressed in the same units.
+
+It can also be used in APIs and badges as `&group=ses` in the URL.
+
+## Examples
+
+Examining last 1 minute `successful` web server responses:
+
+- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=min&after=-60&label=min)
+- ![](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)
+- ![](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)
+- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=max&after=-60&label=max)
+
+## References
+
+- [https://en.wikipedia.org/wiki/Moving_average#exponential-moving-average](https://en.wikipedia.org/wiki/Moving_average#exponential-moving-average)
+- [https://en.wikipedia.org/wiki/Exponential_smoothing](https://en.wikipedia.org/wiki/Exponential_smoothing).
diff --git a/web/api/queries/ses/ses.c b/web/api/queries/ses/ses.c
index 7a46e1b55c..04df738f0d 100644
--- a/web/api/queries/ses/ses.c
+++ b/web/api/queries/ses/ses.c
@@ -8,15 +8,16 @@
struct grouping_ses {
calculated_number alpha;
- calculated_number alpha_older;
+ calculated_number alpha_other;
calculated_number level;
size_t count;
- size_t has_data;
};
static inline void set_alpha(RRDR *r, struct grouping_ses *g) {
- g->alpha = 1.0 / r->group;
- g->alpha_older = 1 - g->alpha;
+ // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
+ // A commonly used value for alpha is 2 / (N + 1)
+ g->alpha = 2.0 / ((calculated_number)r->group + 1.0);
+ g->alpha_other = 1 - g->alpha;
}
void *grouping_init_ses(RRDR *r) {
@@ -32,7 +33,6 @@ void grouping_reset_ses(RRDR *r) {
struct grouping_ses *g = (struct grouping_ses *)r->grouping_data;
g->level = 0.0;
g->count = 0;
- g->has_data = 0;
}
void grouping_free_ses(RRDR *r) {
@@ -44,13 +44,10 @@ void grouping_add_ses(RRDR *r, calculated_number value) {
struct grouping_ses *g = (struct grouping_ses *)r->grouping_data;
if(isnormal(value)) {
- if(unlikely(!g->has_data)) {
+ if(unlikely(!g->count))
g->level = value;
- g->has_data = 1;
- }
-
- g->level = g->alpha * value + g->alpha_older * g->level;
+ g->level = g->alpha * value + g->alpha_other * g->level;
g->count++;
}
}
@@ -58,17 +55,10 @@ void grouping_add_ses(RRDR *r, calculated_number value) {
calculated_number grouping_flush_ses(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
struct grouping_ses *g = (struct grouping_ses *)r->grouping_data;
- calculated_number value;
-
if(unlikely(!g->count || !isnormal(g->level))) {
- value = 0.0;
*rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
+ return 0.0;
}
- else {
- value = g->level;
- }
-
- g->count = 0;
- return value;
+ return g->level;
}
diff --git a/web/api/queries/stddev/README.md b/web/api/queries/stddev/README.md
index e69de29bb2..a404b49025 100644
--- a/web/api/queries/stddev/README.md
+++ b/web/api/queries/stddev/README.md
@@ -0,0 +1,85 @@
+
+# standard deviation (`stddev`)
+
+The standard deviation is a measure that is used to quantify the amount of variation or dispersion
+of a set of data values.
+
+A low standard deviation indicates that the data points tend to be close to the mean (also called the
+expected value) of the set, while a high standard deviation indicates that the data points are spread
+out over a wider range of values.
+
+## how to use
+
+Use it in alarms like this:
+
+```
+ alarm: my_alarm
+ on: my_chart
+lookup: stddev -1m unaligned of my_dimension
+ warn: $this > 1000
+```
+
+`stdev` does not change the units. For example, if the chart units is `requests/sec`, the standard
+deviation will be again expressed in the same units.
+
+It can also be used in APIs and badges as `&group=stddev` in the URL.
+
+## Examples
+
+Examining last 1 minute `successful` web server responses:
+
+- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=min&after=-60&label=min)
+- ![](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)
+- ![](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)
+- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=max&after=-60&label=max)
+
+## References
+
+Check [https://en.wikipedia.org/wiki/Standard_deviation](https://en.wikipedia.org/wiki/Standard_deviation).
+
+---
+
+# Coefficient of variation (`cv`)
+
+The coefficient of variation (`cv``), also known as relative standard deviation (RSD),
+is a standardized measure of dispersion of a probability distribution or frequency distribution.
+
+It is defined as the ratio of the **standard deviation** to the **mean**.
+
+In simple terms, it gives the percentage of change. So, if the average value of a metric is 1000
+and its standard deviation is 100 (meaning that it variates from 900 to 1100), then `cv` is 10%.
+
+This is an easy way to check the % variation, without using absolute values.
+
+For example, you may trigger an alarm if your web server requests/sec `cv` is above 20 (`%`)
+over the last minute. So if your web server was serving 1000 reqs/sec over the last minute,
+it will trigger the alarm if had spikes below 800/sec or above 1200/sec.
+
+## how to use
+
+Use it in alarms like this:
+
+```
+ alarm: my_alarm
+ on: my_chart
+lookup: cv -1m unaligned of my_dimension
+ units: %
+ warn: $this > 20
+```
+
+The units reported by `cv` is always `%`.
+
+It can also be used in APIs and badges as `&group=cv` in the URL.
+
+## Examples
+
+Examining last 1 minute `successful` web server responses:
+
+- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=min&after=-60&label=min)
+- ![](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)
+- ![](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)
+- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&dimensions=success&group=max&after=-60&label=max)
+
+## References
+
+Check [https://en.wikipedia.org/wiki/Coefficient_of_variation](https://en.wikipedia.org/wiki/Coefficient_of_variation).
diff --git a/web/api/queries/stddev/stddev.c b/web/api/queries/stddev/stddev.c
index e88a853740..d1e6bb47eb 100644
--- a/web/api/queries/stddev/stddev.c
+++ b/web/api/queries/stddev/stddev.c
@@ -6,28 +6,26 @@
// ----------------------------------------------------------------------------
// stddev
-struct grouping_stddev {
- size_t series_size;
- size_t next_pos;
+// this implementation comes from:
+// https://www.johndcook.com/blog/standard_deviation/
- LONG_DOUBLE series[];
+struct grouping_stddev {
+ long count;
+ calculated_number m_oldM, m_newM, m_oldS, m_newS;
};
void *grouping_init_stddev(RRDR *r) {
long entries = (r->group > r->group_points) ? r->group : r->group_points;
if(entries < 0) entries = 0;
- struct grouping_stddev *g = (struct grouping_stddev *)callocz(1, sizeof(struct grouping_stddev) + entries * sizeof(LONG_DOUBLE));
- g->series_size = (size_t)entries;
-
- return g;
+ return callocz(1, sizeof(struct grouping_stddev) + entries * sizeof(LONG_DOUBLE));
}
// resets when switches dimensions
// so, clear everything to restart
void grouping_reset_stddev(RRDR *r) {
struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
- g->next_pos = 0;
+ g->count = 0;
}
void grouping_free_stddev(RRDR *r) {
@@ -37,37 +35,135 @@ void grouping_free_stddev(RRDR *r) {
void grouping_add_stddev(RRDR *r, calculated_number value) {
struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
- if(unlikely(g->next_pos >= g->series_size)) {
- 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);
+ if(isnormal(value)) {
+ g->count++;
+
+ // See Knuth TAOCP vol 2, 3rd edition, page 232
+ if (g->count == 1) {
+ g->m_oldM = g->m_newM = value;
+ g->m_oldS = 0.0;
+ }
+ else {
+ g->m_newM = g->m_oldM + (value - g->m_oldM) / g->count;
+ g->m_newS = g->m_oldS + (value - g->m_oldM) * (value - g->m_newM);
+
+ // set up for next iteration
+ g->m_oldM = g->m_newM;
+ g->m_oldS = g->m_newS;
+ }
+ }
+}
+
+static inline calculated_number mean(struct grouping_stddev *g) {
+ return (g->count > 0) ? g->m_newM : 0.0;
+}
+
+static inline calculated_number variance(struct grouping_stddev *g) {
+ return ( (g->count > 1) ? g->m_newS/(g->count - 1) : 0.0 );
+}
+static inline calculated_number stddev(struct grouping_stddev *g) {
+ return sqrtl(variance(g));
+}
+
+calculated_number grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
+ struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
+
+ calculated_number value;
+
+ if(unlikely(!g->count)) {
+ value = 0.0;
+ *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
}
else {
- if(isnormal(value))
- g->series[g->next_pos++] = (LONG_DOUBLE)value;
+ value = stddev(g);
+
+ if(!isnormal(value)) {
+ value = 0.0;
+ *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
+ }
}
+
+ grouping_reset_stddev(r);
+
+ return value;
}
-calculated_number grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
+// https://en.wikipedia.org/wiki/Coefficient_of_variation
+calculated_number grouping_flush_coefficient_of_variation(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
calculated_number value;
- if(unlikely(!g->next_pos)) {
+ if(unlikely(!g->count)) {
value = 0.0;
*rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
}
else {
- value = standard_deviation(g->series, g->next_pos);
+ calculated_number m = mean(g);
+ value = 100.0 * stddev(g) / ((m < 0)? -m : m);
if(!isnormal(value)) {
value = 0.0;
*rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
}
+ }
+
+ grouping_reset_stddev(r);
+
+ return value;
+}
+
+
+/*
+ * Mean = average
+ *
+calculated_number grouping_flush_mean(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
+ struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
- //log_series_to_stderr(g->series, g->next_pos, value, "stddev");
+ calculated_number value;
+
+ if(unlikely(!g->count)) {
+ value = 0.0;
+ *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
+ }
+ else {
+ value = mean(g);
+
+ if(!isnormal(value)) {
+ value = 0.0;
+ *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
+ }
}
- g->next_pos = 0;
+ grouping_reset_stddev(r);
return value;
}
+ */
+
+/*
+ * It is not advised to use this version of variance directly
+ *
+calculated_number grouping_flush_variance(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
+ struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
+
+ calculated_number value;
+ if(unlikely(!g->count)) {
+ value = 0.0;
+ *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
+ }
+ else {
+ value = variance(g);
+
+ if(!isnormal(value)) {
+ value = 0.0;
+ *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
+ }
+ }
+
+ grouping_reset_stddev(r);
+
+ return value;
+}
+*/ \ No newline at end of file
diff --git a/web/api/queries/stddev/stddev.h b/web/api/queries/stddev/stddev.h
index 476fca6da2..c6279324b9 100644
--- a/web/api/queries/stddev/stddev.h
+++ b/web/api/queries/stddev/stddev.h
@@ -11,5 +11,8 @@ extern void grouping_reset_stddev(RRDR *r);
extern void grouping_free_stddev(RRDR *r);
extern void grouping_add_stddev(RRDR *r, calculated_number value);
extern calculated_number grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
+extern calculated_number grouping_flush_coefficient_of_variation(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
+// extern calculated_number grouping_flush_mean(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
+// extern calculated_number grouping_flush_variance(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
#endif //NETDATA_API_QUERIES_STDDEV_H