玖叶教程网

前端编程开发入门

轻松掌握MATLAB - 2.3 数组的索引与变换

上一篇:轻松掌握MATLAB - 2.2 数组的创建


在使用数组的过程中,常常需要对数组中的部分内容进行读取或修改,对数组进行排序、重组,对数组的行、列进行增删,甚至对数组形状和维度进行变换等操作。熟练掌握这些操作是使用MATLAB的基础。

2.3.1 数组的索引

访问数组中指定位置的元素的过程,称为数组的索引(Indexing)。MATLAB中有三种常用的数组索引方法:下标索引、序号索引和逻辑索引。

我们先通过magic函数生成一个4x4的魔方矩阵M,然后演示不同的索引方法。

魔方矩阵是一种特殊矩阵,其行、列、对角线上的元素之和均相等。

1. 下标索引

用元素在数组中的下标来访问这些元素的方法,称为下标索引,也称为按位置索引(Indexing by position)。下标索引的基本语法如下:

 B = A(r,c,...)

即,将需要访问的元素在数组中的位置坐标置于数组名称后面的圆括号中,不同维度的位置坐标以逗号分隔。例如,

返回的是数组M中第3行第2列的元素。位置坐标也可以是向量,用来同时访问该维度的多个元素。例如,

返回的是数组M中第1、3、4行第1列上的元素。可以用冒号表示某一维度的所有下标,用end表示最后一个下标。例如,

返回的是数组M中“第2到end行、所有列”的元素。注意圆括号中两个冒号的区别:第一个是生成行号向量;第二个是索引所在维度全部下标。

2. 序号索引

用元素在数组中的序号来访问这些元素的方法,称为序号索引,也称为线性索引(Linear indexing)。例如,

返回的是数组M中的第15个元素,也就是第3行、第4列的元素。序号索引也支持冒号和end。例如,

返回的是倒数第3个元素。

由于冒号代表了所有的序号,上面的命令返回的是M中的元素按序号排成的列向量。这也是一种将矩阵变成列向量的常用方法。与原来的矩阵M对照可以看出,MATLAB中的数组确实是以“列优先”的规则存储的。

3. 逻辑索引

用一个逻辑数组来筛选数组元素的方法,称为逻辑索引(Logical indexing)。对应逻辑数组中逻辑值为1的位置的元素会被筛选出来。例如,要获取M中不小于10的元素,可以先用逻辑表达式生成一个逻辑数组。

MATLAB中的逻辑型数据只有两个状态:true对应1,false对应0。在逻辑数组LM中,逻辑值为1的位置对应的元素不小于10。用逻辑数组LM对M进行索引,可以得到这些值。

筛选出的元素按其在M中出现的顺序排列成了一个向量,并被赋值给了MG。如果筛选过程中的逻辑数组没有其他用途,可以不用显式地生成它,而是直接用逻辑表达式进行索引。例如,筛选M中小于10的元素

除了获取数组中满足指定条件的元素外,有时还需要获取这些元素在数组中的序号或下标。这可以通过find函数来。其基本语法如下:

 k = find(X)
 k = find(X,n)
 k = find(X,n,direction)
 [row,col] = find(___)
 [row,col,v] = find(___)

find函数查找数组X中的非零元素,并返回前n个非零元素的序号k或下标[row,col]以及非零元素的值v。可选参数direction可以设置为'first'(默认)或'last',用来指定n的计数方向是从前往后还是从后往前。如果n省略,则返回数组X中所有非零元素。例如,获取M中不小于10的元素的序号或下标,

需要说明的是,find函数所返回的实际上是作为输入参数的数组中非零元素的位置。输入参数可以是任意类型的数组,并不局限于逻辑数组。


【说明】

  • find函数是一个使用频率极高的函数。请doc find,学习该函数的用法。
  • 逻辑索引实际上是用一个逻辑矩阵去“筛选”另一个矩阵,充分体现了MATLAB的“矩阵思想”,是执行效率最高的索引方法。特别是对大型矩阵,效果尤为明显。
  • 如果数组A的Size是r×c,则元素A(i,j)的序号为(j-1)*r+i。MATLAB提供了sub2ind和ind2sub函数,用于实现数组的序号和下标的相互转换。例如,

返回的idx是数组M中下标为(1,3)、(3,3)、(4,3)的元素(即第3列上1、3、4行的元素)对应的序号。可以用ind2sub函数将刚得到的序号idx还原为下标进行验证,

2.3.2 数组的变换

对数组的常见操作包括元素的修改、行列的增删、重构、转置、翻转、旋转、元素的移位、排序、维度变换等。

下面我们先生成一个3x5的随机整数数组A和一个字符数组S,然后演示常用的数组操作方法。

MATLAB中的随机数函数生成的是伪随机数,它是基于一个种子和某种算法计算出来的。每次调用随机数函数后,都会自动产生一个新的种子。为了保证示例的可重复性,我们用rng函数手动设置了下一次生成随机数的种子。需要注意的是,我们在字符数组S的最后一个字母B前故意加入了一个空格。

1. 元素的修改


通过索引并赋值可以修改数组中特定位置的元素。例如,


可以看到,数组 A 中第2行第2列列的元素已经被成功修改为了10。用这种方法也可以修改整行或整列的元素。例如,

数组 A 的“所有行、end 列”都被赋值为了 4。使用标量赋值时,索引到的所有元素都会被修改为该标量的值。我们也可以对数组中的一个部分进行索引并赋不同的值,赋值时需要确保等号两边数组的形状相同。例如,修改数组 A 的第 2 、3 行第 1 、2 列的元素,也就是左下角的四个元素:

字符数组中的元素也可以用同样的方法来进行修改。例如,将字符数组S的第一个字母改为大写:

2. 行列的增删

通过超界索引赋值,可以增加行或列。例如,

赋值前,A是一个3×5的数组。直接对其第7列赋值后,A被扩展为了3×7的数组,被跳过的第6列自动以0填充。

通过索引并赋空可以删除行或列。例如,删除数组A的第5到第7列:

如果对数组名赋空,数组中的所有元素将会被全部删除,变成一个空数组,但变量名仍在。要彻底清除数组变量,需要用clear命令。

对字符型数组类似,通过索引并赋值空字符即可删除字符。例如,删除S中字符B前的空格:

3. 重构

用reshape函数可以重构数组,即改变数组的形状。其基本语法如下:

 B = reshape(A,sz)
 B = reshape(A,sz1,...,szN)

该函数将数组A的形状转换为向量sz指定的形状,并将结果赋值给新的变量B。第2中格式用标量sz1,....,sizN来单独指定各维的长度。例如,将前面创建的数组A由3x4重构为6x2:

需要注意的是,在使用reshape函数对数组进行重构的过程中,数组的元素个数不能变,元素的序号不会变,它改变的只是数组的形状。

4. 转置

用transpose函数可以实现数组转置,即把数组的行和列互换。由于转置操作非常常用,MATLAB定义了专门的操作符.',即一个小数点加一个单引号。

注意transpose函数和reshape函数的区别:reshape后数组中元素的序号不会发生变化,而transpose后行列互换,数组中元素的序号也变了。

MATLAB支持复数,虚数单位为i或j。在对复数数组进行转置时,有时需要同时对其取复共轭,这种操作称为共轭转置。MATLAB提供了ctranspose函数进行共轭转置并定义了专门的操作符',即一个单引号。我们以实数数组R为实部,通过添加虚部创建一个复数数组C:

下面是对复数数组 C 进行共轭和复共轭操作的结果,观察结果,体会这两个算符的区别。


【说明】

  • 由于实数数组的复共轭仍是其自身,因此对实数数组进行共轭转置和普通转置得到的结果是一样的。
  • 在 MATLAB 中,i 和 j 被用作虚数单位。为了避免在操作复数时可能出现的问题,建议使用 ni 这种形式来明确表示复数的虚部。特别是当虚部为1时,也要明确写作1i,这可以避免 i 或 j 被重新定义导致错误的情况。例如,如果在程序中定义了i = 1,那么2+1i 仍然是一个复数,但2+i 则会把i视为变量而不是虚数单位,从而导致不易察觉的错误。
  • 一些c语言教材中喜好将i和j用作循环变量。由于上述原因,在MATLAB中应尽量避免将i和j用作循环变量,而是代之以m、n或p、q等。

5. 翻转和旋转

用flip函数可将数组沿指定的方向进行翻转。其基本语法如下:

 B = flip(A)
 B = flip(A,dim)

该函数将数组A沿dim维进行翻转并将结果赋值给B。可选参数dim缺省值为1,即上下翻转。

可以看出,数组A以第2行为轴发生了上下翻转。指定dim为2可实现左右翻转:

可以看出,数组A沿第2和第3列中间的对称轴,发生了左右翻转。由于矩阵的上下翻转和左右翻转最为常用,MATLAB为此定义了专门的函数:flipud和fliplr。例如,

可以看出,无论是上下翻转还是左右翻转,都是矩阵绕着其对称轴翻转了180度。

用rot90函数可以将数组在二维平面内旋转90度。其基本语法如下:

 B = rot90(A)
 B = rot90(A,k)

其中,可选参数k为整数,表示旋转次数,缺省值为1。当k>0时,数组沿逆时针旋转;当k<0时,数组沿顺时针旋转。如果A为多维数组,将在其第一维和第二维确定的平面上进行旋转。


【说明】

  • 在MATLAB中,有许多函数同时存在通用版本和多个专用版本。例如,flip函数是通用的,而fliplr和flipud函数则分别是用于左右和上下翻转的专用函数;前面学习过的cat函数是通用函数,而vertcat和horizcat函数则是专用于纵向和横向拼接的专用函数。这所以这样,主要原因是专用函数的使用频次通常更高一些,用专用函数来来处理这些常见情况可以减少函数中的校验,从而提高效率,并且代码的可读性也会更好。

6. 循环移位

用circshift函数可以将数组中的元素沿指定维度循环平移(Circularly shift)。其基本语法如下:

 B = circshift(A,K)
 B = circshift(A,K,dim)

该函数将数组 A 沿着指定维度 dim 循环平移 k 次。其中,k可以是整数标量或向量。当k>0时,数组沿指定维度向后平移;当k<0时,数组沿指定维度向前平移。可选参数dim的缺省值为长度不等于1的最低维度。例如,如果 A 是一个1xN的行向量,则长度不等于1的最低维度是2,即数组将沿着横向进行循环平移。例如,将A沿横向向右平移2次:

可以看出,平移是循环进行的,在向右平移过程中,尾部被挤掉的列会依次回到数组的开头。


当 k 为整数向量时,它表示沿每个维度的平移次数。需要注意的是,此时不能同时指定 dim 参数,向量k 中的每个元素对应一个维度的平移次数。例如,

数组A首先沿第1维(纵向)被平移了1次,然后沿第2维(横向)被反向平移了2次。该操作等效于circshift(circshift(A,1),-2,2)。

7. 排序

用sort函数可以对数组元素进行排序。其基本语法如下:

 B = sort(A)
 B = sort(A,dim)
 B = sort(___,direction)
 B = sort(___,Name,Value)
 [B,idx] = sort(___)

其中,dim指定排序的维度,默认为长度不等于1的最低维度;direction指定排序的方向,可以为'ascend'(升序)或'descend'(降序),默认为'ascend';Name和Value用来设置排序的方法;输出参数B为排序后的数组;可选参数idx返回的是B中的元素在原数组A中沿排序维度的序号。

将A按默认维度升序排列:

从返回结果可以看出,A中的元素被按列进行了升序排列。因为A的列为长度不等于1的最低维,所以此时默认的dim为1。若想按行升序排列,则需指定dim:

不指定direction时,默认为升序排列。若想按降序排列,则必须指定direction。例如,

在上述几个排序示例中,数组的所有行或列都按相同的规则进行了排序,排序后数据的行列对应关系也发生了变化。而在实际应用中,我们更多时候需要在按某行或列排序后,保持原来的数据的行列关系不变。比如,一个数组中存储的成绩单,每一行对应一个学生的成绩。该数组中,第一列为学号,后续各列为各科成绩。显然,在按学号或某科成绩排序时,原来在同一行上的数据排序后仍然要在同一行上。处理这种情况时,我们可以用两步排序来实现:第一步,先用sort函数返回按指定列排序后的序号;第二步,用这个序号通过下标索引来排列数组。

我们将数组A的各行按其第一列的值进行升序排列。第一步,对第一列排序并返回序号:

这里有3点需要说明:(1) 可以将数组的一部分传给sort函数进行排序;(2) 虽然'ascend'是默认的,但在代码中明确写出可以提高代码的可读性;(3) 如果函数有多个返回参数,它们的含义是按位置确定的。如果不需要某个位置的参数,可以在对应的位置使用’~’将其忽略。

第二步,利用第一步得到的 idx 作为下标来对原数组进行索引赋值,即可得到所有列均按照 idx 中的序号排列的新数组。

可以看到,排序后得到的数组S按第1列升序排列,原来在同一行上的元素,排序后仍在同一行上。

用issorted函数可以判断数组是否已排序。其常用语法格式如下:

 TF = issorted(A)
 TF = issorted(A,dim)
 TF = issorted(___,direction)

其中,dim指定拟判断的维度,默认为长度不等于1的最低维度;direction指定排序的方向,可以为'ascend'或'descend',默认为'ascend'。例如,判断S是否已按默认维度升序排列:

由于前面得到的S只是第一列进行了升序排列,其余列并未按升序排列,所以issorted函数返回逻辑0。查询S的第一列是否以按升序排列:


【说明】

  • 像sort函数中这种以Name和Value成对出现的参数,被称为名-值对参数。每个Name通常都有多个Value可供选择,以便更灵活的控制函数的行为。MATLAB中有许多函数都支持名-值对参数。如果一个函数有多个名-值对参数,那么在调用函数时这些参数的顺序是无关紧要的。因为参数的意义直接与Name相关联。sort函数有两个名-值对参数,详情请doc sort。
  • 函数的普通输入参数的意义是按其在输入参数列表中的位置来确定的。具有默认值的参数可以从后往前连续忽略,一般不能跳过某个参数去输入其后面的参数。而名-值对参数输入时与先后顺序无关。

8. 维度变换

用shiftdim函数可以循环移动数组的维度。其基本语法如下:

 B = shiftdim(A,n)

该函数将数组A的维度移动n次。其中,n为整数。当n>0时,维度向左移动;当n<0时,维度向右移动。例如,

A是一个2×3×4的数组,用shiftdim左移1次后,得到的B为3×4×2的数组,维度发生了循环移位。

用permute函数可以按指定的顺序置换数组的维度。基本语法如下:

 B = permute(A, dimorder)

其中,dimorder是一个长度为ndims(A)的向量,用来指定新的维度顺序。例如,将2×3×4的数组A的第3维换到第1维:

shiftdim和permute函数对数组的操作,相当于换了视角来观察数组。


【实用技巧】

  • 在 MATLAB 的 Command Window 中输入命令时,在输入过程中可以随时按 Tab 键,MATLAB 会搜索并提示满足条件的函数名或变量列表。这种功能称为 Tab Completion,默认是开启的,如果没有开启,可到 Preferences-Command Window-Automatic Completions 开启。
  • 选项“Tab key narrows completions”如果选中,则按Tab出现多个选项时,再次按Tab不会输入当前高亮的选项,而是关闭列表,这样再次按Tab出现的列表中可以去除无效的选项。



下一篇:轻松掌握MATLAB - 2.4 数组的内存管理与矩阵思想

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言