开源地址,欢迎star
Eigen: Slicing and Indexing
本文展示如果使用操作运算符operator()
来索引行和列的子集。这个API是在Eigen 3.4中完成的。它拥有 block API提供的所有功能,甚至更多。特别的,它支持包含提取一行、一列、单个元素的操作以及等间隔的从矩阵或者数组中提取元素。
上述所以提到的操作都是用DenseBase::operator()(const RowIndices&, const ColIndices&)
来完成的,每一个参数可以是:
Eigen::seq
, Eigen::seqN
或者 Eigen::placeholders::lastN
函数构造的ArithmeticSequencestd::vector
, C的数组int[N]
更一般的,该函数可以接受任何有下列两个成员函数接口的对象
operator[]() const;
size() const;
其中
代表任何可以与Eigen::index
兼容的整数,如std::ptrdiff_t
通过Eigen::seq
或Eigen::seqN
函数,取矩阵或向量中均匀间隔的一组行、列或元素,其中seq代表等差数列。他们的用法如下:
function | description | example |
---|---|---|
seq(firstIdx,lastIdx) | 返回从firstIdx 到 lastIdx 的整数 |
seq(2,5) <=> {2,3,4,5} |
seq(firstIdx,lastIdx,incr) | 与上面相同,但是索引每次增加incr | seq(2,8,2) <=> {2,4,6,8} |
seqN(firstIdx,size) | 从firstIdx 开始,索引每次加1,总的个数为size |
seqN(2,5) <=> {2,3,4,5,6} |
seqN(firstIdx,size,incr) | 与上述相同,索引每次增加incr | seqN(2,3,3) <=> {2,5,8} |
一旦等差序列通过operator()传递给它,firststidx
和lasttidx
参数也可以用Eigen::last
符号来定义,该符号表示矩阵/向量的最后一行、最后一列或元素的索引,使用如下:
Intent | Code | Block-API equivalence |
---|---|---|
Bottom-left corner starting at row i with n columns |
A(seq(i,last), seqN(0,n)) | A.bottomLeftCorner(A.rows()-i,n) |
Block starting at i ,j having m rows, and n columns |
A(seqN(i,m), seqN(i,n)) | A.block(i,j,m,n) |
Block starting at i0 ,j0 and ending at i1 ,j1 |
A(seq(i0,i1), seq(j0,j1) | A.block(i0,j0,i1-i0+1,j1-j0+1) |
Even columns of A | A(all, seq(0,last,2)) | |
First n odd rows A |
A(seqN(1,n,2), all) | |
The last past one column | A(all, last-1) | A.col(A.cols()-2) |
The middle row | A(last/2,all) | A.row((A.rows()-1)/2) |
Last elements of v starting at i | v(seq(i,last)) | v.tail(v.size()-i) |
Last n elements of v |
v(seq(last+1-n,last)) | v.tail(n) |
正如在上一个示例中看到的,引用最后n个元素(或行/列)编写起来有点麻烦。使用非默认增量时,这将变得更加棘手和容易出错。因此,Eigen提供了Eigen::placeholders::lastN(size)和Eigen::placeholders::lastN(size,incr) 函数来完成最后几个元素的提取,用法如下:
Intent | Code | Block-API equivalence |
Last n elements of v |
v(lastN(n)) | v.tail(n) |
Bottom-right corner of A of size m times n |
v(lastN(m), lastN(n)) | A.bottomRightCorner(m,n) |
Bottom-right corner of A of size m times n |
v(lastN(m), lastN(n)) | A.bottomRightCorner(m,n) |
Last n columns taking 1 column over 3 |
A(all, lastN(n,3)) |
在性能方面,Eigen和编译器可以利用编译时的大小和增量。为了这个目的,你可以使用Eigen::fix
在编译时刻强制指定大小。而且,它可以和Eigen::last
符号一起使用:
v(seq(last-fix<7>, last-fix<2>))
在这个例子中,Eigen在编译时就知道返回的表达式有6个元素。它等价于:
v(seqN(last-7, fix<6>))
我们可以查看A的偶数列,如:
A(all, seq(0,last,fix<2>))
我们可以把增量设置为负数,如该例子实现A的20到10列中取一半:
A(all, seq(20, 10, fix<-2>))
从最后一个开始,取n个数:
A(seqN(last, n, fix<-1>), all)
也可以使用函数ArithmeticSequence::reverse()
方法来反转序列,之前的例子也可以写作:
A(lastN(n).reverse(), all)
operator()
输入的也可以是ArrayXi
, std::vector
, std::array
,如:
Example:
std::vector ind{4,2,5,5,3};
MatrixXi A = MatrixXi::Random(4,6);
cout << "Initial matrix A:\n" << A << "\n\n";
cout << "A(all,ind):\n" << A(Eigen::placeholders::all,ind) << "\n\n";
Output:
Initial matrix A:
7 9 -5 -3 3 -10
-2 -6 1 0 5 -5
6 -3 0 9 -8 -8
6 6 3 9 2 6
A(all,ind):
3 -5 -10 -10 -3
5 1 -5 -5 0
-8 0 -8 -8 9
2 3 6 6 9
MatrixXi A = MatrixXi::Random(4,6);
cout << "Initial matrix A:\n" << A << "\n\n";
cout << "A(all,{4,2,5,5,3}):\n" << A(Eigen::placeholders::all,{4,2,5,5,3}) << "\n\n";
Output:
Initial matrix A:
7 9 -5 -3 3 -10
-2 -6 1 0 5 -5
6 -3 0 9 -8 -8
6 6 3 9 2 6
A(all,{4,2,5,5,3}):
3 -5 -10 -10 -3
5 1 -5 -5 0
-8 0 -8 -8 9
2 3 6 6 9
ArrayXi ind(5); ind<<4,2,5,5,3;
MatrixXi A = MatrixXi::Random(4,6);
cout << "Initial matrix A:\n" << A << "\n\n";
cout << "A(all,ind-1):\n" << A(Eigen::placeholders::all,ind-1) << "\n\n";
Output:
Initial matrix A:
7 9 -5 -3 3 -10
-2 -6 1 0 5 -5
6 -3 0 9 -8 -8
6 6 3 9 2 6
A(all,ind-1):
-3 9 3 3 -5
0 -6 5 5 1
9 -3 -8 -8 0
9 6 2 2 3
当传递一个具有编译时大小的对象(如Array4i
、std::array
或静态数组)时,返回的表达式也会显示编译时维度。
更一般的,operator()
可以接受任何与下列兼容的T类型的对象ind:
Index s = ind.size(); or Index s = size(ind);
Index i;
i = ind[i];
这意味着您可以轻松地构建自己的序列生成器并将其传递给operator()。下面是一个例子,扩大一个给定的矩阵,同时通过重复填充额外的第一行和列:
struct pad {
Index size() const { return out_size; }
Index operator[] (Index i) const { return std::max(0,i-(out_size-in_size)); }
Index in_size, out_size;
};
Matrix3i A;
A.reshaped() = VectorXi::LinSpaced(9,1,9);
cout << "Initial matrix A:\n" << A << "\n\n";
MatrixXi B(5,5);
B = A(pad{3,5}, pad{3,5});
cout << "A(pad{3,N}, pad{3,N}):\n" << B << "\n\n";
Output:
Initial matrix A:
1 4 7
2 5 8
3 6 9
A(pad{3,N}, pad{3,N}):
1 1 1 4 7
1 1 1 4 7
1 1 1 4 7
2 2 2 5 8
3 3 3 6 9