Search code examples
c++eigenmedian

Is there a way to find the median value of coefficients of an Eigen Matrix?


I was wondering if there was a more efficient way to do it than with manual for loops.


Solution

  • 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.