Skip to content
Snippets Groups Projects
Commit 3c25b989 authored by Rainer Kartmann's avatar Rainer Kartmann
Browse files

Make measures templates, fix quantile (make it consistent with np.quantile())

parent d49e1ad5
No related branches found
No related tags found
No related merge requests found
......@@ -82,7 +82,6 @@ SET(SOURCES
math/statistics/BoxPlotStats.cpp
math/statistics/Histogram.cpp
math/statistics/measures.cpp
shapes/AxisAlignedBoundingBox.cpp
......
......@@ -13,7 +13,6 @@ namespace simox::math
{
public:
BoxPlotStats();
BoxPlotStats(const std::vector<float>& values, bool isSorted = false, float whisk = 1.5);
......
#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;
}
#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));
}
}
......@@ -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)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment