[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
相关文章
|
存储 算法 NoSQL
[Eigen中文文档] 存储顺序
矩阵和二维数组有两种不同的存储顺序:列优先和行优先。本节解释了这些存储顺序以及如何指定应该使用哪一种。
165 0
|
3月前
|
机器学习/深度学习 数据可视化 API
Python Numpy 切片和索引(高级索引、布尔索引、花式索引)
Python Numpy 切片和索引(高级索引、布尔索引、花式索引)
46 3
|
4月前
|
索引 Python
NumPy 教程 之 NumPy 切片和索引 7
NumPy 切片和索引用于访问和修改 `ndarray` 对象。类似于 Python 的列表切片, 使用索引 `[0-n]` 和切片 `slice(start, stop, step)` 或简写为 `[start:stop:step]` 来提取元素。单个索引 `[n]` 获取单个元素, `[n:]` 获取从 `n` 开始的所有元素, `[n:m]` 获取 `n` 至 `m-1` 的元素。省略号 `...` 保持选择维度与数组一致, 如 `a[...,1]` 获取所有第二列, `a[1,...]` 获取第二行, `a[...,1:]` 获取所有第二列及之后的元素。
26 4
|
4月前
|
索引 Python
NumPy 教程 之 NumPy 切片和索引 5
`ndarray`可通过索引或切片访问和修改。使用 `slice` 函数或冒号语法 `[start:stop:step]` 从原数组切割新数组。单参数 `[n]` 获取单个元素, `[n:]` 获取从 n 开始的所有元素, `[n:m]` 获取 n 到 m-1 的元素。 **示例:** ```python import numpy as np a = np.arange(10) # 创建数组 [0 1 2 3 4 5 6 7 8 9] print(a[2:5]) # 输出 [2 3 4] ```
31 3
|
4月前
|
索引 Python
NumPy 教程 之 NumPy 切片和索引 4
NumPy 切片和索引允许访问和修改 `ndarray` 对象的内容。类似于 Python 的列表切片, 可以使用 `start:stop:step` 形式的索引, 其中省略号代表默认值。
23 3
|
4月前
|
索引 Python
NumPy 教程 之 NumPy 切片和索引 2
`ndarray` 支持类似 Python `list` 的切片操作。可通过 `slice` 函数设置 `start`, `stop`, `step` 参数或用冒号语法 `start:stop:step` 从原数组中获取新数组。示例: `a = np.arange(10); b = a[2:7:2]` 输出 `[2 4 6]`.
25 2
|
4月前
|
索引 Python
NumPy 教程 之 NumPy 切片和索引 6
`ndarray`可通过索引或切片访问和修改。索引基于0-n,切片使用`slice`函数或`start:stop:step`语法。单参数如`[2]`获取单一元素,`[2:]`获取从索引2开始至末尾的元素,`[2:7]`获取索引2到6的元素。多维数组也支持这些操作。
23 1
|
4月前
|
索引 Python
NumPy 教程 之 NumPy 切片和索引 1
ndarray对象的内容可以通过索引或切片来访问和修改,与 Python 中 list 的切片操作一样。
24 0
|
6月前
|
机器学习/深度学习 计算机视觉 索引
NumPy索引与切片的高级技巧探索
【4月更文挑战第17天】探索NumPy的高级索引与切片技巧:整数数组、布尔和花式索引用于灵活选取元素;切片步长、反转及多维切片操作实现高效数组处理。在数据分析、图像处理和机器学习等领域有广泛应用,提升代码效率与可读性。
|
存储 算法 NoSQL
[Eigen中文文档] 稀疏矩阵操作
在许多应用中(例如,有限元方法),通常要处理非常大的矩阵,其中只有少数系数不为零。在这种情况下,可以通过使用仅存储非零系数的特殊表示来减少内存消耗并提高性能。这样的矩阵称为稀疏矩阵。
463 0