16

Suppose I have

Eigen::VectorXd x; //{1,2,3,4,5,6,7,8}

and

Eigen::VectorXd ind_vec; //{0,2,4,5}

Is there a way an easy way to extract the ind_vec elements of x?

Something like:

x.extract(ind_vec) returning {1, 3, 5, 6}
chrisaycock
  • 36,470
  • 14
  • 88
  • 125
user1526533
  • 441
  • 1
  • 5
  • 9
  • Is there something easier than specifying the indices directly? You have the elements and their indices. What else do you want? – CroCo Oct 09 '14 at 21:57
  • Might be a good idea to use `Eigen::VectorXi` rather than `Eigen::VectorXd` for indices. – Alec Jacobson Apr 18 '20 at 17:18

6 Answers6

18

Since the current answer was not satisfactory for me, I googled a bit and I found this tutorial in the Eigen documentation.

#include <Eigen/Dense>
#include <iostream>
using namespace std;
int main()
{
  Eigen::ArrayXf v(6);
  v << 1, 2, 3, 4, 5, 6;
  cout << "v.head(3) =" << endl << v.head(3) << endl << endl;
  cout << "v.tail<3>() = " << endl << v.tail<3>() << endl << endl;
  v.segment(1,4) *= 2;
  cout << "after 'v.segment(1,4) *= 2', v =" << endl << v << endl;
}

Will output:

v.head(3) =
1
2
3

v.tail<3>() = 
4
5
6

after 'v.segment(1,4) *= 2', v =
 1
 4
 6
 8
10
 6

I haven't tested it with vectors, but I guess should be possible as well.

Javi
  • 3,440
  • 5
  • 29
  • 43
  • 6
    This works only for consecutive indices and doesn't answer the OP's question (Eigen::VectorXd ind_vec; //{0,2,4,5}) – kingusiu Apr 22 '18 at 21:49
11

In C++ 11 (and up) do this:

 ind_vec.unaryExpr(x);

You can make use of unaryExpr(Functor) since we are taking an index array and applying a functor to each element of the array. The result type will have the same dimensions as the index array. For the functor, we need an object with an operator:

 Scalar operator() (Index index) const {
     return x[index];
 }

As it happens, Eigen::Matrix has just such an operator already. Here's a full example:

Eigen::VectorXd x(8);  x << 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8;
Eigen::Array4i ind_vec(0,2,4,5);

// result has dimensions as ind_vec matrix/array, and scalar type from x
Eigen::Array4d result = ind_vec.unaryExpr(x);

std::cout << "result^T = " << result.transpose() << std::endl;

// Output:
// result^T = 1.1 3.3 5.5 6.6

The only caveat is that this requires at least C++11 to work. The problem is that eigen internally relies upon std::result_of to get the scalar result type. Without that, you might get errors stating that a cast is required.

ɲeuroburɳ
  • 6,990
  • 3
  • 24
  • 22
  • +1 since this is the closest answer to the OP's question. I only have a remark about the example code: in my case (running C++14) 'ind_vec' should be an array of double as well, otherwise 'result' would be filled with casted integer version of the entries in the input vector 'x' – Marco Ferro Sep 06 '21 at 20:01
5

Seems like it'd be easy to write yourself if it's just for vectors:

#include "Eigen/Core"

template <typename T, typename T2>
T extract(const T2& full, const T& ind)
{
    int num_indices = ind.innerSize();
    T target(num_indices);
    for (int i = 0; i < num_indices; i++)
    {
        target[i] = full[ind[i]];
    }
    return target;
}

int main()
{
    Eigen::VectorXd full(8); 
    full << 1, 2, 3, 4, 5, 6, 7, 8;
    Eigen::Vector4d ind_vec(4);
    ind_vec << 0, 2, 4, 5;
    std::cout << "full:" << full<< std::endl;
    std::cout << "ind_vec:" << ind_vec<< std::endl;
    std::cout << "extracted" << extract(full,ind_vec) << std::endl;
}

That should work for most cases

edit: for cases where your index scalar type is different than your source and target scalar type the following will work (for all build-in Eigen types).

template <typename T, typename T2>
Eigen::Matrix<typename T2::Scalar,T::RowsAtCompileTime,T::ColsAtCompileTime,T::Options> 
extract2(const Eigen::DenseBase<T2>& full, const Eigen::DenseBase<T>& ind)
{
    using target_t = Eigen::Matrix < T2::Scalar, T::RowsAtCompileTime, T::ColsAtCompileTime, T::Options > ;
    int num_indices = ind.innerSize();
    target_t target(num_indices);
    for (int i = 0; i < num_indices; i++)
    {
        target[i] = full[ind[i]];
    }
    return target;
} 

(this is different from the other one in that you can use a vector of ints as indices and a vector of doubles as source and get a vector of doubles returned instead of a vector of ints as extract() above would do)

PeterT
  • 7,981
  • 1
  • 26
  • 34
  • Thanks! I'd guess that is the most efficient way. Though I wonder if this is what is done behind the scenes in subvector extraction in Matlab: something like x[[1,2,4,8]]. Does it go through a for loop over 1,2,4,8 like your solution or something even more efficient? – user1526533 Oct 09 '14 at 05:41
  • @user1526533, Matlab is different than C++. – CroCo Oct 09 '14 at 22:00
3

This is now supported in Eigen 3.4 via slicing and indexing:

Eigen::VectorXd x(8); x<<1,2,3,4,5,6,7,8;
Eigen::VectorXi ind_vec(4); ind_vec<<0,2,4,5;

Eigen::VectorXd x_slice = x(ind_vec);

As the docs note, ind_vec can also be

an arbitrary list of row or column indices stored as either an ArrayXi, a std::vector, std::array<int,N>, etc.

wacava
  • 401
  • 4
  • 6
0

Using libigl's igl::slice, you can achieve this via:

Eigen::VectorXd result;
igl::slice(x,ind_vec,result);
Alec Jacobson
  • 6,032
  • 5
  • 51
  • 88
0

since Eigen 3.4, you can slice Eigen Vectors and Matrices using an array of indices as follows:

Eigen::VectorXd x; //{1,2,3,4,5,6,7,8}
Eigen::VectorXd ind_vec; //{0,2,4,5}

x(ind_vec);             // indexes the vector using a vector of indices
x(ind_vec, Eigen::all); // also works

taken from: https://eigen.tuxfamily.org/dox/group__TutorialSlicingIndexing.html

You can similarly index rows and vectors of matrices in this manner

greygore
  • 66
  • 5