diff --git a/SimoxUtility/CMakeLists.txt b/SimoxUtility/CMakeLists.txt index a88c86cefac5190551030afe8effefc7569647a4..e1372578d523819a798ef22e53220cb64be0ffff 100644 --- a/SimoxUtility/CMakeLists.txt +++ b/SimoxUtility/CMakeLists.txt @@ -82,7 +82,6 @@ SET(SOURCES math/statistics/BoxPlotStats.cpp math/statistics/Histogram.cpp - math/statistics/measures.cpp shapes/AxisAlignedBoundingBox.cpp diff --git a/SimoxUtility/math/statistics/BoxPlotStats.h b/SimoxUtility/math/statistics/BoxPlotStats.h index 9295fc7ea5ac3f58df5d8574b7d62eb2bf8ff964..ecd2057398bd550eb53660280f1c8e3121673598 100644 --- a/SimoxUtility/math/statistics/BoxPlotStats.h +++ b/SimoxUtility/math/statistics/BoxPlotStats.h @@ -13,7 +13,6 @@ namespace simox::math { public: - BoxPlotStats(); BoxPlotStats(const std::vector<float>& values, bool isSorted = false, float whisk = 1.5); diff --git a/SimoxUtility/math/statistics/measures.cpp b/SimoxUtility/math/statistics/measures.cpp deleted file mode 100644 index 044b4d5a501cd2a5d0d9562ae553e96801168933..0000000000000000000000000000000000000000 --- a/SimoxUtility/math/statistics/measures.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "measures.h" - -#include <algorithm> -#include <cmath> -#include <stdexcept> - -#include <SimoxUtility/error/SimoxError.h> - - -static void checkNotEmpty(const std::vector<float>& values) -{ - if (values.empty()) - { - throw simox::error::SimoxError("Passed vector of values is empty."); - } -} - -void simox::math::sort(std::vector<float>& values) -{ - std::sort(values.begin(), values.end()); -} - -std::vector<float> simox::math::sorted(const std::vector<float>& values) -{ - std::vector<float> s = values; - std::sort(s.begin(), s.end()); - return s; -} - -float simox::math::min(const std::vector<float>& values, bool isSorted) -{ - checkNotEmpty(values); - return isSorted ? values.front() : *std::min_element(values.begin(), values.end()); -} - -float simox::math::max(const std::vector<float>& values, bool isSorted) -{ - checkNotEmpty(values); - return isSorted ? values.back() : *std::max_element(values.begin(), values.end()); -} - -float simox::math::mean(const std::vector<float>& values) -{ - checkNotEmpty(values); - - float sum = 0; - for (float v : values) - { - sum += v; - } - return sum / values.size(); -} - -float simox::math::stddev(const std::vector<float>& values) -{ - return stddev(values, mean(values)); -} - -float simox::math::stddev(const std::vector<float>& values, float mean) -{ - checkNotEmpty(values); - float sum = 0; - for (float v : values) - { - float diff = v - mean; - sum += diff * diff; - } - float variance = sum / (values.size() - 1); - return std::sqrt(variance); -} - -float simox::math::quantile(const std::vector<float>& _values, float p, bool isSorted) -{ - checkNotEmpty(_values); - const std::vector<float>& values = isSorted ? _values : sorted(_values); - - float location = p < 1 ? p * values.size() : values.size() - 1; - - std::size_t floor = static_cast<std::size_t>(std::floor(location)); - std::size_t ceil = static_cast<std::size_t>(std::ceil(location)); - - if (floor == ceil) - { - return values.at(floor); - } - else - { - float t = location - floor; - return (1 - t) * values.at(floor) + t * values.at(ceil); - } -} - -float simox::math::lower_quartile(const std::vector<float>& values, bool isSorted) -{ - return quantile(values, .25, isSorted); -} - -float simox::math::median(const std::vector<float>& values, bool isSorted) -{ - return quantile(values, .5, isSorted); -} - -float simox::math::upper_quartile(const std::vector<float>& values, bool isSorted) -{ - return quantile(values, .75, isSorted); -} - -float simox::math::interquartile_range(const std::vector<float>& _values, bool isSorted) -{ - checkNotEmpty(_values); - - const std::vector<float>& values = isSorted ? _values : sorted(_values); - return interquartile_range(lower_quartile(values, true), upper_quartile(values, true)); -} - -float simox::math::interquartile_range(float lowerQuartile, float upperQuartile) -{ - return upperQuartile - lowerQuartile; -} - diff --git a/SimoxUtility/math/statistics/measures.h b/SimoxUtility/math/statistics/measures.h index fe0d5e44674531e36d5ae90e442e2ab2178ec8b7..c690f84c817253cd8c9ad983658a9295265a14bd 100644 --- a/SimoxUtility/math/statistics/measures.h +++ b/SimoxUtility/math/statistics/measures.h @@ -1,29 +1,153 @@ #pragma once +#include <algorithm> +#include <cmath> +#include <sstream> #include <vector> +#include <SimoxUtility/error/SimoxError.h> + + namespace simox::math { - void sort(std::vector<float>& values); - std::vector<float> sorted(const std::vector<float>& values); +namespace detail +{ + template <typename Float> + void check_not_empty(const std::vector<Float>& values) + { + if (values.empty()) + { + throw simox::error::SimoxError("Passed vector of values is empty."); + } + } +} + + template <typename Float> + std::vector<Float> sorted(const std::vector<Float>& values) + { + std::vector<Float> s = values; + std::sort(s.begin(), s.end()); + return s; + } + + + template <typename Float> + Float min(const std::vector<Float>& values, bool isSorted = false) + { + detail::check_not_empty(values); + return isSorted ? values.front() : *std::min_element(values.begin(), values.end()); + } + + template <typename Float> + Float max(const std::vector<Float>& values, bool isSorted = false) + { + detail::check_not_empty(values); + return isSorted ? values.back() : *std::max_element(values.begin(), values.end()); + } + + template <typename Float> + Float mean(const std::vector<Float>& values) + { + detail::check_not_empty(values); + Float sum = 0; + for (Float v : values) + { + sum += v; + } + return sum / values.size(); + } - float min(const std::vector<float>& values, bool isSorted=false); - float max(const std::vector<float>& values, bool isSorted=false); - float mean(const std::vector<float>& values); + template <typename Float> + Float stddev(const std::vector<Float>& values, Float mean) + { + detail::check_not_empty(values); + Float sum = 0; + for (Float v : values) + { + Float diff = v - mean; + sum += diff * diff; + } + Float variance = sum / (values.size() - 1); + return std::sqrt(variance); + } - float stddev(const std::vector<float>& values); - float stddev(const std::vector<float>& values, float mean); + template <typename Float> + Float stddev(const std::vector<Float>& values) + { + return stddev(values, mean(values)); + } - float quantile(const std::vector<float>& values, float p, bool isSorted=false); - float lower_quartile(const std::vector<float>& values, bool isSorted=false); - float median(const std::vector<float>& values, bool isSorted=false); - float upper_quartile(const std::vector<float>& values, bool isSorted=false); + template <typename Float> + Float quantile(const std::vector<Float>& _values, Float q, bool isSorted = false) + { + if (q < 0 || q > 1) + { + std::stringstream ss; + ss << "Quantile must be in [0, 1], but was " << q << "."; + throw error::SimoxError(ss.str()); + } + detail::check_not_empty(_values); + const std::vector<Float>& values = isSorted ? _values : sorted(_values); - float interquartile_range(const std::vector<float>& values, bool isSorted=false); - float interquartile_range(float lower_quartile, float upper_quartile); + if (q <= 0) + { + return values.front(); + } + if (q >= 1) + { + return values.back(); + } + + Float loc = q * (values.size() - 1); + int iloc = int(std::floor(loc)); + + if (loc == iloc) + { + return values[iloc]; + } + else + { + Float t = loc - iloc; + return (1 - t) * values[iloc] + t * values[iloc + 1]; + } + } + + template <typename Float> + Float lower_quartile(const std::vector<Float>& values, bool isSorted = false) + { + return quantile<Float>(values, .25, isSorted); + } + + template <typename Float> + Float median(const std::vector<Float>& values, bool isSorted = false) + { + return quantile<Float>(values, .5, isSorted); + } + + template <typename Float> + Float upper_quartile(const std::vector<Float>& values, bool isSorted = false) + { + return quantile<Float>(values, .75, isSorted); + } + + template <typename Float> + Float interquartile_range(Float lowerQuartile, Float upperQuartile) + { + return upperQuartile - lowerQuartile; + } + + template <typename Float> + Float interquartile_range(const std::vector<Float>& _values, bool isSorted = false) + { + detail::check_not_empty(_values); + + const std::vector<Float>& values = isSorted ? _values : sorted(_values); + return interquartile_range(lower_quartile(values, true), upper_quartile(values, true)); + } } + diff --git a/VirtualRobot/math/statistics/measures.cpp b/VirtualRobot/math/statistics/measures.cpp index 73aa17070690b24b5550180d392ffd0e5ae8cb40..baaf92dcdd9364b177ca67335db2115661ec161b 100644 --- a/VirtualRobot/math/statistics/measures.cpp +++ b/VirtualRobot/math/statistics/measures.cpp @@ -8,7 +8,7 @@ namespace math { namespace stat void sort(std::vector<float>& values) { - simox::math::sort(values); + std::sort(values.begin(), values.end()); } std::vector<float> sorted(const std::vector<float>& values)