2
0
mirror of https://github.com/boostorg/math.git synced 2026-01-19 04:22:09 +00:00

Rename 'absolute_median' to 'median_absolute_deviation' [CI SKIP]

This commit is contained in:
Nick Thompson
2019-01-07 10:50:06 -07:00
parent ff09a81d67
commit df447ae00d
6 changed files with 139 additions and 157 deletions

View File

@@ -13,13 +13,7 @@
``
#include <boost/math/tools/signal_statistics.hpp>
namespace boost{ namespace math{ namespace tools {
template<class Container>
auto absolute_median(Container & c);
template<class ForwardIterator>
auto absolute_median(ForwardIterator first, ForwardIterator last);
namespace boost::math::tools {
template<class Container>
auto absolute_gini_coefficient(Container & c);
@@ -57,7 +51,7 @@ namespace boost{ namespace math{ namespace tools {
template<class Container>
auto m2m4_snr_estimator_db(Container const & noisy_signal,typename Container::value_type estimated_signal_kurtosis=1, typename Container::value_type estimate_noise_kurtosis=3);
}}}
}
``
[heading Description]
@@ -69,18 +63,6 @@ In general, you can store your data in an Eigen array, and Armadillo vector, `st
These routines are usable in float, double, long double, and Boost.Multiprecision precision, as well as their complex extensions whenever the computation is well-defined.
For certain operations (total variation, for example) integer inputs are supported.
[heading Absolute Median]
The absolute median is used in signal processing, where the median of the magnitude of the coefficients in some expansion are used to estimate noise variance.
See [@https://wavelet-tour.github.io/ Mallat] for details.
The absolute median supports both real and complex arithmetic, modifies its input, and requires random access containers.
std::vector<double> v{-1, 1};
double m = boost::math::tools::absolute_median(v);
// m = 1
// Alternative syntax, using a subset of the container:
m = boost::math::tools::absolute_median(v.begin(), v.begin() + 1);
[heading Absolute Gini Coefficient]
The Gini coefficient, first used to measure wealth inequality, is also one of the best measures of the sparsity of an expansion in a basis.

View File

@@ -63,6 +63,12 @@ namespace boost{ namespace math{ namespace tools {
template<class ForwardIterator>
auto median(ForwardIterator first, ForwardIterator last);
template<class RandomAccessIterator>
auto median_absolute_deviation(ForwardIterator first, ForwardIterator last, typename std::iterator_traits<RandomAccessIterator>::value_type center=std::numeric_limits<Real>::quiet_NaN());
template<class RandomAccessContainer>
auto median_absolute_deviation(RandomAccessContainer v, typename RandomAccessContainer::value_type center=std::numeric_limits<Real>::quiet_NaN());
template<class Container>
auto gini_coefficient(Container & c);
@@ -169,7 +175,7 @@ Simultaneously computes the first four [@https://en.wikipedia.org/wiki/Central_m
[heading Median]
Compute the median of a dataset:
Computes the median of a dataset:
std::vector<double> v{1,2,3,4,5};
double m = boost::math::tools::median(v.begin(), v.end());
@@ -179,6 +185,27 @@ The calculation of the median is a thin wrapper around the C++11 [@https://en.cp
Therefore, all requirements of `std::nth_element` are inherited by the median calculation.
In particular, the container must allow random access.
[heading Median Absolute Deviation]
Computes the [@https://en.wikipedia.org/wiki/Median_absolute_deviation median absolute deviation] of a dataset:
std::vector<double> v{1,2,3,4,5};
double mad = boost::math::tools::median_absolute_deviation(v);
By default, the deviation from the median is used.
If you have some prior that the median is zero, or wish to compute the median absolute deviation from the mean,
use the following:
// prior is that center is zero:
double center = 0;
double mad = boost::math::tools::median_absolute_deviation(v, center);
// compute median absolute deviation from the mean:
double mu = boost::math::tools::mean(v);
double mad = boost::math::tools::median_absolute_deviation(v, mu);
/Nota bene:/ The input vector is modified.
Again the vector is passed into a call to [@https://en.cppreference.com/w/cpp/algorithm/nth_element `nth_element`].
[heading Gini Coefficient]

View File

@@ -15,36 +15,7 @@
#include <boost/math/tools/univariate_statistics.hpp>
namespace boost{ namespace math{ namespace tools {
template<class RandomAccessIterator>
auto absolute_median(RandomAccessIterator first, RandomAccessIterator last)
{
using std::abs;
using RealOrComplex = typename std::iterator_traits<RandomAccessIterator>::value_type;
size_t num_elems = std::distance(first, last);
BOOST_ASSERT_MSG(num_elems > 0, "The median of a zero-length vector is undefined.");
auto comparator = [](RealOrComplex a, RealOrComplex b) { return abs(a) < abs(b);};
if (num_elems & 1)
{
auto middle = first + (num_elems - 1)/2;
std::nth_element(first, middle, last, comparator);
return abs(*middle);
}
else
{
auto middle = first + num_elems/2 - 1;
std::nth_element(first, middle, last, comparator);
std::nth_element(middle, middle+1, last, comparator);
return (abs(*middle) + abs(*(middle+1)))/abs(static_cast<RealOrComplex>(2));
}
}
template<class RandomAccessContainer>
inline auto absolute_median(RandomAccessContainer & v)
{
return absolute_median(v.begin(), v.end());
}
namespace boost::math::tools {
template<class ForwardIterator>
auto absolute_gini_coefficient(ForwardIterator first, ForwardIterator last)
@@ -352,5 +323,5 @@ inline auto m2m4_snr_estimator_db(Container const & noisy_signal, typename Cont
return 10*log10(m2m4_snr_estimator(noisy_signal, estimated_signal_kurtosis, estimated_noise_kurtosis));
}
}}}
}
#endif

View File

@@ -13,7 +13,7 @@
#include <boost/multiprecision/detail/number_base.hpp>
namespace boost{ namespace math{ namespace tools {
namespace boost::math::tools {
template<class ForwardIterator>
auto mean(ForwardIterator first, ForwardIterator last)
@@ -382,7 +382,40 @@ inline auto sample_gini_coefficient(RandomAccessContainer & v)
return sample_gini_coefficient(v.begin(), v.end());
}
template<class RandomAccessIterator>
auto median_absolute_deviation(RandomAccessIterator first, RandomAccessIterator last, typename std::iterator_traits<RandomAccessIterator>::value_type center=std::numeric_limits<typename std::iterator_traits<RandomAccessIterator>::value_type>::quiet_NaN())
{
using std::abs;
using Real = typename std::iterator_traits<RandomAccessIterator>::value_type;
using std::isnan;
if (isnan(center))
{
center = boost::math::tools::median(first, last);
}
size_t num_elems = std::distance(first, last);
BOOST_ASSERT_MSG(num_elems > 0, "The median of a zero-length vector is undefined.");
auto comparator = [&center](Real a, Real b) { return abs(a-center) < abs(b-center);};
if (num_elems & 1)
{
auto middle = first + (num_elems - 1)/2;
std::nth_element(first, middle, last, comparator);
return abs(*middle);
}
else
{
auto middle = first + num_elems/2 - 1;
std::nth_element(first, middle, last, comparator);
std::nth_element(middle, middle+1, last, comparator);
return (abs(*middle) + abs(*(middle+1)))/abs(static_cast<Real>(2));
}
}
template<class RandomAccessContainer>
inline auto median_absolute_deviation(RandomAccessContainer & v, typename RandomAccessContainer::value_type center=std::numeric_limits<typename RandomAccessContainer::value_type>::quiet_NaN())
{
return median_absolute_deviation(v.begin(), v.end(), center);
}
}}}
}
#endif

View File

@@ -33,99 +33,6 @@ using boost::math::constants::two_pi;
* 6) Does it work with integer data if sensible?
*/
template<class Real>
void test_absolute_median()
{
std::vector<Real> v{-1, 2, -3, 4, -5, 6, -7};
Real m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 4);
std::mt19937 g(12);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::tools::absolute_median(v);
BOOST_TEST_EQ(m, 4);
v = {1, -2, -3, 3, -4, -5};
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 3);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 3);
v = {-1};
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 1);
v = {-1, 1};
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 1);
v = {2, -4};
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 3);
v = {1, -1, 1};
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 1);
v = {1, 2, -3};
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 2);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 2);
std::array<Real, 3> w{1, 2, -3};
m = boost::math::tools::absolute_median(w);
BOOST_TEST_EQ(m, 2);
// boost.ublas vector?
boost::numeric::ublas::vector<Real> u(6);
u[0] = 1;
u[1] = 2;
u[2] = -3;
u[3] = 1;
u[4] = 2;
u[5] = -3;
m = boost::math::tools::absolute_median(u);
BOOST_TEST_EQ(m, 2);
}
template<class Complex>
void test_complex_absolute_median()
{
typedef typename Complex::value_type Real;
std::mt19937 g(18);
std::vector<Complex> v{{0,1}, {0,-2},{0,3}, {0,-4}, {0,5}, {0,-6}, {0,7}};
Real m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 4);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::tools::absolute_median(v);
BOOST_TEST_EQ(m, 4);
v = {{0,1}, {0,-2}, {0,-3}, {0,3}, {0,4}, {0,-5}};
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 3);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 3);
v = {{0, -1}};
m = boost::math::tools::absolute_median(v.begin(), v.end());
BOOST_TEST_EQ(m, 1);
boost::numeric::ublas::vector<Complex> w(1);
w[0] = {0, -1};
m = boost::math::tools::absolute_median(w);
BOOST_TEST_EQ(m, 1);
}
template<class Real>
void test_hoyer_sparsity()
{
@@ -386,16 +293,6 @@ void test_m2m4_snr_estimator()
int main()
{
test_absolute_median<float>();
test_absolute_median<double>();
test_absolute_median<long double>();
test_absolute_median<cpp_bin_float_50>();
test_complex_absolute_median<std::complex<float>>();
test_complex_absolute_median<std::complex<double>>();
test_complex_absolute_median<std::complex<long double>>();
test_complex_absolute_median<cpp_complex_50>();
test_absolute_gini_coefficient<float>();
test_absolute_gini_coefficient<double>();
test_absolute_gini_coefficient<long double>();

View File

@@ -347,6 +347,73 @@ void test_median()
BOOST_TEST_EQ(m, 2);
}
template<class Real>
void test_median_absolute_deviation()
{
std::vector<Real> v{-1, 2, -3, 4, -5, 6, -7};
Real m = boost::math::tools::median_absolute_deviation(v.begin(), v.end(), 0);
BOOST_TEST_EQ(m, 4);
std::mt19937 g(12);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::tools::median_absolute_deviation(v, 0);
BOOST_TEST_EQ(m, 4);
v = {1, -2, -3, 3, -4, -5};
m = boost::math::tools::median_absolute_deviation(v.begin(), v.end(), 0);
BOOST_TEST_EQ(m, 3);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::tools::median_absolute_deviation(v.begin(), v.end(), 0);
BOOST_TEST_EQ(m, 3);
v = {-1};
m = boost::math::tools::median_absolute_deviation(v.begin(), v.end(), 0);
BOOST_TEST_EQ(m, 1);
v = {-1, 1};
m = boost::math::tools::median_absolute_deviation(v.begin(), v.end(), 0);
BOOST_TEST_EQ(m, 1);
// The median is zero, so coincides with the default:
m = boost::math::tools::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 1);
m = boost::math::tools::median_absolute_deviation(v);
BOOST_TEST_EQ(m, 1);
v = {2, -4};
m = boost::math::tools::median_absolute_deviation(v.begin(), v.end(), 0);
BOOST_TEST_EQ(m, 3);
v = {1, -1, 1};
m = boost::math::tools::median_absolute_deviation(v.begin(), v.end(), 0);
BOOST_TEST_EQ(m, 1);
v = {1, 2, -3};
m = boost::math::tools::median_absolute_deviation(v.begin(), v.end(), 0);
BOOST_TEST_EQ(m, 2);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::tools::median_absolute_deviation(v.begin(), v.end(), 0);
BOOST_TEST_EQ(m, 2);
std::array<Real, 3> w{1, 2, -3};
m = boost::math::tools::median_absolute_deviation(w, 0);
BOOST_TEST_EQ(m, 2);
// boost.ublas vector?
boost::numeric::ublas::vector<Real> u(6);
u[0] = 1;
u[1] = 2;
u[2] = -3;
u[3] = 1;
u[4] = 2;
u[5] = -3;
m = boost::math::tools::median_absolute_deviation(u, 0);
BOOST_TEST_EQ(m, 2);
}
template<class Real>
void test_sample_gini_coefficient()
{
@@ -471,6 +538,11 @@ int main()
test_median<long double>();
test_median<cpp_bin_float_50>();
test_median_absolute_deviation<float>();
test_median_absolute_deviation<double>();
test_median_absolute_deviation<long double>();
test_median_absolute_deviation<cpp_bin_float_50>();
test_gini_coefficient<float>();
test_gini_coefficient<double>();
test_gini_coefficient<long double>();