aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuca Deri <deri@ntop.org>2021-03-11 00:04:33 +0100
committerLuca Deri <deri@ntop.org>2021-03-11 00:04:33 +0100
commit6833ee2bbec4c2de2489a2091f6c2d1ce0b7b558 (patch)
tree4a4f04e0e282482b6e45e58ad5737173db470612
parent5b7fe1360a1ed13c9cf6e1f789f0c1a20b175bd8 (diff)
Added single exponential smoothing API
int ndpi_ses_init(struct ndpi_ses_struct *ses, double alpha, float significance); int ndpi_ses_add_value(struct ndpi_ses_struct *ses, const u_int32_t _value, double *forecast, double *confidence_band);
-rw-r--r--example/ndpiReader.c152
-rw-r--r--src/include/ndpi_api.h.in5
-rw-r--r--src/include/ndpi_typedefs.h11
-rw-r--r--src/lib/ndpi_analyze.c96
4 files changed, 235 insertions, 29 deletions
diff --git a/example/ndpiReader.c b/example/ndpiReader.c
index aec78d778..3e9c54f8d 100644
--- a/example/ndpiReader.c
+++ b/example/ndpiReader.c
@@ -3722,20 +3722,53 @@ void rulesUnitTest() {
void rsiUnitTest() {
struct ndpi_rsi_struct s;
unsigned int v[] = {
+ 31,
+ 87,
+ 173,
+ 213,
+ 223,
+ 230,
+ 238,
+ 245,
+ 251,
+ 151,
+ 259,
+ 261,
+ 264,
+ 264,
+ 270,
+ 273,
+ 288,
+ 288,
+ 304,
+ 304,
+ 350,
+ 384,
+ 423,
+ 439,
+ 445,
+ 445,
+ 445,
+ 445
+ };
+
+ unsigned int v1[] = {
+
2227, 2219, 2208, 2217, 2218, 2213, 2223, 2243, 2224, 2229,
2215, 2239, 2238, 2261, 2336, 2405, 2375, 2383, 2395, 2363,
2382, 2387, 2365, 2319, 2310, 2333, 2268, 2310, 2240, 2217,
};
u_int i, n = sizeof(v) / sizeof(unsigned int);
-
+ u_int debug = 0;
+
assert(ndpi_alloc_rsi(&s, 8) == 0);
for(i=0; i<n; i++) {
float rsi = ndpi_rsi_add_value(&s, v[i]);
-#if 0
- printf("%2d) RSI = %f\n", i, rsi);
-#endif
+
+ if(debug)
+ printf("%2d) RSI = %f\n", i, rsi);
}
ndpi_free_rsi(&s);
@@ -3796,7 +3829,6 @@ void hwUnitTest() {
void hwUnitTest2() {
struct ndpi_hw_struct hw;
- u_int num_learning_points = 12;
u_int8_t trace = 1;
double v[] = {
31.908466339111,
@@ -3814,7 +3846,7 @@ void hwUnitTest2() {
264.78540039062,
264.78540039062,
270.47451782227,
- 273.3671875,
+ 173.3671875,
288.34222412109,
288.34222412109,
304.24795532227,
@@ -3828,13 +3860,20 @@ void hwUnitTest2() {
445.05981445312,
445.05981445312
};
+ u_int num_learning_points = 1;
u_int i, num = sizeof(v) / sizeof(double);
- float alpha = 0.5, beta = 0.5, gamma = 0.1;
-
- assert(ndpi_hw_init(&hw, num_learning_points, 0 /* 0=multiplicative, 1=additive */, alpha, beta, gamma, 0.05) == 0);
+ float alpha = 0.9, beta = 0.5, gamma = 1;
+ FILE *fd = fopen("/tmp/result.csv", "w");
- if(trace)
+ assert(ndpi_hw_init(&hw, num_learning_points, 0 /* 0=multiplicative, 1=additive */,
+ alpha, beta, gamma, 0.05) == 0);
+
+ if(trace) {
printf("\nHolt-Winters [alpha: %.1f][beta: %.1f][gamma: %.1f]\n", alpha, beta, gamma);
+
+ if(fd)
+ fprintf(fd, "index;value;prediction;lower;upper;anomaly\n");
+ }
for(i=0; i<num; i++) {
double prediction, confidence_band;
@@ -3843,13 +3882,94 @@ void hwUnitTest2() {
lower = prediction - confidence_band, upper = prediction + confidence_band;
- if(trace)
+ if(trace) {
printf("%2u)\t%12.3f\t%.3f\t%12.3f\t%12.3f\t %s [%.3f]\n", i, v[i], prediction, lower, upper,
((rc == 0) || ((v[i] >= lower) && (v[i] <= upper))) ? "OK" : "ANOMALY",
confidence_band);
+
+ if(fd)
+ fprintf(fd, "%u;%.0f;%.0f;%.0f;%.0f;%s\n",
+ i, v[i], prediction, lower, upper,
+ ((rc == 0) || ((v[i] >= lower) && (v[i] <= upper))) ? "OK" : "ANOMALY");
+ }
}
-
+
+ if(fd) fclose(fd);
+
ndpi_hw_free(&hw);
+
+ //exit(0);
+}
+
+/* *********************************************** */
+
+void sesUnitTest() {
+ struct ndpi_ses_struct ses;
+ u_int8_t trace = 0;
+ double v[] = {
+ 31.908466339111,
+ 87.339714050293,
+ 173.47660827637,
+ 213.92568969727,
+ 223.32124328613,
+ 230.60134887695,
+ 238.09457397461,
+ 245.8137512207,
+ 251.09228515625,
+ 251.09228515625,
+ 259.21997070312,
+ 261.98754882812,
+ 264.78540039062,
+ 264.78540039062,
+ 270.47451782227,
+ 173.3671875,
+ 288.34222412109,
+ 288.34222412109,
+ 304.24795532227,
+ 304.24795532227,
+ 350.92227172852,
+ 384.54431152344,
+ 423.25942993164,
+ 439.43322753906,
+ 445.05981445312,
+ 445.05981445312,
+ 445.05981445312,
+ 445.05981445312
+ };
+ u_int num_learning_points = 1;
+ u_int i, num = sizeof(v) / sizeof(double);
+ float alpha = 0.9;
+ FILE *fd = fopen("/tmp/result.csv", "w");
+
+ assert(ndpi_ses_init(&ses, alpha, 0.05) == 0);
+
+ if(trace) {
+ printf("\nSingle Exponential Smoothing [alpha: %.1f]\n", alpha);
+
+ if(fd)
+ fprintf(fd, "index;value;prediction;lower;upper;anomaly\n");
+ }
+
+ for(i=0; i<num; i++) {
+ double prediction, confidence_band;
+ double lower, upper;
+ int rc = ndpi_ses_add_value(&ses, v[i], &prediction, &confidence_band);
+
+ lower = prediction - confidence_band, upper = prediction + confidence_band;
+
+ if(trace) {
+ printf("%2u)\t%12.3f\t%.3f\t%12.3f\t%12.3f\t %s [%.3f]\n", i, v[i], prediction, lower, upper,
+ ((rc == 0) || ((v[i] >= lower) && (v[i] <= upper))) ? "OK" : "ANOMALY",
+ confidence_band);
+
+ if(fd)
+ fprintf(fd, "%u;%.0f;%.0f;%.0f;%.0f;%s\n",
+ i, v[i], prediction, lower, upper,
+ ((rc == 0) || ((v[i] >= lower) && (v[i] <= upper))) ? "OK" : "ANOMALY");
+ }
+ }
+
+ if(fd) fclose(fd);
}
/* *********************************************** */
@@ -3938,8 +4058,12 @@ int original_main(int argc, char **argv) {
return(-1);
}
- // hwUnitTest2();
-
+#ifdef HW_TEST
+ hwUnitTest2();
+#endif
+
+ sesUnitTest();
+
/* Internal checks */
// binUnitTest();
//hwUnitTest();
diff --git a/src/include/ndpi_api.h.in b/src/include/ndpi_api.h.in
index c12910609..cb5e45931 100644
--- a/src/include/ndpi_api.h.in
+++ b/src/include/ndpi_api.h.in
@@ -1474,6 +1474,11 @@ extern "C" {
/* ******************************* */
+ int ndpi_ses_init(struct ndpi_ses_struct *ses, double alpha, float significance);
+ int ndpi_ses_add_value(struct ndpi_ses_struct *ses, const u_int32_t _value, double *forecast, double *confidence_band);
+
+ /* ******************************* */
+
int ndpi_jitter_init(struct ndpi_jitter_struct *hw, u_int16_t num_periods);
void ndpi_jitter_free(struct ndpi_jitter_struct *hw);
float ndpi_jitter_add_value(struct ndpi_jitter_struct *s, const float value);
diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h
index 35b3b95dd..de9a404c8 100644
--- a/src/include/ndpi_typedefs.h
+++ b/src/include/ndpi_typedefs.h
@@ -1663,6 +1663,17 @@ struct ndpi_hw_struct {
double *s;
};
+struct ndpi_ses_struct {
+ struct {
+ double alpha, ro;
+ } params;
+
+ u_int32_t num_values;
+ double sum_square_error, last_forecast, last_value;
+};
+
+/* **************************************** */
+
/* Prototype used to define custom DGA detection function */
typedef int (*ndpi_custom_dga_predict_fctn)(const char* domain, int domain_length);
diff --git a/src/lib/ndpi_analyze.c b/src/lib/ndpi_analyze.c
index 3856e0f5f..19938b14f 100644
--- a/src/lib/ndpi_analyze.c
+++ b/src/lib/ndpi_analyze.c
@@ -852,6 +852,8 @@ void ndpi_free_rsi(struct ndpi_rsi_struct *s) {
/* ************************************* */
+// #define DEBUG_RSI
+
/*
This function adds a new value and returns the computed RSI, or -1
if there are too few points (< num_learning_values)
@@ -941,6 +943,7 @@ double ndpi_avg_inline(u_int32_t *v, u_int num) {
}
/* *********************************************************** */
+/* *********************************************************** */
/*
Initializes Holt-Winters with Confidence Interval
@@ -951,7 +954,7 @@ double ndpi_avg_inline(u_int32_t *v, u_int num) {
additive If set to 1 will use the Holt-Winters additive seasonal (should be the default), otherwise the multiplicative seasonal.
alpha Level: specifies the coefficient for the level smoothing. Range 0..1. The higher α, the faster the method forgets old values
beta Trend: specifies the coefficient for the trend smoothing. Range 0..1.
- gamma Seasonal: specifies the coefficient for the seasonal smoothing. Range 0..1. With gamma = 0, seaasonal correction is not used.
+ gamma Seasonal: specifies the coefficient for the seasonal smoothing. Range 0..1. With gamma = 0, seasonal correction is not used.
significance Significance level for the forecats sed for computing lower and upper bands. Range 0..1. Typical values 0.05 or less.
See https://en.wikipedia.org/wiki/Statistical_significance
@@ -978,8 +981,6 @@ int ndpi_hw_init(struct ndpi_hw_struct *hw,
if((significance < 0) || (significance > 1)) significance = 0.05;
hw->params.ro = ndpi_normal_cdf_inverse(1 - (significance / 2.));
- hw->params.ro = 1.95996398454005;
-
if((hw->y = (u_int32_t*)ndpi_calloc(hw->params.num_season_periods, sizeof(u_int32_t))) == NULL)
return(-1);
@@ -1028,13 +1029,13 @@ int ndpi_hw_add_value(struct ndpi_hw_struct *hw, const u_int32_t _value, double
u_int idx = hw->num_values % hw->params.num_season_periods;
double prev_u, prev_v, prev_s, value = (double)_value;
double sq, error;
-
+
if(hw->num_values == hw->params.num_season_periods) {
double avg = ndpi_avg_inline(hw->y, hw->params.num_season_periods);
u_int i;
if(avg == 0) avg = 1; /* Avoid divisions by zero */
-
+
for(i=0; i<hw->params.num_season_periods; i++)
hw->s[i] = hw->y[i] / avg;
@@ -1043,7 +1044,7 @@ int ndpi_hw_add_value(struct ndpi_hw_struct *hw, const u_int32_t _value, double
hw->u = 0;
else
hw->u = _value / hw->s[i];
-
+
hw->v = 0;
ndpi_free(hw->y);
hw->y = NULL;
@@ -1051,12 +1052,12 @@ int ndpi_hw_add_value(struct ndpi_hw_struct *hw, const u_int32_t _value, double
idx = hw->num_values % hw->params.num_season_periods;
prev_u = hw->u, prev_v = hw->v, prev_s = hw->s[idx];
-
+
if(prev_s != 0)
hw->u = ((hw->params.alpha * value) / prev_s) + ( 1 - hw->params.alpha) * (hw->u + hw->v);
else
hw->u = 0; /* Avoid divisions by zero */
-
+
hw->v = (hw->params.beta * (hw->u - prev_u)) + ((1 - hw->params.beta ) * hw->v);
if(hw->u != 0)
@@ -1080,7 +1081,7 @@ int ndpi_hw_add_value(struct ndpi_hw_struct *hw, const u_int32_t _value, double
*forecast, hw->sum_square_error,
sq, *confidence_band);
#endif
-
+
hw->num_values++, idx = (idx + 1) % hw->params.num_season_periods;
return(1); /* We're in business: forecast is meaningful now */
@@ -1092,7 +1093,7 @@ int ndpi_hw_add_value(struct ndpi_hw_struct *hw, const u_int32_t _value, double
/*
Jitter calculator
-
+
Used to determine how noisy is a signal
*/
@@ -1100,7 +1101,7 @@ int ndpi_jitter_init(struct ndpi_jitter_struct *s, u_int16_t num_learning_values
memset(s, 0, sizeof(struct ndpi_jitter_struct));
if(num_learning_values < 2) num_learning_values = 2;
-
+
s->empty = 1, s->num_values = num_learning_values;
s->observations = (float*)ndpi_calloc(num_learning_values, sizeof(float));
@@ -1108,7 +1109,7 @@ int ndpi_jitter_init(struct ndpi_jitter_struct *s, u_int16_t num_learning_values
s->last_value = 0;
return(0);
} else
- return(-1);
+ return(-1);
}
/* ************************************* */
@@ -1132,7 +1133,7 @@ float ndpi_jitter_add_value(struct ndpi_jitter_struct *s, const float value) {
s->observations[s->next_index] = val;
s->jitter_total += val;
}
-
+
s->last_value = value, s->next_index = (s->next_index + 1) % s->num_values;
if(s->next_index == 0) s->jitter_ready = 1; /* We have completed one round */
@@ -1141,10 +1142,75 @@ float ndpi_jitter_add_value(struct ndpi_jitter_struct *s, const float value) {
value, val, s->jitter_total,
s->jitter_ready ? (s->jitter_total / s->num_values) : -1);
#endif
-
+
if(!s->jitter_ready)
return(-1); /* Too early */
- else
+ else
return(s->jitter_total / s->num_values);
}
+
+/* *********************************************************** */
+/* *********************************************************** */
+
+/*
+ Single Exponential Smoothing
+*/
+
+int ndpi_ses_init(struct ndpi_ses_struct *ses, double alpha, float significance) {
+ memset(ses, 0, sizeof(struct ndpi_ses_struct));
+
+ ses->params.alpha = alpha;
+
+ if((significance < 0) || (significance > 1)) significance = 0.05;
+ ses->params.ro = ndpi_normal_cdf_inverse(1 - (significance / 2.));
+
+ return(0);
+}
+
+/* *********************************************************** */
+
+/*
+ Returns the forecast and the band (forecast +/- band are the upper and lower values)
+
+ Input
+ ses: Datastructure previously initialized
+ value The value to add to the measurement
+
+ Output
+ forecast The forecasted value
+ confidence_band The value +/- on which the value should fall is not an anomaly
+
+ Return code
+ 0 Too early: we're still in the learning phase. Output values are zero.
+ 1 Normal processing: forecast and confidence_band are meaningful
+*/
+int ndpi_ses_add_value(struct ndpi_ses_struct *ses, const u_int32_t _value, double *forecast, double *confidence_band) {
+ double value = (double)_value, error;
+ int rc;
+
+ if(ses->num_values == 0)
+ *forecast = value;
+ else
+ *forecast = (ses->params.alpha * (ses->last_value - ses->last_forecast)) + ses->last_forecast;
+
+ error = value - *forecast;
+ ses->sum_square_error += error * error;
+
+ if(ses->num_values > 0) {
+ double sq = sqrt(ses->sum_square_error / (ses->num_values+1));
+
+ *confidence_band = ses->params.ro * sq;
+ rc = 1;
+ } else
+ *confidence_band = 0, rc = 0;
+
+ ses->num_values++, ses->last_value = value, ses->last_forecast = *forecast;
+
+#ifdef SES_DEBUG
+ printf("[num_values: %u][[error: %.3f][forecast: %.3f][sqe: %.3f][sq: %.3f][confidence_band: %.3f]\n",
+ ses->num_values, error, *forecast, ses->sum_square_error, sq, *confidence_band);
+#endif
+
+ return(rc);
+}