Skip to content

Commit

Permalink
median_absolute_deviation bug fix for non-zero center (#997)
Browse files Browse the repository at this point in the history
Fix bug causing median_absolute_deviation to return incorrect values
when a non-zero center (such as median which is the default) was used.
  • Loading branch information
rasmushenningsson authored Jun 19, 2023
1 parent 7ce0570 commit 0852c16
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 14 deletions.
8 changes: 4 additions & 4 deletions include/boost/math/statistics/univariate_statistics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,14 +505,14 @@ auto median_absolute_deviation(ExecutionPolicy&& exec, RandomAccessIterator firs
{
auto middle = first + (num_elems - 1)/2;
std::nth_element(exec, first, middle, last, comparator);
return abs(*middle);
return abs(*middle-center);
}
else
{
auto middle = first + num_elems/2 - 1;
std::nth_element(exec, first, middle, last, comparator);
std::nth_element(exec, middle, middle+1, last, comparator);
return (abs(*middle) + abs(*(middle+1)))/abs(static_cast<Real>(2));
return (abs(*middle-center) + abs(*(middle+1)-center))/abs(static_cast<Real>(2));
}
}

Expand Down Expand Up @@ -1043,14 +1043,14 @@ Real median_absolute_deviation(RandomAccessIterator first, RandomAccessIterator
{
auto middle = first + (num_elems - 1)/2;
std::nth_element(first, middle, last, comparator);
return abs(*middle);
return abs(*middle-center);
}
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));
return (abs(*middle-center) + abs(*(middle+1)-center))/abs(static_cast<Real>(2));
}
}

Expand Down
65 changes: 60 additions & 5 deletions test/univariate_statistics_backwards_compatible_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,11 +612,7 @@ void test_median_absolute_deviation()
v = {-1, 1};
m = boost::math::statistics::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::statistics::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 1);

m = boost::math::statistics::median_absolute_deviation(v);
m = boost::math::statistics::median_absolute_deviation(v, 0);
BOOST_TEST_EQ(m, 1);


Expand Down Expand Up @@ -649,6 +645,65 @@ void test_median_absolute_deviation()
u[5] = -3;
m = boost::math::statistics::median_absolute_deviation(u, 0);
BOOST_TEST_EQ(m, 2);


v = {-1, 2, -3, 4, -5, 6, -7};
m = boost::math::statistics::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 4);

g = std::mt19937(12);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::statistics::median_absolute_deviation(v);
BOOST_TEST_EQ(m, 4);

v = {1, -2, -3, 3, -4, -5};
m = boost::math::statistics::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 2);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::statistics::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 2);

v = {-1};
m = boost::math::statistics::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 0);

v = {-1, 1};
m = boost::math::statistics::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 1);

m = boost::math::statistics::median_absolute_deviation(v);
BOOST_TEST_EQ(m, 1);


v = {2, -4};
m = boost::math::statistics::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 3);

v = {1, -1, 1};
m = boost::math::statistics::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 0);

v = {1, 2, -3};
m = boost::math::statistics::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 1);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::statistics::median_absolute_deviation(v.begin(), v.end());
BOOST_TEST_EQ(m, 1);

w = {1, 2, -3};
m = boost::math::statistics::median_absolute_deviation(w);
BOOST_TEST_EQ(m, 1);

// boost.ublas vector?
boost::numeric::ublas::vector<Real> u2(6);
u2[0] = 1;
u2[1] = 2;
u2[2] = -3;
u2[3] = 1;
u2[4] = 2;
u2[5] = -3;
m = boost::math::statistics::median_absolute_deviation(u2);
BOOST_TEST_EQ(m, 1);
}


Expand Down
65 changes: 60 additions & 5 deletions test/univariate_statistics_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,11 +577,7 @@ void test_median_absolute_deviation(ExecutionPolicy&& exec)
v = {-1, 1};
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end(), 0);
BOOST_TEST_EQ(m, 1);
// The median is zero, so coincides with the default:
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end());
BOOST_TEST_EQ(m, 1);

m = boost::math::statistics::median_absolute_deviation(exec, v);
m = boost::math::statistics::median_absolute_deviation(exec, v, 0);
BOOST_TEST_EQ(m, 1);


Expand Down Expand Up @@ -614,6 +610,65 @@ void test_median_absolute_deviation(ExecutionPolicy&& exec)
u[5] = -3;
m = boost::math::statistics::median_absolute_deviation(exec, u, 0);
BOOST_TEST_EQ(m, 2);


v = {-1, 2, -3, 4, -5, 6, -7};
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end());
BOOST_TEST_EQ(m, 4);

g = std::mt19937(12);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::statistics::median_absolute_deviation(exec, v);
BOOST_TEST_EQ(m, 4);

v = {1, -2, -3, 3, -4, -5};
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end());
BOOST_TEST_EQ(m, 2);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end());
BOOST_TEST_EQ(m, 2);

v = {-1};
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end());
BOOST_TEST_EQ(m, 0);

v = {-1, 1};
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end());
BOOST_TEST_EQ(m, 1);

m = boost::math::statistics::median_absolute_deviation(exec, v);
BOOST_TEST_EQ(m, 1);


v = {2, -4};
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end());
BOOST_TEST_EQ(m, 3);

v = {1, -1, 1};
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end());
BOOST_TEST_EQ(m, 0);

v = {1, 2, -3};
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end());
BOOST_TEST_EQ(m, 1);
std::shuffle(v.begin(), v.end(), g);
m = boost::math::statistics::median_absolute_deviation(exec, v.begin(), v.end());
BOOST_TEST_EQ(m, 1);

w = {1, 2, -3};
m = boost::math::statistics::median_absolute_deviation(exec, w);
BOOST_TEST_EQ(m, 1);

// boost.ublas vector?
boost::numeric::ublas::vector<Real> u2(6);
u2[0] = 1;
u2[1] = 2;
u2[2] = -3;
u2[3] = 1;
u2[4] = 2;
u2[5] = -3;
m = boost::math::statistics::median_absolute_deviation(exec, u2);
BOOST_TEST_EQ(m, 1);
}


Expand Down

0 comments on commit 0852c16

Please sign in to comment.