1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00
VILLASnode/common/lib/hist.cpp
Steffen Vogel a366b80109 Fix formatting
Signed-off-by: Steffen Vogel <post@steffenvogel.de>
2024-05-28 19:45:55 -07:00

231 lines
5.9 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Histogram class.
*
* Author: Steffen Vogel <post@steffenvogel.de>
* SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University
* SPDX-License-Identifier: Apache-2.0
*/
#include <algorithm>
#include <cmath>
#include <villas/config.hpp>
#include <villas/exceptions.hpp>
#include <villas/hist.hpp>
#include <villas/table.hpp>
#include <villas/utils.hpp>
using namespace villas;
using namespace villas::utils;
namespace villas {
Hist::Hist(int buckets, Hist::cnt_t wu)
: resolution(0), high(0), low(0),
highest(std::numeric_limits<double>::min()),
lowest(std::numeric_limits<double>::max()), last(0), total(0), warmup(wu),
higher(0), lower(0), data(buckets, 0), _m{0, 0}, _s{0, 0} {}
void Hist::put(double value) {
last = value;
// Update min/max
if (value > highest)
highest = value;
if (value < lowest)
lowest = value;
if (data.size()) {
if (total < warmup) {
// We are still in warmup phase... Waiting for more samples...
} else if (data.size() && total == warmup && warmup != 0) {
low = getMean() - 3 * getStddev();
high = getMean() + 3 * getStddev();
resolution = (high - low) / data.size();
} else if (data.size() && (total == warmup) && (warmup == 0)) {
// There is no warmup phase
// TODO resolution = ?
} else {
idx_t idx = std::round((value - low) / resolution);
// Check bounds and increment
if (idx >= (idx_t)data.size())
higher++;
else if (idx < 0)
lower++;
else
data[idx]++;
}
}
total++;
// Online / running calculation of variance and mean
// by Donald Knuths Art of Computer Programming, Vol 2, page 232, 3rd edition
if (total == 1) {
_m[1] = _m[0] = value;
_s[1] = 0.0;
} else {
_m[0] = _m[1] + (value - _m[1]) / total;
_s[0] = _s[1] + (value - _m[1]) * (value - _m[0]);
// Set up for next iteration
_m[1] = _m[0];
_s[1] = _s[0];
}
}
void Hist::reset() {
total = 0;
higher = 0;
lower = 0;
highest = std::numeric_limits<double>::min();
lowest = std::numeric_limits<double>::max();
for (auto &elm : data)
elm = 0;
}
double Hist::getMean() const {
return total > 0 ? _m[0] : std::numeric_limits<double>::quiet_NaN();
}
double Hist::getVar() const {
return total > 1 ? _s[0] / (total - 1)
: std::numeric_limits<double>::quiet_NaN();
}
double Hist::getStddev() const { return sqrt(getVar()); }
void Hist::print(Logger logger, bool details, std::string prefix) const {
if (total > 0) {
Hist::cnt_t missed = total - higher - lower;
logger->info("{}Counted values: {} ({} between {} and {})", prefix, total,
missed, low, high);
logger->info("{}Highest: {:g}", prefix, highest);
logger->info("{}Lowest: {:g}", prefix, lowest);
logger->info("{}Mu: {:g}", prefix, getMean());
logger->info("{}1/Mu: {:g}", prefix, 1.0 / getMean());
logger->info("{}Variance: {:g}", prefix, getVar());
logger->info("{}Stddev: {:g}", prefix, getStddev());
if (details && total - higher - lower > 0) {
char *buf = dump();
logger->info("{}Matlab: {}", prefix, buf);
free(buf);
plot(logger);
}
} else
logger->info("{}Counted values: {}", prefix, total);
}
void Hist::plot(Logger logger) const {
// Get highest bar
Hist::cnt_t max = *std::max_element(data.begin(), data.end());
std::vector<TableColumn> cols = {
{-9, TableColumn::Alignment::RIGHT, "Value", "%+9.3g"},
{-6, TableColumn::Alignment::RIGHT, "Count", "%6ju"},
{0, TableColumn::Alignment::LEFT, "Plot", "%s", "occurrences"}};
Table table = Table(logger, cols);
// Print plot
table.header();
for (size_t i = 0; i < data.size(); i++) {
double value = low + (i)*resolution;
Hist::cnt_t cnt = data[i];
int bar = cols[2].getWidth() * ((double)cnt / max);
char *buf = strf("%s", "");
for (int i = 0; i < bar; i++)
buf = strcatf(&buf, "\u2588");
table.row(3, value, cnt, buf);
free(buf);
}
}
char *Hist::dump() const {
char *buf = new char[128];
if (!buf)
throw MemoryAllocationError();
memset(buf, 0, 128);
strcatf(&buf, "[ ");
for (auto elm : data)
strcatf(&buf, "%ju ", elm);
strcatf(&buf, "]");
return buf;
}
json_t *Hist::toJson() const {
json_t *json_buckets, *json_hist;
json_hist = json_pack("{ s: f, s: f, s: i }", "low", low, "high", high,
"total", total);
if (total > 0) {
json_object_update(json_hist,
json_pack("{ s: i, s: i, s: f, s: f, s: f, s: f, s: f }",
"higher", higher, "lower", lower, "highest",
highest, "lowest", lowest, "mean", getMean(),
"variance", getVar(), "stddev", getStddev()));
}
if (total - lower - higher > 0) {
json_buckets = json_array();
for (auto elm : data)
json_array_append(json_buckets, json_integer(elm));
json_object_set(json_hist, "buckets", json_buckets);
}
return json_hist;
}
int Hist::dumpJson(FILE *f) const {
json_t *j = Hist::toJson();
int ret = json_dumpf(j, f, 0);
json_decref(j);
return ret;
}
int Hist::dumpMatlab(FILE *f) const {
fprintf(f, "struct(");
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();
fprintf(f, "'buckets', %s", buf);
free(buf);
} else
fprintf(f, "'buckets', zeros(1, %zu)", data.size());
fprintf(f, ")");
return 0;
}
} // namespace villas