mirror of
https://github.com/netdata/netdata.git
synced 2025-04-13 17:19:11 +00:00
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
This commit is contained in:
parent
0a78758a11
commit
f857aa35ae
15 changed files with 430 additions and 54 deletions
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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} \
|
||||
|
|
8
web/api/queries/des/Makefile.am
Normal file
8
web/api/queries/des/Makefile.am
Normal file
|
@ -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)
|
1
web/api/queries/des/README.md
Normal file
1
web/api/queries/des/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
# double exponential smoothing
|
112
web/api/queries/des/des.c
Normal file
112
web/api/queries/des/des.c
Normal file
|
@ -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;
|
||||
}
|
15
web/api/queries/des/des.h
Normal file
15
web/api/queries/des/des.h
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
||||
## 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).
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
else {
|
||||
value = g->level;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
g->count = 0;
|
||||
|
||||
return value;
|
||||
return g->level;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
||||
## 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:
|
||||
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
||||
## References
|
||||
|
||||
Check [https://en.wikipedia.org/wiki/Coefficient_of_variation](https://en.wikipedia.org/wiki/Coefficient_of_variation).
|
|
@ -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);
|
||||
}
|
||||
else {
|
||||
if(isnormal(value))
|
||||
g->series[g->next_pos++] = (LONG_DOUBLE)value;
|
||||
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->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);
|
||||
value = stddev(g);
|
||||
|
||||
if(!isnormal(value)) {
|
||||
value = 0.0;
|
||||
*rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
|
||||
}
|
||||
|
||||
//log_series_to_stderr(g->series, g->next_pos, value, "stddev");
|
||||
}
|
||||
|
||||
g->next_pos = 0;
|
||||
grouping_reset_stddev(r);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// 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->count)) {
|
||||
value = 0.0;
|
||||
*rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
|
||||
}
|
||||
else {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
*/
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue