|
Packit |
67cb25 |
/* movstat/mvacc.c
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Moving window mean/variance accumulator - based on a modification
|
|
Packit |
67cb25 |
* to Welford's algorithm, discussed here:
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* https://stackoverflow.com/a/6664212
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Copyright (C) 2018 Patrick Alken
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
67cb25 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
67cb25 |
* the Free Software Foundation; either version 3 of the License, or (at
|
|
Packit |
67cb25 |
* your option) any later version.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
67cb25 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
67cb25 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
67cb25 |
* General Public License for more details.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
67cb25 |
* along with this program; if not, write to the Free Software
|
|
Packit |
67cb25 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#include <config.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_math.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_vector.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_errno.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_movstat.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
typedef double ringbuf_type_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#include "ringbuf.c"
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
typedef struct
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t n; /* window size */
|
|
Packit |
67cb25 |
size_t k; /* number of samples currently in window */
|
|
Packit |
67cb25 |
double mean; /* current window mean */
|
|
Packit |
67cb25 |
double M2; /* current window M2 */
|
|
Packit |
67cb25 |
ringbuf *rbuf; /* ring buffer storing current window */
|
|
Packit |
67cb25 |
} mvacc_state_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static size_t
|
|
Packit |
67cb25 |
mvacc_size(const size_t n)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t size = 0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
size += sizeof(mvacc_state_t);
|
|
Packit |
67cb25 |
size += ringbuf_size(n);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return size;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
mvacc_init(const size_t n, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
mvacc_state_t * state = (mvacc_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->n = n;
|
|
Packit |
67cb25 |
state->k = 0;
|
|
Packit |
67cb25 |
state->mean = 0.0;
|
|
Packit |
67cb25 |
state->M2 = 0.0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->rbuf = (ringbuf *) ((unsigned char *) vstate + sizeof(mvacc_state_t));
|
|
Packit |
67cb25 |
ringbuf_init(n, state->rbuf);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
mvacc_insert(const double x, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
mvacc_state_t * state = (mvacc_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (ringbuf_is_full(state->rbuf))
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/* remove oldest window element and add new one */
|
|
Packit |
67cb25 |
double old = ringbuf_peek_back(state->rbuf);
|
|
Packit |
67cb25 |
double prev_mean = state->mean;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->mean += (x - old) / (double) state->n;
|
|
Packit |
67cb25 |
state->M2 += ((old - prev_mean) + (x - state->mean)) * (x - old);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double delta = x - state->mean;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* Welford algorithm:
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* mu_new = mu_old + (x - mu_old) / n
|
|
Packit |
67cb25 |
* M2_new = M2_old + (x - mu_old) * (x - mu_new)
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
++(state->k);
|
|
Packit |
67cb25 |
state->mean += delta / (double) state->k;
|
|
Packit |
67cb25 |
state->M2 += delta * (x - state->mean);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* add new element to ring buffer */
|
|
Packit |
67cb25 |
ringbuf_insert(x, state->rbuf);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
mvacc_delete(void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
mvacc_state_t * state = (mvacc_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (!ringbuf_is_empty(state->rbuf))
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (state->k > 1)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* mu_new = mu_old + (mu_old - x_old) / (n - 1)
|
|
Packit |
67cb25 |
* M2_new = M2_old - (mu_old - x_old) * (mu_new - x_old)
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double old = ringbuf_peek_back(state->rbuf);
|
|
Packit |
67cb25 |
double prev_mean = state->mean;
|
|
Packit |
67cb25 |
double delta = prev_mean - old;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->mean += delta / (state->k - 1.0);
|
|
Packit |
67cb25 |
state->M2 -= delta * (state->mean - old);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (state->k == 1)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
state->mean = 0.0;
|
|
Packit |
67cb25 |
state->M2 = 0.0;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
ringbuf_pop_back(state->rbuf);
|
|
Packit |
67cb25 |
--(state->k);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
mvacc_mean(void * params, double * result, const void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
const mvacc_state_t * state = (const mvacc_state_t *) vstate;
|
|
Packit |
67cb25 |
(void) params;
|
|
Packit |
67cb25 |
*result = state->mean;
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
mvacc_variance(void * params, double * result, const void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
const mvacc_state_t * state = (const mvacc_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
(void) params;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->k < 2)
|
|
Packit |
67cb25 |
*result = 0.0;
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
*result = state->M2 / (state->k - 1.0);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
mvacc_sd(void * params, double * result, const void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double variance;
|
|
Packit |
67cb25 |
int status = mvacc_variance(params, &variance, vstate);
|
|
Packit |
67cb25 |
*result = sqrt(variance);
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static const gsl_movstat_accum mean_accum_type =
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
mvacc_size,
|
|
Packit |
67cb25 |
mvacc_init,
|
|
Packit |
67cb25 |
mvacc_insert,
|
|
Packit |
67cb25 |
mvacc_delete,
|
|
Packit |
67cb25 |
mvacc_mean
|
|
Packit |
67cb25 |
};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_movstat_accum *gsl_movstat_accum_mean = &mean_accum_type;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static const gsl_movstat_accum variance_accum_type =
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
mvacc_size,
|
|
Packit |
67cb25 |
mvacc_init,
|
|
Packit |
67cb25 |
mvacc_insert,
|
|
Packit |
67cb25 |
mvacc_delete,
|
|
Packit |
67cb25 |
mvacc_variance
|
|
Packit |
67cb25 |
};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_movstat_accum *gsl_movstat_accum_variance = &variance_accum_type;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static const gsl_movstat_accum sd_accum_type =
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
mvacc_size,
|
|
Packit |
67cb25 |
mvacc_init,
|
|
Packit |
67cb25 |
mvacc_insert,
|
|
Packit |
67cb25 |
mvacc_delete,
|
|
Packit |
67cb25 |
mvacc_sd
|
|
Packit |
67cb25 |
};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_movstat_accum *gsl_movstat_accum_sd = &sd_accum_type;
|