[Eigen中文文档] 切片和索引

简介: 本文介绍了如何使用操作运算符operator()索引行和列的子集。该 API 在 Eigen 3.4 中引入。它支持 block API 提供的所有功能。特别是,它支持切片,即获取一组行、列或元素,以及等间隔的从矩阵或者数组中提取元素。

专栏总目录

英文原文(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::vectorstd::array 、 C的数组int[N]

更一般的,该函数可以接受任何有下列两个成员函数接口的对象

<integral type> operator[](<integral type>) const;
<integral type> size() const;

其中<integral type> 代表任何可以与Eigen::index兼容的整数,如std::ptrdiff_t

基本的切片

通过Eigen::seqEigen::seqN函数,取矩阵或向量中均匀间隔的一组行、列或元素,其中seq代表等差数列,用法如下:

方法 描述 示例
seq(firstIdx,lastIdx) 返回从firstIdxlastIdx的整数序列 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()传递给它,firstIdxlastIdx参数也可以用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(seq(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   -13780431
  607950953   115899597   291438716   653214605    27772105  1015276632
  640895091   -48539462   466641602  -737276042   728237978  -445566813
  884005969   276748203  -769652652  -212720294   241892198   582736218
 -649503489  -290373134   229713912  -795018962  -438018766    57434405
 -353856438    28778235 -1038736613  -840076701   295391245   579635549

A(seq(i,Eigen::last), seqN(0,n)): (i: 2, n: 3)
  607950953   115899597   291438716
  640895091   -48539462   466641602
  884005969   276748203  -769652652
 -649503489  -290373134   229713912
 -353856438    28778235 -1038736613

A(seq(i,m), seqN(j,n)): (i: 1, m: 2, j: 2, n: 4)
 893772102 -779039257 -605038689  -13780431
 291438716  653214605   27772105 1015276632

A(seq(i0,i1), seq(j0,j1)): (i0: 1, i1: 2, j0: 2, j1: 4)
 893772102 -779039257 -605038689
 291438716  653214605   27772105

A的偶数列: 
  730547559   971155939  1071432243
 -226810938   893772102  -605038689
  607950953   291438716    27772105
  640895091   466641602   728237978
  884005969  -769652652   241892198
 -649503489   229713912  -438018766
 -353856438 -1038736613   295391245

A的前n个奇数行: (n: 3)
-226810938 -477225175  893772102 -779039257 -605038689  -13780431
 640895091  -48539462  466641602 -737276042  728237978 -445566813
-649503489 -290373134  229713912 -795018962 -438018766   57434405

A的倒数第二列: 
1071432243
-605038689
  27772105
 728237978
 241892198
-438018766
 295391245

A的中间一行: 
 640895091  -48539462  466641602 -737276042  728237978 -445566813

Initial 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
3

Initial matrix A:
  730547559   576018668   971155939  -552146456  1071432243    52156343
 -226810938  -477225175   893772102  -779039257  -605038689   -13780431
  607950953   115899597   291438716   653214605    27772105  1015276632
  640895091   -48539462   466641602  -737276042   728237978  -445566813
  884005969   276748203  -769652652  -212720294   241892198   582736218
 -649503489  -290373134   229713912  -795018962  -438018766    57434405
 -353856438    28778235 -1038736613  -840076701   295391245   579635549

A右下角m行n列的块: (m: 3, n: 2)
 241892198  582736218
-438018766   57434405
 295391245  579635549

A的最后n列, 步长为m: (n: 3, m: 2)
 576018668 -552146456   52156343
-477225175 -779039257  -13780431
 115899597  653214605 1015276632
 -48539462 -737276042 -445566813
 276748203 -212720294  582736218
-290373134 -795018962   57434405
  28778235 -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  -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";

输出:

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";

输出:

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

当传递一个具有编译时大小的对象(如Array4istd::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 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
相关文章
|
9月前
|
编译器 索引
[Eigen中文文档] 块操作
本文介绍了块操作。块是matrix或array的部分矩形元素。块表达式既可以用作右值也可以用作左值。与Eigen表达式一样,如果让编译器进行优化,则块操作的运行时间成本为零。
77 0
|
9月前
|
存储 算法 NoSQL
[Eigen中文文档] 存储顺序
矩阵和二维数组有两种不同的存储顺序:列优先和行优先。本节解释了这些存储顺序以及如何指定应该使用哪一种。
87 0
|
8天前
|
BI 测试技术 索引
Python学习笔记之NumPy模块——超详细(安装、数组创建、正态分布、索引和切片、数组的复制、维度修改、拼接、分割...)-1
Python学习笔记之NumPy模块——超详细(安装、数组创建、正态分布、索引和切片、数组的复制、维度修改、拼接、分割...)
|
1月前
|
机器学习/深度学习 计算机视觉 索引
NumPy索引与切片的高级技巧探索
【4月更文挑战第17天】探索NumPy的高级索引与切片技巧:整数数组、布尔和花式索引用于灵活选取元素;切片步长、反转及多维切片操作实现高效数组处理。在数据分析、图像处理和机器学习等领域有广泛应用,提升代码效率与可读性。
|
9月前
|
存储 算法 NoSQL
[Eigen中文文档] 稀疏矩阵操作
在许多应用中(例如,有限元方法),通常要处理非常大的矩阵,其中只有少数系数不为零。在这种情况下,可以通过使用仅存储非零系数的特殊表示来减少内存消耗并提高性能。这样的矩阵称为稀疏矩阵。
273 0
|
1月前
|
索引 Python 数据处理
【Python Numpy教程】切片和索引
【Python Numpy教程】切片和索引
【Python Numpy教程】切片和索引
|
1月前
[Halcon&笔记] Tuple类型数组
[Halcon&笔记] Tuple类型数组
42 1
|
9月前
[Eigen中文文档] 固定大小的可向量化Eigen对象
本文主要解释 固定大小可向量化 的含义。
61 0
|
9月前
|
存储 数据建模 API
[Eigen中文文档] 稀疏矩阵快速参考指南
本页面简要介绍了类SparseMatrix中可用的主要操作。在处理稀疏矩阵时,重要的一点是要了解它们的存储方式:即行优先或列优先,Eigen默认为列优先。对稀疏矩阵进行的大多数算术操作都会默认它们具有相同的存储顺序。
135 0
|
9月前
|
测试技术
[Eigen中文文档] Array类与元素操作
与Matrix类用于线性代数计算不同的是,Array类提供了通用目的数组。此外,Array类提供了一种执行按系数运算的简单方法,这可能没有线性代数意义,例如对每一个元素都加一个常数或按系数将两个数组相乘。
131 0