summaryrefslogtreecommitdiffstats
path: root/web/api/queries/median
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--web/api/queries/median/Makefile.am8
-rw-r--r--web/api/queries/median/README.md39
-rw-r--r--web/api/queries/median/median.c79
-rw-r--r--web/api/queries/median/median.h15
4 files changed, 141 insertions, 0 deletions
diff --git a/web/api/queries/median/Makefile.am b/web/api/queries/median/Makefile.am
new file mode 100644
index 0000000..19554be
--- /dev/null
+++ b/web/api/queries/median/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/median/README.md b/web/api/queries/median/README.md
new file mode 100644
index 0000000..72f51a2
--- /dev/null
+++ b/web/api/queries/median/README.md
@@ -0,0 +1,39 @@
+# Median
+
+The median is the value separating the higher half from the lower half of a data sample
+(a population or a probability distribution). For a data set, it may be thought of as the
+"middle" value.
+
+`median` is not an accurate average. However, it eliminates all spikes, by sorting
+all the values in a period, and selecting the value in the middle of the sorted array.
+
+## how to use
+
+Use it in alarms like this:
+
+```
+ alarm: my_alarm
+ on: my_chart
+lookup: median -1m unaligned of my_dimension
+ warn: $this > 1000
+```
+
+`median` does not change the units. For example, if the chart units is `requests/sec`, the result
+will be again expressed in the same units.
+
+It can also be used in APIs and badges as `&group=median` 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&options=unaligned&dimensions=success&group=min&after=-60&label=min)
+- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&options=unaligned&dimensions=success&group=average&after=-60&label=average)
+- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&options=unaligned&dimensions=success&group=median&after=-60&label=median&value_color=orange)
+- ![](https://registry.my-netdata.io/api/v1/badge.svg?chart=web_log_nginx.response_statuses&options=unaligned&dimensions=success&group=max&after=-60&label=max)
+
+## References
+
+- [https://en.wikipedia.org/wiki/Median](https://en.wikipedia.org/wiki/Median).
+
+[![analytics](https://www.google-analytics.com/collect?v=1&aip=1&t=pageview&_s=1&ds=github&dr=https%3A%2F%2Fgithub.com%2Fnetdata%2Fnetdata&dl=https%3A%2F%2Fmy-netdata.io%2Fgithub%2Fweb%2Fapi%2Fqueries%2Fmedian%2FREADME&_u=MAC~&cid=5792dfd7-8dc4-476b-af31-da2fdb9f93d2&tid=UA-64295674-3)]()
diff --git a/web/api/queries/median/median.c b/web/api/queries/median/median.c
new file mode 100644
index 0000000..5a13b2e
--- /dev/null
+++ b/web/api/queries/median/median.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "median.h"
+
+
+// ----------------------------------------------------------------------------
+// median
+
+struct grouping_median {
+ size_t series_size;
+ size_t next_pos;
+
+ LONG_DOUBLE series[];
+};
+
+void *grouping_create_median(RRDR *r) {
+ long entries = r->group;
+ if(entries < 0) entries = 0;
+
+ struct grouping_median *g = (struct grouping_median *)callocz(1, sizeof(struct grouping_median) + entries * sizeof(LONG_DOUBLE));
+ g->series_size = (size_t)entries;
+
+ return g;
+}
+
+// resets when switches dimensions
+// so, clear everything to restart
+void grouping_reset_median(RRDR *r) {
+ struct grouping_median *g = (struct grouping_median *)r->internal.grouping_data;
+ g->next_pos = 0;
+}
+
+void grouping_free_median(RRDR *r) {
+ freez(r->internal.grouping_data);
+ r->internal.grouping_data = NULL;
+}
+
+void grouping_add_median(RRDR *r, calculated_number value) {
+ struct grouping_median *g = (struct grouping_median *)r->internal.grouping_data;
+
+ if(unlikely(g->next_pos >= g->series_size)) {
+ error("INTERNAL ERROR: median buffer overflow on chart '%s' - next_pos = %zu, series_size = %zu, r->group = %ld.", r->st->name, g->next_pos, g->series_size, r->group);
+ }
+ else {
+ if(isnormal(value))
+ g->series[g->next_pos++] = (LONG_DOUBLE)value;
+ }
+}
+
+calculated_number grouping_flush_median(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
+ struct grouping_median *g = (struct grouping_median *)r->internal.grouping_data;
+
+ calculated_number value;
+
+ if(unlikely(!g->next_pos)) {
+ value = 0.0;
+ *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
+ }
+ else {
+ if(g->next_pos > 1) {
+ sort_series(g->series, g->next_pos);
+ value = (calculated_number)median_on_sorted_series(g->series, g->next_pos);
+ }
+ else
+ value = (calculated_number)g->series[0];
+
+ if(!isnormal(value)) {
+ value = 0.0;
+ *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
+ }
+
+ //log_series_to_stderr(g->series, g->next_pos, value, "median");
+ }
+
+ g->next_pos = 0;
+
+ return value;
+}
+
diff --git a/web/api/queries/median/median.h b/web/api/queries/median/median.h
new file mode 100644
index 0000000..dd2c1ff
--- /dev/null
+++ b/web/api/queries/median/median.h
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_API_QUERIES_MEDIAN_H
+#define NETDATA_API_QUERIES_MEDIAN_H
+
+#include "../query.h"
+#include "../rrdr.h"
+
+extern void *grouping_create_median(RRDR *r);
+extern void grouping_reset_median(RRDR *r);
+extern void grouping_free_median(RRDR *r);
+extern void grouping_add_median(RRDR *r, calculated_number value);
+extern calculated_number grouping_flush_median(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
+
+#endif //NETDATA_API_QUERIES_MEDIAN_H