Histogram: Add Percentile calculation
This commit is contained in:
+51
-8
@@ -12,13 +12,16 @@
|
||||
#include "MultiLinePlot.h"
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
class SetAverage {
|
||||
std::vector<T> sum;
|
||||
std::vector<uint64_t> count;
|
||||
std::mutex m;
|
||||
mutable std::mutex m;
|
||||
|
||||
public:
|
||||
explicit SetAverage(size_t bins) : sum(bins), count(bins) {}
|
||||
explicit SetAverage(size_t bins) : sum(bins), count(bins) {
|
||||
}
|
||||
|
||||
void Add(size_t bin, T val) {
|
||||
std::unique_lock ul(m);
|
||||
|
||||
@@ -28,7 +31,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
MultiLinePlot GetPlot() {
|
||||
MultiLinePlot GetPlot() const {
|
||||
std::unique_lock ul(m);
|
||||
|
||||
MultiLinePlotStruct plot;
|
||||
@@ -49,16 +52,19 @@ public:
|
||||
|
||||
class FloatHistogram {
|
||||
std::vector<uint64_t> count;
|
||||
std::mutex m;
|
||||
mutable std::mutex m;
|
||||
float min;
|
||||
float max;
|
||||
float spacing;
|
||||
|
||||
public:
|
||||
explicit FloatHistogram(size_t bins, float in_min, float in_max) : count(bins), min(in_min), max(in_max) {
|
||||
if (bins == 0)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterBelowMin, "FloatHistogram neds to have non-zero bins");
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterBelowMin,
|
||||
"FloatHistogram neds to have non-zero bins");
|
||||
spacing = (max - min) / bins;
|
||||
}
|
||||
|
||||
void Add(float val) {
|
||||
std::unique_lock ul(m);
|
||||
|
||||
@@ -68,11 +74,11 @@ public:
|
||||
count[bin] += 1;
|
||||
}
|
||||
|
||||
MultiLinePlot GetPlot() {
|
||||
MultiLinePlot GetPlot() const {
|
||||
std::unique_lock ul(m);
|
||||
|
||||
MultiLinePlotStruct plot;
|
||||
plot.x.resize(count.size());
|
||||
plot.x.resize(count.size());
|
||||
plot.y.resize(count.size());
|
||||
for (int i = 0; i < count.size(); i++) {
|
||||
plot.x[i] = min + (i + 0.5f) * spacing;
|
||||
@@ -82,6 +88,43 @@ public:
|
||||
ret.AddPlot(plot);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t TotalCount() const {
|
||||
std::unique_lock ul(m);
|
||||
uint64_t total = 0;
|
||||
for (auto c: count) total += c;
|
||||
return total;
|
||||
}
|
||||
|
||||
// Returns the value x such that approximately `percent`% of samples are <= x.
|
||||
// - percent must be in [0, 100]
|
||||
// - returns std::nullopt if histogram is empty
|
||||
std::optional<float> Percentile(float percent) const {
|
||||
if (!std::isfinite(percent) || percent < 0.0f || percent > 100.0f) {
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterBelowMin,
|
||||
"FloatHistogram Percentile expects percent in [0, 100]");
|
||||
}
|
||||
|
||||
std::unique_lock ul(m);
|
||||
|
||||
uint64_t total = 0;
|
||||
for (auto c: count) total += c;
|
||||
if (total == 0) return std::nullopt;
|
||||
|
||||
// Target rank in [0, total-1]
|
||||
const double q = static_cast<double>(percent) / 100.0;
|
||||
const auto target = static_cast<uint64_t>(std::floor(q * static_cast<double>(total - 1)));
|
||||
|
||||
uint64_t cumulative = 0;
|
||||
for (size_t i = 0; i < count.size(); i++) {
|
||||
cumulative += count[i];
|
||||
if (target < cumulative && count[i] > 0)
|
||||
return min + static_cast<float>(i) * spacing + 0.5f * spacing;
|
||||
}
|
||||
|
||||
// If due to rounding we didn't return inside the loop, clamp to the last bin's upper edge.
|
||||
return max;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_HISTOGRAM_H
|
||||
|
||||
@@ -53,3 +53,20 @@ TEST_CASE("FloatHistogram") {
|
||||
CHECK(p.GetPlots()[0].y[50] == 1.0);
|
||||
CHECK(p.GetPlots()[0].y[99] == 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE("FloatHistogram_Percentile") {
|
||||
FloatHistogram h(111, 89.5, 200.5);
|
||||
for (int i = 0; i < 8; i++)
|
||||
h.Add(100.0f);
|
||||
h.Add(150.0f);
|
||||
h.Add(200.0f);
|
||||
|
||||
CHECK(h.Percentile(0.0f).value_or(-1) == Catch::Approx(100.0f));
|
||||
CHECK(h.Percentile(30.0f).value_or(-1) == Catch::Approx(100.0f));
|
||||
CHECK(h.Percentile(50.0f).value_or(-1) == Catch::Approx(100.0f));
|
||||
CHECK(h.Percentile(80.0f).value_or(-1) == Catch::Approx(100.0f));
|
||||
CHECK(h.Percentile(90.0f).value_or(-1) == Catch::Approx(150.0f));
|
||||
CHECK(h.Percentile(100.0f).value_or(-1) == Catch::Approx(200.0f));
|
||||
|
||||
CHECK(h.TotalCount() == 10);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user