171 lines
5.1 KiB
C++
171 lines
5.1 KiB
C++
// Copyright (C) 2018 Davis E. King (davis@dlib.net)
|
|
// License: Boost Software License See LICENSE.txt for the full license.
|
|
#ifndef DLIB_ISOTONIC_ReGRESSION_H_
|
|
#define DLIB_ISOTONIC_ReGRESSION_H_
|
|
|
|
#include "isotonic_regression_abstract.h"
|
|
#include <vector>
|
|
#include <utility>
|
|
#include <cstddef>
|
|
|
|
namespace dlib
|
|
{
|
|
class isotonic_regression
|
|
{
|
|
public:
|
|
|
|
template <
|
|
typename const_iterator,
|
|
typename iterator
|
|
>
|
|
void operator() (
|
|
const_iterator begin,
|
|
const_iterator end,
|
|
iterator obegin
|
|
)
|
|
{
|
|
do_isotonic_regression(begin, end);
|
|
|
|
// unpack blocks to output
|
|
for (auto& block : blocks)
|
|
{
|
|
for (size_t k = 0; k < block.num; ++k)
|
|
set_val(*obegin++, block.avg);
|
|
}
|
|
|
|
blocks.clear();
|
|
}
|
|
|
|
void operator() (
|
|
std::vector<double>& vect
|
|
) { (*this)(vect.begin(), vect.end(), vect.begin()); }
|
|
|
|
template <typename T, typename U>
|
|
void operator() (
|
|
std::vector<std::pair<T,U>>& vect
|
|
) { (*this)(vect.begin(), vect.end(), vect.begin()); }
|
|
|
|
|
|
template <
|
|
typename const_iterator,
|
|
typename iterator
|
|
>
|
|
void fit_with_linear_output_interpolation (
|
|
const_iterator begin,
|
|
const_iterator end,
|
|
iterator obegin
|
|
)
|
|
{
|
|
do_isotonic_regression(begin, end);
|
|
|
|
// Unpack blocks to output, but here instead of producing the step function
|
|
// output we linearly interpolate. Note that this actually fits the data less
|
|
// than the step-function, but in many applications might be closer to what you
|
|
// really want when using isotonic_regression than the step function.
|
|
for (size_t i = 0; i < blocks.size(); ++i)
|
|
{
|
|
auto& block = blocks[i];
|
|
|
|
double prev = (blocks.front().avg + block.avg)/2;
|
|
if (i > 0)
|
|
prev = (blocks[i-1].avg+block.avg)/2;
|
|
|
|
double next = (blocks.back().avg + block.avg)/2;
|
|
if (i+1 < blocks.size())
|
|
next = (blocks[i+1].avg+block.avg)/2;
|
|
|
|
for (size_t k = 0; k < block.num; ++k)
|
|
{
|
|
const auto mid = block.num/2.0;
|
|
if (k < mid)
|
|
{
|
|
const double alpha = k/mid;
|
|
set_val(*obegin++, (1-alpha)*prev + alpha*block.avg);
|
|
}
|
|
else
|
|
{
|
|
const double alpha = k/mid-1;
|
|
set_val(*obegin++, alpha*next + (1-alpha)*block.avg);
|
|
}
|
|
}
|
|
}
|
|
|
|
blocks.clear();
|
|
}
|
|
|
|
void fit_with_linear_output_interpolation (
|
|
std::vector<double>& vect
|
|
) { fit_with_linear_output_interpolation(vect.begin(), vect.end(), vect.begin()); }
|
|
|
|
template <typename T, typename U>
|
|
void fit_with_linear_output_interpolation (
|
|
std::vector<std::pair<T,U>>& vect
|
|
) { fit_with_linear_output_interpolation(vect.begin(), vect.end(), vect.begin()); }
|
|
|
|
private:
|
|
|
|
template <
|
|
typename const_iterator
|
|
>
|
|
void do_isotonic_regression (
|
|
const_iterator begin,
|
|
const_iterator end
|
|
)
|
|
{
|
|
blocks.clear();
|
|
|
|
// Do the actual isotonic regression. The output is a step-function and is
|
|
// stored in the vector of blocks.
|
|
for (auto i = begin; i != end; ++i)
|
|
{
|
|
blocks.emplace_back(get_val(*i));
|
|
while (blocks.size() > 1 && prev_block().avg > current_block().avg)
|
|
{
|
|
// merge the last two blocks.
|
|
prev_block() = prev_block() + current_block();
|
|
blocks.pop_back();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
static double get_val(const T& v) { return v;}
|
|
|
|
template <typename T, typename U>
|
|
static double get_val(const std::pair<T,U>& v) { return v.second;}
|
|
|
|
template <typename T>
|
|
static void set_val(T& v, double val) { v = val;}
|
|
|
|
template <typename T, typename U>
|
|
static void set_val(std::pair<T,U>& v, double val) { v.second = val;}
|
|
|
|
|
|
|
|
struct block_t
|
|
{
|
|
block_t(double val) : num(1), avg(val) {}
|
|
block_t(size_t n, double val) : num(n), avg(val) {}
|
|
|
|
size_t num;
|
|
double avg;
|
|
|
|
inline block_t operator+(const block_t& rhs) const
|
|
{
|
|
return block_t(num+rhs.num,
|
|
(num*avg + rhs.num*rhs.avg)/(num+rhs.num));
|
|
}
|
|
};
|
|
|
|
inline block_t& prev_block() { return blocks[blocks.size()-2]; }
|
|
inline block_t& current_block() { return blocks.back(); }
|
|
|
|
std::vector<block_t> blocks;
|
|
};
|
|
}
|
|
|
|
#endif // DLIB_ISOTONIC_ReGRESSION_H_
|
|
|
|
|