专栏总目录
本文目录
- 概述
- 基本的切片
- 编译时的大小和步长
- 倒序
- 索引序列
- 自定义索引列表
英文原文(Slicing and Indexing)
本文介绍了如何使用操作运算符operator()
索引行和列的子集。该 API 在 Eigen 3.4 中引入。它支持 block API 提供的所有功能。特别是,它支持切片,即获取一组行、列或元素,以及等间隔的从矩阵或者数组中提取元素。
概述
所有上述操作都是通过DenseBase::operator()(const RowIndices&, const ColIndices&)
来完成的,每一个参数可以是:
- 索引单行或列的整数,包括符号索引
- 符号
Eigen::all
表示按递增顺序排列的所有行或列 - 由
Eigen::seq
,Eigen::seqN
或者Eigen::placeholders::lastN
函数构造的算数序列 - 任意一维整数向量、数组,形式如Eigen
向量
、数组
、表达式
、std::vector
、std::array
、 C的数组int[N]
更一般的,该函数可以接受任何有下列两个成员函数接口的对象
<integral type> operator[](<integral type>) const;
<integral type> size() const;
其中<integral type>
代表任何可以与Eigen::index
兼容的整数,如std::ptrdiff_t
基本的切片
通过Eigen::seq
或Eigen::seqN
函数,取矩阵或向量中均匀间隔的一组行、列或元素,其中seq
代表等差数列,用法如下:
方法 | 描述 | 示例 |
---|---|---|
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()
传递给它,firstIdx
和lastIdx
参数也可以用Eigen::last
符号来定义,该符号表示矩阵/向量的最后一行、最后一列或最后一个元素的索引,使用如下:
目的 | 代码 | 等价的块操作 |
---|---|---|
包含从第i行到最后一行,从第0列开始,共n列的块 | A(seq(i,last), seqN(0,n)) | A.bottomLeftCorner(A.rows()-i,n) |
包含从第i行到第m行,从第j列开始,共n列的块 | A(seqN(i,m), seqN(j,n)) | A.block(i,j,m,n) |
包含从第i0行到第i1行,从第j0列到第j1列的块 | A(seq(i0,i1), seq(j0,j1) | A.block(i0,j0,i1-i0+1,j1-j0+1) |
A的偶数列 | A(all, seq(0,last,2)) | |
A的前n个奇数行 | A(seqN(1,n,2), all) | |
倒数第二列 | A(all, last-1) | A.col(A.cols()-2) |
A的中间一行 | A(last/2,all) | A.row((A.rows()-1)/2) |
向量v从第i个元素到最后一个元素 | v(seq(i,last)) | v.tail(v.size()-i) |
向量v的最后n个元素 | v(seq(last+1-n,last)) | v.tail(n) |
示例如下:
// 代码索引 3-5-1-1
MatrixXi A = MatrixXi::Random(7, 6);
cout << "Initial matrix A:\n"<< A << "\n\n";// 包含从第i行到最后一行,从第0列开始,共n列的块(i: 2, n: 3)
cout << "A(seq(i,Eigen::last), seqN(0,n)): (i: 2, n: 3)\n"<< A(seq(2, Eigen::last), seqN(0, 3)) << "\n\n";// 包含从第i行到第m行,从第j列开始,共n列的块(i: 1, m: 2, j: 2, n: 4)
cout << "A(seq(i,m), seqN(j,n)): (i: 1, m: 2, j: 2, n: 4)\n"<< A(seqN(1, 2), seqN(2, 4)) << "\n\n";// 包含从第i0行到第i1行,从第j0列到第j1列的块(i0: 1, i1: 2, j0: 2, j1: 4)
cout << "A(seq(i0,i1), seq(j0,j1)): (i0: 1, i1: 2, j0: 2, j1: 4)\n"<< A(seq(1, 2), seq(2, 4)) << "\n\n";// A的偶数列
cout << "A的偶数列: \n"<< A(Eigen::all, seq(0, Eigen::last, 2)) << "\n\n";// A的前n个奇数行 (n: 3)
cout << "A的前n个奇数行: (n: 3)\n"<< A(seqN(1, 3, 2), Eigen::all) << "\n\n";// A的倒数第二列
cout << "A的倒数第二列: \n"<< A(Eigen::all, Eigen::last-1) << "\n\n";// A的中间一行
cout << "A的中间一行: \n"<< A(Eigen::last/2, Eigen::all) << "\n\n";Eigen::VectorXi v{{4,2,5,8,3}};
cout << "Initial vector v:\n"<< v << "\n\n";// 向量v从第i个元素到最后一个元素 (i: 2)
cout << "向量v从第i个元素到最后一个元素: (i: 2)\n"<< v(seq(2, Eigen::last)) << "\n\n";// 向量v最后n个元素
cout << "向量v最后n个元素: (n: 3)\n"<< v(seq(Eigen::last-3+1, Eigen::last)) << "\n\n";
输出如下:
Initial matrix A:730547559 576018668 971155939 -552146456 1071432243 52156343-226810938 -477225175 893772102 -779039257 -605038689 -13780431607950953 115899597 291438716 653214605 27772105 1015276632640895091 -48539462 466641602 -737276042 728237978 -445566813884005969 276748203 -769652652 -212720294 241892198 582736218-649503489 -290373134 229713912 -795018962 -438018766 57434405-353856438 28778235 -1038736613 -840076701 295391245 579635549A(seq(i,Eigen::last), seqN(0,n)): (i: 2, n: 3)607950953 115899597 291438716640895091 -48539462 466641602884005969 276748203 -769652652-649503489 -290373134 229713912-353856438 28778235 -1038736613A(seq(i,m), seqN(j,n)): (i: 1, m: 2, j: 2, n: 4)893772102 -779039257 -605038689 -13780431291438716 653214605 27772105 1015276632A(seq(i0,i1), seq(j0,j1)): (i0: 1, i1: 2, j0: 2, j1: 4)893772102 -779039257 -605038689291438716 653214605 27772105A的偶数列: 730547559 971155939 1071432243-226810938 893772102 -605038689607950953 291438716 27772105640895091 466641602 728237978884005969 -769652652 241892198-649503489 229713912 -438018766-353856438 -1038736613 295391245A的前n个奇数行: (n: 3)
-226810938 -477225175 893772102 -779039257 -605038689 -13780431640895091 -48539462 466641602 -737276042 728237978 -445566813
-649503489 -290373134 229713912 -795018962 -438018766 57434405A的倒数第二列:
1071432243
-60503868927772105728237978241892198
-438018766295391245A的中间一行: 640895091 -48539462 466641602 -737276042 728237978 -445566813Initial vector v:
4
2
5
8
3向量v从第i个元素到最后一个元素: (i: 2)
5
8
3向量v最后n个元素: (n: 3)
5
8
3
正如在上一个示例中看到的,引用最后n个元素(或行/列)编写起来有点麻烦。使用非默认增量时,这将变得更加棘手和容易出错。因此,Eigen提供了Eigen::placeholders::lastN(size)
和Eigen::placeholders::lastN(size,incr)
函数来完成最后几个元素的提取,用法如下:
Eigen官方建议使用
Eigen::lastN(size)
和Eigen::lastN(size,incr)
代替Eigen::placeholders::lastN(size)
和Eigen::placeholders::lastN(size,incr)
目的 | 代码 | 等价的块操作 |
---|---|---|
向量v的最后n个元素 | v(lastN(n)) | v.tail(n) |
A右下角m行n列的块 | A(lastN(m), lastN(n)) | A.bottomRightCorner(m,n) |
A的最后n列,步长为m | A(all, lastN(n,m)) |
示例如下:
Eigen::VectorXi v{{4,2,5,8,3}};
cout << "Initial vector v:\n"<< v << "\n\n";// Eigen提供最后几个元素的提取函数
// 向量v最后n个元素
cout << "向量v最后n个元素: (n: 3)\n"<< v(Eigen::lastN(3)) << "\n\n";MatrixXi A = MatrixXi::Random(7, 6);
cout << "Initial matrix A:\n"<< A << "\n\n";// A右下角m行n列的块
cout << "A右下角m行n列的块: (m: 3, n: 2)\n"<< A(Eigen::lastN(3), Eigen::lastN(2)) << "\n\n";// A的最后n列,步长为m
cout << "A的最后n列, 步长为m: (n: 3, m: 2)\n"<< A(Eigen::all, Eigen::lastN(3, 2)) << "\n\n";
输出如下:
Initial vector v:
4
2
5
8
3向量v最后n个元素: (n: 3)
5
8
3Initial matrix A:730547559 576018668 971155939 -552146456 1071432243 52156343-226810938 -477225175 893772102 -779039257 -605038689 -13780431607950953 115899597 291438716 653214605 27772105 1015276632640895091 -48539462 466641602 -737276042 728237978 -445566813884005969 276748203 -769652652 -212720294 241892198 582736218-649503489 -290373134 229713912 -795018962 -438018766 57434405-353856438 28778235 -1038736613 -840076701 295391245 579635549A右下角m行n列的块: (m: 3, n: 2)241892198 582736218
-438018766 57434405295391245 579635549A的最后n列, 步长为m: (n: 3, m: 2)576018668 -552146456 52156343
-477225175 -779039257 -13780431115899597 653214605 1015276632-48539462 -737276042 -445566813276748203 -212720294 582736218
-290373134 -795018962 5743440528778235 -840076701 579635549
编译时的大小和步长
在性能方面,Eigen和编译器可以利用编译时的大小和步长。为此,可以使用Eigen::fix<val>
在编译时强制指定大小。而且,它可以和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>))
倒序
也可以把步长设置为负数,按降序枚举行/列索引,例如,从第 20 列开始到第 10 列结束, 步长为-2
:
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<int>
, std::array<int,N>
等,如:
示例:
std::vector<int> 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";
输出如下:
Initial matrix A:7 9 -5 -3 3 -10-2 -6 1 0 5 -56 -3 0 9 -8 -86 6 3 9 2 6A(all,ind):3 -5 -10 -10 -35 1 -5 -5 0-8 0 -8 -8 92 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";
输出:
Initial matrix A:7 9 -5 -3 3 -10-2 -6 1 0 5 -56 -3 0 9 -8 -86 6 3 9 2 6A(all,{4,2,5,5,3}):3 -5 -10 -10 -35 1 -5 -5 0-8 0 -8 -8 92 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";
输出:
Initial matrix A:7 9 -5 -3 3 -10-2 -6 1 0 5 -56 -3 0 9 -8 -86 6 3 9 2 6A(all,ind-1):
-3 9 3 3 -50 -6 5 5 19 -3 -8 -8 09 6 2 2 3
当传递一个具有编译时大小的对象(如Array4i
、std::array<int, N>
或静态数组)时,返回的表达式也会显示编译时维度。
自定义索引列表
更一般的,operator()
可以接受任何类型的对象:
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<Index>(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";
输出:
Initial matrix A:
1 4 7
2 5 8
3 6 9A(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