I was wondering if there was a more efficient way to do it than with manual for loops.
EDIT: This answer requires the devel branch of eigen! I've been using that branch for so long, that I forgot that e.g. reshaped
is not part of the stable branch...
There is a way. Median calculation typically requires sorting, so that's what I did here, too. If you have a mutable object/expression, its values are sorted and the median is calculated. If you have a const object/expression, a copy is sorted and the median calculated. Hope this helps!
#include <Eigen/Dense>
#include <iostream>
#include <algorithm>
template<typename Derived>
typename Derived::Scalar median( Eigen::DenseBase<Derived>& d ){
auto r { d.reshaped() };
std::sort( r.begin(), r.end() );
return r.size() % 2 == 0 ?
r.segment( (r.size()-2)/2, 2 ).mean() :
r( r.size()/2 );
}
template<typename Derived>
typename Derived::Scalar median( const Eigen::DenseBase<Derived>& d ){
typename Derived::PlainObject m { d.replicate(1,1) };
return median(m);
}
int main(){
// vector with odd length
Eigen::Vector3f vec1 { 0.1, 2.3, 1.1 };
std::cout << median(vec1) << " (expected: 1.1)\n";
// vector with even length
Eigen::Vector4f vec2 { 1, 3, 0, 2 };
std::cout << median(vec2) << " (expected: 1.5)\n";
Eigen::Matrix3f mat;
mat <<
0, 8, 3,
2, 1, 5,
4, 7, 6;
// const reference (operates on a copy)
const Eigen::Matrix3f& matCRef { mat };
std::cout << median(matCRef) << " (expected: 4)\n";
// block expression (operates on a copy of that expression)
auto& cBlock { matCRef.block(1,1,2,2) };
std::cout << median(cBlock) << " (expected: 5.5)\n";
// matrix itself last, because it's going to be sorted afterwards:
std::cout << median(mat) << " (expected: 4)\n";
return 0;
}
So, for an odd number of coefficients, the median value is returned, for an even number of coefficients, the average between lower and upper median is returned.