2019-06-03 17:31:12 +02:00
|
|
|
|
/** Histogram class.
|
2018-08-22 11:29:39 +02:00
|
|
|
|
*
|
|
|
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
2019-01-13 00:42:26 +01:00
|
|
|
|
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
2018-08-22 11:29:39 +02:00
|
|
|
|
* @license GNU General Public License (version 3)
|
|
|
|
|
*
|
|
|
|
|
* VILLAScommon
|
|
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <float.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
|
2019-04-23 12:57:51 +02:00
|
|
|
|
#include <villas/utils.hpp>
|
2019-04-07 15:12:32 +02:00
|
|
|
|
#include <villas/hist.hpp>
|
2018-08-22 11:29:39 +02:00
|
|
|
|
#include <villas/config.h>
|
2019-04-07 15:12:32 +02:00
|
|
|
|
#include <villas/table.hpp>
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
2019-05-30 12:43:37 +02:00
|
|
|
|
using namespace villas::utils;
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
namespace villas {
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
Hist::Hist(int buckets, Hist::cnt_t wu)
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-06-03 17:31:12 +02:00
|
|
|
|
length = buckets;
|
|
|
|
|
warmup = wu;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
data = (Hist::cnt_t *) (buckets ? alloc(length * sizeof(Hist::cnt_t)) : nullptr);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
Hist::reset();
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
Hist::~Hist()
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-06-03 17:31:12 +02:00
|
|
|
|
if (data)
|
|
|
|
|
free(data);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
void Hist::put(double value)
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-06-03 17:31:12 +02:00
|
|
|
|
last = value;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
/* Update min/max */
|
2019-06-03 17:31:12 +02:00
|
|
|
|
if (value > highest)
|
|
|
|
|
highest = value;
|
|
|
|
|
if (value < lowest)
|
|
|
|
|
lowest = value;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
if (total < warmup) {
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
}
|
2019-06-03 17:31:12 +02:00
|
|
|
|
else if (total == warmup) {
|
|
|
|
|
low = getMean() - 3 * getStddev();
|
|
|
|
|
high = getMean() + 3 * getStddev();
|
|
|
|
|
resolution = (high - low) / length;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2019-06-03 17:31:12 +02:00
|
|
|
|
int idx = round((value - low) / resolution);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
/* Check bounds and increment */
|
2019-06-03 17:31:12 +02:00
|
|
|
|
if (idx >= length)
|
|
|
|
|
higher++;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
else if (idx < 0)
|
2019-06-03 17:31:12 +02:00
|
|
|
|
lower++;
|
|
|
|
|
else if (data != nullptr)
|
|
|
|
|
data[idx]++;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
total++;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
/* Online / running calculation of variance and mean
|
|
|
|
|
* by Donald Knuth’s Art of Computer Programming, Vol 2, page 232, 3rd edition */
|
2019-06-03 17:31:12 +02:00
|
|
|
|
if (total == 1) {
|
|
|
|
|
_m[1] = _m[0] = value;
|
|
|
|
|
_s[1] = 0.0;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2019-06-03 17:31:12 +02:00
|
|
|
|
_m[0] = _m[1] + (value - _m[1]) / total;
|
|
|
|
|
_s[0] = _s[1] + (value - _m[1]) * (value - _m[0]);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
2019-03-26 06:50:56 +01:00
|
|
|
|
/* Set up for next iteration */
|
2019-06-03 17:31:12 +02:00
|
|
|
|
_m[1] = _m[0];
|
|
|
|
|
_s[1] = _s[0];
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
void Hist::reset()
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-06-03 17:31:12 +02:00
|
|
|
|
total = 0;
|
|
|
|
|
higher = 0;
|
|
|
|
|
lower = 0;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
highest = -DBL_MAX;
|
|
|
|
|
lowest = DBL_MAX;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
if (data)
|
|
|
|
|
memset(data, 0, length * sizeof(unsigned));
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
double Hist::getMean() const
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-06-03 17:31:12 +02:00
|
|
|
|
return (total > 0) ? _m[0] : NAN;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
double Hist::getVar() const
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-06-03 17:31:12 +02:00
|
|
|
|
return (total > 1) ? _s[0] / (total - 1) : NAN;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
double Hist::getStddev() const
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-06-03 17:31:12 +02:00
|
|
|
|
return sqrt(getVar());
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
void Hist::print(int details) const
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-06-03 17:31:12 +02:00
|
|
|
|
if (total > 0) {
|
|
|
|
|
Hist::cnt_t missed = total - higher - lower;
|
|
|
|
|
|
|
|
|
|
info("Counted values: %ju (%ju between %f and %f)", total, missed, low, high);
|
|
|
|
|
info("Highest: %g", highest);
|
|
|
|
|
info("Lowest: %g", lowest);
|
|
|
|
|
info("Mu: %g", getMean());
|
|
|
|
|
info("1/Mu: %g", 1.0 / getMean());
|
|
|
|
|
info("Variance: %g", getVar());
|
|
|
|
|
info("Stddev: %g", getStddev());
|
|
|
|
|
|
|
|
|
|
if (details > 0 && total - higher - lower > 0) {
|
|
|
|
|
char *buf =dump();
|
2019-01-22 16:00:20 +01:00
|
|
|
|
info("Matlab: %s", buf);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
free(buf);
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
plot();
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-06-03 17:31:12 +02:00
|
|
|
|
info("Counted values: %ju", total);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
void Hist::plot() const
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-06-03 17:31:12 +02:00
|
|
|
|
Hist::cnt_t max = 1;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
/* Get highest bar */
|
2019-06-03 17:31:12 +02:00
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
|
if (data[i] > max)
|
|
|
|
|
max = data[i];
|
2018-08-22 11:29:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-07 15:12:32 +02:00
|
|
|
|
std::vector<TableColumn> cols = {
|
|
|
|
|
{ -9, TableColumn::align::RIGHT, "Value", "%+9.3g" },
|
|
|
|
|
{ -6, TableColumn::align::RIGHT, "Count", "%6ju" },
|
2019-04-10 09:25:38 +02:00
|
|
|
|
{ 0, TableColumn::align::LEFT, "Plot", "%s", "occurences" }
|
2018-08-22 11:29:39 +02:00
|
|
|
|
};
|
|
|
|
|
|
2019-04-07 15:12:32 +02:00
|
|
|
|
Table table = Table(cols);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
/* Print plot */
|
2019-04-07 15:12:32 +02:00
|
|
|
|
table.header();
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
|
double value = low + (i) * resolution;
|
|
|
|
|
Hist::cnt_t cnt = data[i];
|
2019-04-07 15:12:32 +02:00
|
|
|
|
int bar = cols[2].getWidth() * ((double) cnt / max);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
char *buf = strf("%s", "");
|
|
|
|
|
for (int i = 0; i < bar; i++)
|
|
|
|
|
buf = strcatf(&buf, "\u2588");
|
|
|
|
|
|
2019-04-10 09:25:38 +02:00
|
|
|
|
table.row(3, value, cnt, buf);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
free(buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
char * Hist::dump() const
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-04-07 15:12:32 +02:00
|
|
|
|
char *buf = (char *) alloc(128);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
strcatf(&buf, "[ ");
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
for (int i = 0; i < length; i++)
|
|
|
|
|
strcatf(&buf, "%ju ", data[i]);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
strcatf(&buf, "]");
|
|
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
json_t * Hist::toJson() const
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
|
|
|
|
json_t *json_buckets, *json_hist;
|
|
|
|
|
|
|
|
|
|
json_hist = json_pack("{ s: f, s: f, s: i }",
|
2019-06-03 17:31:12 +02:00
|
|
|
|
"low", low,
|
|
|
|
|
"high", high,
|
|
|
|
|
"total", total
|
2018-08-22 11:29:39 +02:00
|
|
|
|
);
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
if (total > 0) {
|
2018-08-22 11:29:39 +02:00
|
|
|
|
json_object_update(json_hist, json_pack("{ s: i, s: i, s: f, s: f, s: f, s: f, s: f }",
|
2019-06-03 17:31:12 +02:00
|
|
|
|
"higher", higher,
|
|
|
|
|
"lower", lower,
|
|
|
|
|
"highest", highest,
|
|
|
|
|
"lowest", lowest,
|
|
|
|
|
"mean", getMean(),
|
|
|
|
|
"variance", getVar(),
|
|
|
|
|
"stddev", getStddev()
|
2018-08-22 11:29:39 +02:00
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
if (total - lower - higher > 0) {
|
2018-08-22 11:29:39 +02:00
|
|
|
|
json_buckets = json_array();
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
for (int i = 0; i < length; i++)
|
|
|
|
|
json_array_append(json_buckets, json_integer(data[i]));
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
json_object_set(json_hist, "buckets", json_buckets);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return json_hist;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
int Hist::dumpJson(FILE *f) const
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
2019-06-03 17:31:12 +02:00
|
|
|
|
json_t *j = Hist::toJson();
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
int ret = json_dumpf(j, f, 0);
|
|
|
|
|
|
|
|
|
|
json_decref(j);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:31:12 +02:00
|
|
|
|
int Hist::dumpMatlab(FILE *f) const
|
2018-08-22 11:29:39 +02:00
|
|
|
|
{
|
|
|
|
|
fprintf(f, "struct(");
|
2019-06-03 17:31:12 +02:00
|
|
|
|
fprintf(f, "'low', %f, ", low);
|
|
|
|
|
fprintf(f, "'high', %f, ", high);
|
|
|
|
|
fprintf(f, "'total', %ju, ", total);
|
|
|
|
|
fprintf(f, "'higher', %ju, ", higher);
|
|
|
|
|
fprintf(f, "'lower', %ju, ", lower);
|
|
|
|
|
fprintf(f, "'highest', %f, ", highest);
|
|
|
|
|
fprintf(f, "'lowest', %f, ", lowest);
|
|
|
|
|
fprintf(f, "'mean', %f, ", getMean());
|
|
|
|
|
fprintf(f, "'variance', %f, ", getVar());
|
|
|
|
|
fprintf(f, "'stddev', %f, ", getStddev());
|
|
|
|
|
|
|
|
|
|
if (total - lower - higher > 0) {
|
|
|
|
|
char *buf = dump();
|
2018-08-22 11:29:39 +02:00
|
|
|
|
fprintf(f, "'buckets', %s", buf);
|
|
|
|
|
free(buf);
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-06-03 17:31:12 +02:00
|
|
|
|
fprintf(f, "'buckets', zeros(1, %d)", length);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
|
|
|
|
|
fprintf(f, ")");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2019-06-03 17:31:12 +02:00
|
|
|
|
|
|
|
|
|
} /* namespace villas */
|