MATLAB学习笔记2:MATLAB基础知识(中)

阅读前请注意:

1. 该学习笔记是华中师范大学HelloWorld程序设计协会2021年寒假MATLAB培训的学习记录,是基于培训课堂内容的总结归纳、拓展阅读。博客内容由 @K2SO4钾 撰写、编辑,发布于 @K2SO4钾 的个人投稿与华中师范大学HelloWorld程序设计协会CSDN官方账号 @CCNU_HelloWorld注意,如需转载需得到作者 @K2SO4钾 的同意与授权!

2. 学习笔记基于 《MATLAB R2018a完全自学一本通》(刘浩, 韩晶) 1 ,笔记中增加了很多程序示例和笔者个人的思考。学习笔记面向刚接触MATLAB的新手,内容偏基础。笔记中示例用MATLAB版本为MATLAB R2019a

3. 请谅解笔记可能会出现的错误,欢迎指正讨论;由于MATLAB更新导致的旧代码无法使用的情况,也欢迎讨论交流。


2.0 MATLAB学习笔记:传送门汇总


2.0.1 MATLAB学习笔记:传送门汇总


MATLAB学习笔记0:学习须知

MATLAB学习笔记1:MATLAB概述

MATLAB学习笔记2:MATLAB基础知识(上)

MATLAB学习笔记3:MATLAB编程基础(前半)

MATLAB学习笔记3:MATLAB编程基础(后半)


2.0.2 MATLAB拓展学习:传送门汇总


MATLAB拓展学习T1:匿名函数和内联函数

MATLAB拓展学习T2:程序性能优化技术

MATLAB拓展学习T3:histogram函数详解

MATLAB拓展学习T4:数据导入与查表技术

内容施工中~~


2.0.3 MATLAB深入学习:传送门汇总


MATLAB深入学习S1:数字滤波技术的MATLAB实现

MATLAB深入学习S2:元胞自动机原理及MATLAB实现

MATLAB深入学习S3:图像处理技术

内容施工中~~


2.0.4 MATLAB应用实例:传送门汇总


MATLAB应用实例Y1:Floyd算法

内容施工中~~






2.1 数据类型


2.1.4 字符数组类型(char)与字符串数组类型(string)


MATLAB中对于字符串的存储一般使用字符数组类型和字符串数组类型,其中前者更为常用。

MATLAB中,字符串可以展示在屏幕上,或者用于进行一些特定的操作,或者用于构成一些命令。一个字符串在MATLAB中可以看做是一个 行向量,行向量中的每一个元素存放着 ASCII码,表示对应的一个字符。字符串展示在命令行窗口的时候也是以字符的形式展示,而不是ASCII码。

MATLAB的字符串中可以有中文,因此使用字符数组类型表示一个字符需要2字节(16bit)(如果是中文的话需要2byte的存储空间进行表示,如果只是英文符号则只需1byte即可,即ASCII码。MATLAB兼容中文,因此采用一个字符2byte的存储大小)。使用第一章讲过的whos函数能快速查看字符串占用空间的大小,用abs函数也能查看字符串中各个字符的ASCII码。

使用单引号可以快速创建一个字符数组。

clc, clear all, close all
str = 'abc我'
whos
abs(str) % 查看字符串每一个元素的ASCII

字符串
上面这个例子中,字符串 s t r str str使用一个 1 ∗ 4 1*4 14大小的字符行向量表示。

由于字符数组中保存的是ASCII码,因此如果直接对字符数组进行运算时,其结果会以ASCII码的矩阵表示出来。再使用 char函数(旧版本中为setstr函数),可将ASCII码转换为字符数组

clc, clear all, close all
str1 = 'abc我'
str2 = str1+3 % 这其实是一个double类型的矩阵,记录了ASCII码
str3 = char(str2)

在这里插入图片描述

由于字符数组本身就是一个矩阵,因此很多操作与矩阵相同。下面将介绍对于字符串的常见操作。


2.1.4.1 字符数组提取与合并

与提取矩阵中的元素、合并两个矩阵的操作类似,提取时使用冒号 : 对字符数组中的元素进行提取,合并时使用中括号 [] 对两个字符数组进行合并。注意,如果两个字符数组长度(即维度)不同,合并时只能合并为新的行向量,而不能合并为一个字符矩阵。

字符数组的倒置与字符数组的提取操作相似,见下例。

clc, clear all, close all
str1 = 'MATLAB';
str2 = 'yyds';
str3 = '_yyds_';
ans1 = str1(4:6) % 提取str1中的'LAB',也可写成str(4:1:6)
ans2 = [str1,str2] % 合并str1,str2为新的行向量
try
    ans3 = [str1;str2] % 报错,因为矩阵维度不同
catch
    ans4 = [str1;str3]
end
ans5 = str1(6:-1:1) % 倒置,相当于从str1第6位开始,每次向前取一位直到第1位
ans6 = str1(1:2:6) % 对str1隔一个字符取一个

操作1

2.1.4.2 字符数组取ASCII码与ASCII码恢复字符数组

上文提到,这里不再赘述。使用abs函数取字符数组的ASCII码,使用 char或setstr函数(旧版本) 从ASCII码恢复字符数组。


2.1.4.3 字符数组的比较

比较两个字符数组是否相同,通常有以下函数:

字符串
除了字符数组比较函数,也可以使用关系运算符,来 逐一比较两个字符数组各个字符是否相同。需要注意的是,这种比较方法需要两字符数组长度相同(即维度相同)。比较时通过ASCII码进行比较,返回结果为逻辑真(1)或逻辑假(0)

clc,clear all,close all;
str1 = 'snack';
str2 = 'snake';
ans1 = str1 == str2 % 将str1 == str2的运算结果赋值给ans1
ans2 = str1 > str2

字符串

2.1.4.4 字符数组的查找和替换

对于字符数组的查找,通常有以下函数:

findstr(str1, str2)(旧版本):查找str1、str2中较短的字符数组在较长的字符数组中出现的位置,即在一个较长的字符数组中查找另一个较短的字符数组。如果有多个位置出现就返回出现位置的行向量,如果没有搜索到就返回空矩阵。

strfind(str1, str2):查找str2在str1中出现的位置,即在一个较长的字符数组中查找另一个较短的字符数组。如果有多个位置出现就返回出现位置的行向量,如果没有搜索到就返回空矩阵。

[row, col] = find(expr):该函数返回矩阵中符合expr表达式的元素索引,即返回符合表达式的所有矩阵的下标。对于各种类型的矩阵(包括字符数组)使用find函数进行检索十分高效。

对于字符数组的替换:

strrep(str1, str2, str3):将字符数组str1中包含的字符数组str2全部更换为字符数组str3。

clc, clear all, close all
str1 = 'Welcome to CCNU HelloWorld !';
str2 = 'CCNU';
ans1 = findstr(str2,str1)
ans2 = strfind(str1,str2)
ans3 = find(str1(1,:)=='o') % 返回str1第一行中元素值为'o'的列
ans4 = strrep(str1,str2,'Central China Normal University')

字符串


2.1.4.5 执行文本中的MATLAB表达式

当字符数组中包含了可执行的MATLAB表达式时,可使用eval函数进行执行。

clc, clear all, close all
a = pi/3;
str = 'sin(a)+cos(a)'
eval(str)

在这里插入图片描述

2.1.4.6 字符数组的数值转换函数

常用函数如下:

字符串


2.1.4.7 字符串数组简介、字符串的打印

字符串数组,顾名思义就是数组中的每一个元素都是字符串。使用双引号 " " 创建一个 1 ∗ 1 1*1 11大小的字符串数组(即string类型),也称字符串标量。也可以使用string函数创建一个字符串类型,或者通过中括号 [] 和双引号 " " 创建一个字符串类型。

fprint函数只能在命令行窗口打印字符串标量,而disp函数可以在命令行窗口打印字符串数组。

clc, clear all, close all
str1 = "abc"
str2 = string('abc')
str3 = ["wjn","dyz","zwt","pym","wx"]
fprintf(str1)
disp(str3)
whos

在这里插入图片描述

基础学习阶段,字符串数组的应用相较于字符数组的应用要窄很多,操作字符串数组的函数数量有限。因此字符串数组只是简单介绍,具体的运用参考帮助文档。


2.1.4.8 其它关于字符串的函数(了解)

字符串
更多字符串函数参考帮助文档。



2.1.5 结构体类型(struct)


结构体,是指由一系列具有相同类型或不同类型的数据构成的数据集合,也即使用名为字段的数据容器将相关数据组合在一起的数据类型。

结构体里的成员可以是任意的数据类型(甚至是一个结构体类型,但是嵌套进去的结构体需要提前构建好)。访问该结构体的成员使用点 .(dot) 进行访问,例如欲访问结构体stu_sys中的major,使用stu_sys.major即可访问。结构体的创建也是这样(当然也可以使用struct函数进行创建),无需声明。

使用 rmfield(s,field) 删除结构体中的字段。

clc,clear all,close all;
TIME1.h = 8;
TIME1.m =30;
TIME2.h = 10;
TIME2.m =30;
stu_sys.student_name = 'dyz';
stu_sys.student_ID = 20190001;
stu_sys.major = 'physics';
stu_sys.final_score = 92;
stu_sys.exam_start = TIME1;
stu_sys.exam_end = TIME2;

stu_sys
fprintf('\n结构体stu_sys的信息:\n')
whos
fprintf('该学生的专业是:%s', stu_sys.major)

在这里插入图片描述

使用struct函数结合单元数组能快捷创建 结构体数组

clc,clear all,close all;
s = struct('type',{'big','little'},'color','red','x',{3 4})
s(1).type

上面这段代码就产生了一个 1 ∗ 2 1*2 12的结构体数组。访问其中的元素,需要指定是结构体数组当中的第几个

结构体
结构体


————↓———— 提 示 ————↓————

在MATLAB当中使用结构体时,直接采用点的方式创建更为高效、便捷。使用struct函数构建结构体时,应先清楚其调用方式:

S = struct(‘内容1’, 值1, ‘内容2’, 值2, …)

————↑———————————↑————



2.1.6 函数句柄(function handle)


MATLAB当中,对于一个函数的调用分为两种方式:直接调用方式和间接调用方式

直接调用方式,即调用一个存放在.m文件里的、函数名与文件名同名的函数。存放函数的M文件的位置即MATLAB搜索该函数时寻找的路径。例如调用sin函数,MATLAB会在工作路径中搜索sin.m文件,并调用该文件内的sin函数。

间接调用方式,即将一个函数的输入参数、输出参数、处理数据的表达式使用一个函数句柄来表示。这个函数句柄的路径与存放函数句柄代码段的.m脚本文件的存储路径一致,因此用户在创建时无需考虑调用位置。用户可通过函数句柄来创建一些简单函数,而无需再使用function函数在代码段末尾或其它函数文件中建立函数。

函数句柄可以减少代码的冗余,便于用户编写程序;便于函数之间互相调用,提高重复执行代码的效率;提高函数调用的可靠性、兼容性。但也有很明显的缺点,即无法表示功能更加复杂的函数,尤其是涉及到循环体、调用复杂函数时。

匿名函数就是一种函数句柄

设置一个函数句柄有三种方式。

第一种方式是使用 @ 符号来创建

设置一个函数句柄(伪代码)
函数名称 = @(输入参数1, 输入参数2, …) 表达式

第二种方式是使用str2func函数,将字符串转化为函数句柄

设置一个函数句柄(伪代码)
% 第一种方式
函数名称 = str2func(’@(输入参数1, 输入参数2, …) 表达式’)
% 第二种方式:未来版本中,这样的写法将会报错
函数名称 = str2func(‘表达式’)

第三种方式是使用符号表达式,再使用matlabFunction函数将其转化为函数句柄:

设置一个函数句柄(伪代码)
syms 输入参数1 输入参数2 …
函数名称 = matlabFunction(符号表达式)

调用一个函数句柄的方式如下:

调用一个函数句柄(伪代码)
输出参数 = 函数名称(输入参数1, 输入参数2, …)


clc,clear all,close all
%% 第一种设置方式
func1 = @(x,y) sin(x).*cos(y)
ans1 = func1(pi/2,0)
%% 第二种设置方式
func2 = str2func('@(x,y,z) x+y+z')
ans2 = func2(1,2,3)
%% 第三种设置方式
syms x y;
a = 1; b = 2;
func3 = matlabFunction(a*x+b*y)
ans3 = func3(1,1)

在这里插入图片描述


函数句柄的构造同样可以对既有的函数的引用。例如,如果要对exp函数进行引用,可以使用:

新函数名 = @既有函数名

clc,clear all,close all
fh = @exp;
fh(3)

在这里插入图片描述

函数句柄的具体应用(嵌套、表示分段函数等)、内联函数和匿名函数的使用等请参考:MATLAB拓展学习T1:匿名函数和内联函数



2.1.7 单元矩阵(cell)


单元矩阵也称元胞数组、单元数组,是一种广义上的矩阵。单元矩阵中的元素可以是任意的数据类型(因此你也可以进行单元矩阵套单元矩阵的“套娃”),其每个元素的==大小(Size)和存储空间(Bytes)==可按照存储元素的数据类型而变化。单元矩阵和普通的矩阵一样,可以为多维矩阵,也可以进行合并、提取、删除操作(普通矩阵的操作见2.3节)。

创建一个单元矩阵有两种方式:使用cell函数、使用大括号 ‘{ }’ 进行创建。

clc,clear all,close all
A = cell(3,2) % 创建一个3*2的空元胞数组
B = {'abc',uint(2);logical(0),3+2*1i} % 创建一个已经赋值的2*2元胞数组

在这里插入图片描述

MATLAB针对单元矩阵提供两种操作:寻访单元矩阵(单元外标识:Cell Indexing)、寻访单元矩阵中的元素(单元内寻址:Content Addressing)。两种操作使用的符号不同,功能也不同,使用时要尤其注意区分。

使用小括号 ‘( )’ 寻访单元矩阵。例如对单元矩阵A进行A(m,n)操作,即提取出单元矩阵A的第m行、第n列的单元,得到是一个 1 ∗ 1 1*1 11的cell类型;使用大括号 ‘{ }’ 寻访单元矩阵中的元素。例如对单元矩阵A进行A{m,n}操作,即提取出单元矩阵A的第m行、第n列单元中的元素,得到的是对应单元中元素的数据类型。

对于单元矩阵进行赋值、删除单元矩阵某个单元的元素时,需要使用大括号;对单元矩阵进行合并需要使用中括号(使用大括号将形成单元矩阵的嵌套);提取单元矩阵、删除单元矩阵时,需要使用小括号。


clc,clear all,close all
A = {"abab";5.4};
B = {'abc',uint(2);logical(0),3+2*1i};
%% 单元矩阵的寻访、单元矩阵元素的寻访
ans1 = B(2,2) % 单元矩阵的寻访(提取单元矩阵),得到cell类型
ans2 = B{2,2} % 单元矩阵元素的寻访(提取元素),得到complex类型
%% 对单元矩阵中单元的元素赋值
% A(1,1) = 2; % 会报错:无法从 double 转换为 cell
A{1,1} = 2;
ans3 = A
%% 合并单元矩阵AB
ans4 = [A,B]
%% 删除单元矩阵B的第二行
B(2,:) = [];
ans5 = B
%% 继续删除单元矩阵B1行第2列的元素
B{1,2} = [];
ans6 = B

在这里插入图片描述

2.1.8 map容器(Map)(了解)


2.1.8.1 map容器的简介(了解)

map容器也称map映射表,是一种高级数据类型,用于 将一个量线性映射到另一个量

map容器当中 包含了一组键(keys)和其对应的值(values)这一组键中不能有相同的键,每一个键线性地对应着一个值。map容器的线性映射的特性和函数的映射特性相似。对于函数来说,每一个自变量都是独一无二的,且都有唯一的因变量值与其对应,而反过来这种关系就不成立。map容器与其类似,它的每一个键都是独一无二的,且都有唯一的值与其对应,反过来不成立。

在这里插入图片描述

在这里插入图片描述

因此map容器中的键必须是同种数据类型,且为以下三种数据类型之一:数值数组、字符向量元胞数组、字符串数组;而每个键所对应的值可以是不同的数据类型,只需要和键一一对应即可。

下表为KeyType允许的数据类型:

在这里插入图片描述



2.1.8.2 map容器的优势(了解)

map容器作为一种快速查找的高级数据结构,具有其它数据结构不具有的优点。2

生活中有许多类似map容器这样映射方式的实例。例如学校当中的 ‘学号-姓名’ 对,学号独一无二,因此作为 ‘键’ ,每一个学号都有其对应的姓名,姓名和姓名之间可以相同,因此姓名作为 ‘值’ 。

考虑上述的 ‘学号-姓名’ ,如果我们使用之前讲过的单元数组存放,很显然,学号和姓名之间的一一映射关系是无法表现出来的。如果想要找到某个键对应的值,则需要在键对应的元胞数组中找到该键的列下标,再依据此下标在值对应的元胞数组中找到该键对应的值。由于需要遍历元胞数组,因此这一过程将消耗更多的时间。

其次,键的元胞数组无法将每个键限定为独一无二,且在添加新的 ‘键-值’ 对的时候也无法保证新增的键和既有的键不同。

再有,删除 ‘键-值’ 对、修改某个键对应的值、展示某一些键对应的值的时候,也会遇到同样的问题,即键和值的一一对应关系无法体现,导致我们只能在键中遍历寻找其下标,使用其下标再去更改对应值的内容,耗费时间。

最后,添加新的 ‘键-值’ 对时,如果添加内容超过预设的元胞数组大小,MATLAB会重新分配内存,无法动态地分配内存,代码性能进一步下降。

clc,clear all,close all
% 原键-值对
fprintf('原键-值对\n')
stu_ID = {201901,201801,202001,201902,201903} % 键
stu_name = {'dyz','wjn','lyx','zwt','pym'} %% 新增键-值对
fprintf('新增键-值对\n')
stu_ID{end+1} = 2019 % 重新分配,降低性能
stu_name{end+1} = 'wx'
% 删除键-值对201801-'wjn'
fprintf('删除键-值对201801-wjn\n')
for i = 1:length(stu_ID) % 遍历,降低性能
    if stu_ID{1,i} == 201801
        index = i;
        stu_ID(index) = [];
        stu_name(index) = [];
        stu_ID
        stu_name
        break
    end
end

在这里插入图片描述

考虑一一映射关系,我们还可以想到我们学过的一种数据类型:结构体。如果使用之前学过的结构体来代替map容器,相较于表达元胞数组它有一定的优势:能表达这样的映射关系,因此在 ‘键-值’ 对的修改、删除、查询方面相较于元胞数组有优势。但是和元胞数组一样,仍然具有不能使键独一无二的缺点。

另一个缺点是,如果键是一个数值类型,结构体将无法存放(因为变量的命名是有限制的)。只有当键是字符数组类型时,才可以用结构体类型替代map容器。这样一来,像上文的 ‘学号-姓名’ 这样的关系,结构体将无法表达。

还有一个缺点,即当我们需要存放的 ‘键-值’ 对数量较大时,采用结构体类型来存放使得代码过于冗长。

在这里插入代码片clc,clear all,close all
% 机票ID-乘机人
% 原键-值对
T_P.AM704 = 'dyz';
T_P.CT497 = 'wjn';
T_P.SG627 = 'lyx';
fprintf('原键-值对\n')
T_P
% 添加键-值对
T_P.MS562 = 'pym';
fprintf('添加键-值对\n')
T_P
% 删除键-值对
T_P = rmfield(T_P,'CT497');
fprintf('删除键-值对\n')
T_P
% 查询键-值对
fprintf('查询AM704的值\n')
T_P.AM704

在这里插入图片描述

综上所述,map容器相较于其它数据类型有以下优点:

  1. 能反映 ‘键-值’ 的一一对应关系,并且能确保键的独一无二;
  2. 能快速查找键对应的值,也能快速展示所有的 ‘键-值’ 对,对于 ‘键-值’ 对有计数。查找复杂度为O(1),而使用遍历方法查找的复杂度为O(n);
  3. 添加新的 ‘键-值’ 对时不会重新分配空间,无需预分配;
  4. 有专门用于修改键对应值的函数,有专门移除 ‘键-值’ 对的函数,对于map容器的操作很方便;



2.1.8.3 map容器的创建(了解)

MATLAB中通过 containers.Map 函数创建map容器及其映射关系。其调用方式为:

containers.Map(keySet, valueSet) :创建一个Map对象,其中包含来自keySet的键,每个键映射到valueSet中的一个对应值,二者应具有相同数量的元素,而keySet中的每个键必须唯一;

containers.Map(‘KeyType’, 指定键的数据类型, ‘ValueType’, 指定值的数据类型):创建一个空的 Map 对象,并指定稍后可以添加到其中的键和值的数据类型。

map的基础属性有三项:Count(记录该map容器中 ‘键-值’ 对的总数)、KeyType(记录该map容器中键的数据类型)、ValueType(记录该map容器中值的数据类型,如果有多种数据类型,则记录 ‘any’ )。map还有一个名为 ‘UniformValues’ 的属性,默认为false,规定了ValueType是否需要统一。如果需要将值的类型统一,可以将 ‘UniformValues’ 设置为true。

clc,clear all,close all
% 键为数值数组(double),值为字符数组(char)
cm1 = containers.Map([2020 2021 2022 2023 2024],{'鼠','牛','虎','兔','龙'})
% 键为字符向量的元胞数组(char),值为任意数据类型(any)
cm2 = containers.Map({'A201901','C201802','D201803','Counter'},{'dyz','wjn','hhq',3})
% 键为字符串数组(char),值为数值数组(double)
keySet = ["MU5260","MU7004","MU6062"];
valueSet = [46 60 58];
cm3 = containers.Map(keySet,valueSet)

在这里插入图片描述

2.1.8.4 map容器的检索(了解)

对于一个map容器,输入键即可快速检索到其对应的值。下面给出了三种检索容器中键对应值的方法:

检索单一键的值:

对应的值 = map容器名称(键)

检索多个键的值:使用values函数

对应的值 = values(map容器名称, 键)

检索某个map容器内所有的键和对应的值:keys函数和values函数

键 = keys(map容器名称)
对应的值 = values(map容器名称)
(二者一一对应)

clc,clear all,close all
cm1 = containers.Map([2020 2021 2022 2023 2024],{'鼠','牛','虎','兔','龙'});
cm2 = containers.Map({'A201901','C201802','D201803','Counter'},{'dyz','wjn','hhq',3});
cm3 = containers.Map(["MU5260","MU7004","MU6062"],[46 60 58],'UniformValues',true);
result1 = cm1(2022)
result2 = values(cm2,{'A201901','C201802','D201803'})
result3_keys = keys(cm3)
result3_values = values(cm3)

在这里插入图片描述



2.1.8.5 map容器的操作(了解)

移除 ‘键-值’ 对


使用remove函数可以移除map容器中的 ‘键-值’ 对,调用方式为:

remove(map容器名称, 欲移除的键)

clc,clear all,close all
TicketNum = {'A204','F40K','7U99','D1A4','XH7A','S47A'};
Passengers = {'dyz','wjn','pym','lyx','wx','zwt'};
cm = containers.Map(TicketNum,Passengers);
fprintf('map容器cm:\n')
keys(cm)
values(cm)
% 移除键'XH7A','S47A'
removed_cm = remove(cm,{'XH7A','S47A'});
fprintf('map容器removed_cm:\n')
keys(removed_cm)
values(removed_cm)

在这里插入图片描述


新增 ‘键-值’ 对


已存在的map容器名称(新增键名) = 对应值:新增 ‘键-值’ 对。

clc,clear all,close all
TicketNum = {'A204','F40K','7U99','D1A4','XH7A','S47A'};
Passengers = {'dyz','wjn','pym','lyx','wx','zwt'};
cm = containers.Map(TicketNum,Passengers);
fprintf('map容器cm:\n')
keys(cm)
values(cm)
% 新增键'CM6A','DM4D'
cm('CM6A') = 'xzh';
cm('DM4D') = 'hhq';
added_cm = cm;
fprintf('map容器added_cm:\n')
keys(added_cm)
values(added_cm)

在这里插入图片描述


修改键


修改键名需要将原有的键先移除再添加更正后的键及其对应的值

clc,clear all,close all
TicketNum = {'A204','F40K','7U99','D1A4','XH7A','S47A'};
Passengers = {'dyz','wjn','pym','lyx','wx','zwt'};
cm = containers.Map(TicketNum,Passengers);
fprintf('map容器cm:\n')
keys(cm)
values(cm)
% 修改键'A204''A256'
remove(cm,'A204'); % 移除'A204'
cm('A256') = 'dyz'; % 增加'A256'
modified_cm = cm;
fprintf('map容器modified_cm:\n')
keys(modified_cm)
values(modified_cm)

在这里插入图片描述

修改键对应的值


修改键对应的值与新增 ‘键-值’ 对的方式一样。

已存在的map容器名称(已有键名) = 新的对应值

clc,clear all,close all
TicketNum = {'A204','F40K','7U99','D1A4','XH7A','S47A'};
Passengers = {'dyz','wjn','pym','lyx','wx','zwt'};
cm = containers.Map(TicketNum,Passengers);
fprintf('map容器cm:\n')
keys(cm)
values(cm)
% 修改键'D1A4'中的'lyx''hhq'
cm('D1A4') = 'hhq';
modified_cm = cm;
fprintf('map容器modified_cm:\n')
keys(modified_cm)
values(modified_cm)

在这里插入图片描述



2.1.9 table表(table)(了解)


生活中,我们总是会碰到这样那样的表,比如一个年级寒假每天的体温打卡表,或者单位里一个部门所有员工的信息表。处理数据时,我们也是在与表打交道,我们从表格中提取出原始数据,再对数据进行分析、解释。仔细想来,我们周遭的很多数据都可以存储到表格中。因为表状的数据在工程计算中被越来越广泛的使用,MATLAB中设计了table类型。

此外,我们上一节中也谈到,基础的数据类型例如元胞数组、结构体等或多或少会有缺陷(这样的缺陷主要源于数据的关联性无法存储、操作起来较为麻烦等),因此像table表、map容器这样的数据类型应用会更广泛。

使用table函数可以创建一个表格:

table(var1, var2, … , ‘VariableNames’, {‘var1_name’, ‘var2_name’, …} ):var1,var2,…,varn是等长的列向量,作为表的第1、2、…、n列,这些列被分别命名为’var1_name’, ‘var2_name’, …

例如运用MATLAB创建如下的表:

在这里插入图片描述
这样的表格可以通过importdata函数、xlsread函数或者csvread函数直接导入,但是不在我们本节的讨论范围之内。使用table函数可以创建表格:

clc,clear all,close all
FDN = {'New Energy';'Ordinary Cola';'Medical Apparatus';'Dairy Produce';'Driverless'};
FC = {'0001';'0002';'0003';'0004';'0005'};
C = {'DEV-ENERGY Inc';'SNG-Food Inc';'CS-Med Inc';'SNG-Food Inc';'Tex-Tech Inc';};
IN = [1.432;-0.661;2.386;0.254;3.046];
Class = uint16([3;1;6;1;2]);
My_Table = table(FDN,FC,C,IN,Class,'VariableNames', ...
    {'Funds_Descriptive_Name','Funds_Code','Company','Fluctuation_Range','Class'})

在这里插入图片描述
细心的同学可能发现了,在给表的每一列命名时,笔者并没有使用原表中的 ‘Fund’s Descriptive Name’ 等字符串,而是使用的 ‘Funds_Descriptive_Name’ 等,这是因为 列名、表名均要符合MATLAB的变量命名规范,通过 isvarname(var) 可快速查看变量名是否为合法变量名。

对于table类型的数据类型,MATLAB有三种方式对其中的元素进行访问:


(1)使用小括号 ‘( )’ ,返回table类型


使用 表名(行范围, 列范围) 进行访问,结果会将表内对应范围的内容形成一个新的表格进行返回。以上文的表格为例:

% 以上文的表格 'My_Table' 为例
My_Table(2:4,2:3) % 访问My_Table的2-4行,2-3

在这里插入图片描述

(2)使用大括号 ‘{ }’ ,返回选中内容的数据类型


需要注意,由于使用大括号时返回选中内容的数据类型,当选中内容的数据类型不同时,可能会发生错误(即无法串联为一个结果矩阵)。

% 以上文的表格 'My_Table' 为例
result1 = My_Table{1:3,1:2} % 由于创建时使用的元胞数组创建,这里也将返回元胞数组,其中包含了字符串
result2 = My_Table{1:4,4} % 返回double类型
result3 = My_Table{1:3,4:5} % 由于第四列是double,第五列是uint16,
                            % 二者合并后会形成3*2的uint16矩阵(double类型会被强制转化为uint16类型)
% result4 = My_Table{1:3,3:4} % 会报错,因为选定的内容中既有double又有cell(cell中包含了字符串),MATLAB无法将其合并为一个矩阵

在这里插入图片描述

(3)使用dot和括号


使用 表名.列名(行范围)表名.列名{行范围} (对于数值类型的数据不支持用后者进行访问)进行访问,前者返回选中内容的数据类型后者返回选中内容的具体每一个元素

% 以上文中 'My_Table' 为例
result1 = My_Table.Funds_Descriptive_Name(3:5) % 返回3*1的cell类型(cell中包含了字符串)
result2 = My_Table.Funds_Descriptive_Name{3:5} % 返回3个结果,每个结果均为字符串类型
result3 = My_Table.Fluctuation_Range(3:5) % 返回3*1的double类型
% result4 = My_Table.Fluctuation_Range{3,5} % 会报错,因为选定的内容不支持使用花括号进行索引

在这里插入图片描述

使用table表这样的数据类型能更快速、更便捷地对其中的数据进行处理。如下例。


☆ 例2-9:(了解) 某超时水果区20日销售情况从数据库中导出如下表。按要求完成任务。

在这里插入图片描述
(1)根据表格内容,使用table数据类型将数据存入MATLAB;

(2)不计进货等成本,计算20天内这三种水果总共卖出的价格;

(3)绘制三种水果每天销售额的变化曲线于同一张图内(使用plot函数);

(4)求20天内卖出苹果数量的最大值、平均值、中位数。


分析:

clc,clear all,close all

%1)根据表格内容,使用table数据类型将数据存入MATLAB;
apple = [32;41;55;71;90;84;76;73;70;66;65;63;61;77;109;122;127;103;109;118];
banana = [181;186;146;186;172;144;154;167;188;189;148;189;188;164;180;147;161;186;180;188];
tomato = [423;436;437;395;342;319;314;323;382;444;444;445;446;420;373;363;321;419;438;444];
date = {'2.04';'2.05';'2.06';'2.07';'2.08';'2.09';'2.10';'2.11';'2.12';'2.13';'2.14';'2.15';'2.16';'2.17';'2.18';'2.19';'2.20';'2.21';'2.22';'2.23'};
apple_unitprice = [1.58000000000000;1.52000000000000;1.97000000000000;2;1.29000000000000;1.64000000000000;1.59000000000000;1.83000000000000;1.90000000000000;1.96000000000000;1.39000000000000;1.87000000000000;1.84000000000000;1.26000000000000;1.21000000000000;1.65000000000000;2.20000000000000;1.47000000000000;1.76000000000000;1.33000000000000];
banana_unitprice = [1.56000000000000;0.980000000000000;1.27000000000000;1.50000000000000;1.73000000000000;1.81000000000000;1.32000000000000;0.840000000000000;0.850000000000000;0.980000000000000;1.67000000000000;0.980000000000000;1.64000000000000;0.960000000000000;1.77000000000000;1.09000000000000;0.910000000000000;0.970000000000000;1.40000000000000;1.23000000000000];
tomato_unitprice = [0.920000000000000;1.40000000000000;1.15000000000000;1.12000000000000;1.48000000000000;0.860000000000000;1.32000000000000;1.32000000000000;0.950000000000000;1.14000000000000;0.650000000000000;0.630000000000000;1.10000000000000;1.35000000000000;1.50000000000000;0.700000000000000;1.14000000000000;1.04000000000000;0.590000000000000;0.910000000000000];
My_Table = table(date,apple,banana,tomato,apple_unitprice,banana_unitprice,tomato_unitprice,'VariableNames', ...
    {'date','apple','banana','tomato','apple_unitprice','banana_unitprice','tomato_unitprice'})

%2)不计进货等成本,计算20天内这三种水果总共卖出的价格;
Total_Sales = sum(My_Table.apple.*My_Table.apple_unitprice)+ ...
    sum(My_Table.banana.*My_Table.banana_unitprice)+ ...
    sum(My_Table.tomato.*My_Table.tomato_unitprice)

%3)绘制三种水果每天销售额的变化曲线于同一张图内(使用plot函数);
figure(1)
h = plot(1:20,(My_Table.apple.*My_Table.apple_unitprice)');
h.LineWidth = 2;
h.Color = 'r';
grid on
hold on
h = plot(1:20,(My_Table.banana.*My_Table.banana_unitprice)');
h.LineWidth = 2;
h.Color = 'g';
hold on
h = plot(1:20,(My_Table.tomato.*My_Table.tomato_unitprice)');
h.LineWidth = 2;
h.Color = 'b';
xlabel = 'date';
ylabel = 'sales';
legend('Apple','Banana','Tomato');

%4)求20天内卖出苹果数量的最大值、平均值、中位数。
apple_max = max(My_Table.apple)
apple_avg = mean(My_Table.apple)
apple_median = median(My_Table.apple)

在这里插入图片描述

在这里插入图片描述





思考题2


☆ 思考题2-7:"All the wise for himself, a fool all for others."是一句经典的英文名言,译为“智者一切求自己,愚者一切求他人”。讲这句英文名言作为字符数组存储于变量standard中。给出字符串str1-str5,这五个字符串可以组合成字符串standard,但是它们目前每一个当中都存在问题。按要求完成任务。

standard = 'All the wise for himself, a fool all for others.';
str1 = 'all the wise ';
str2 = 'for flesmih, a '
str3 = '22222f22o22o22l2222';
str4 = '$$all$$for$$';
str5 = 'othmmmmmers.'

(1)str1中 ‘all’ 没有大写,请通过下标索引的方式修改;

(2)str2中 ‘himself’ 被倒置,请通过字符串提取的方式修改;

(3)str3中包含了单词 ‘fool’,请将该单词提取出来;

(4)str4中原本为空格的位置被 ‘$$’ 代替,请使用字符串替换函数将其替换为空格;

(5)str5中 ‘mmmmm’ 多余,请删除;

(6)将str1-str5组合为 ‘All the wise for himself, a fool all for others.’,使用两种方式,将你得到的字符串与标准字符串standard比较,验证是否有误。


☆ 思考题2-8:已知四个变量如下所示。按要求完成任务。

var_1 = [82,91,13,92,64,10,28,55,96,97];
var_2 = {'str1',41,[30 20 10],'str2',70,[10,20;30,40],'str3','str4',uint8(4)};
var_3 = {'a','b','c','d','e'};
var_4 = 'abc';

(1)将这4个变量以 1 ∗ 2 1*2 12结构体数组的方式存储,结构体名称为 ‘struct2_8’ ,前两个变量存储在结构体数组的第一个元素中,后两个变量存储于第二个元素中;

(2)访问var_1中的前5个元素,返回的类型为元胞数组;访问var_3中的元素 ‘d’,返回的类型为字符类型;访问var_3中的前3个元素,返回的类型为元胞数组类型;

(3)(较难)提取出var_2中字符数组类型的数据,并将它们组成一个 1 ∗ 4 1*4 14的元胞数组。



部分思考题答案


思考题2-7

clc,clear all,close all
standard = 'All the wise for himself, a fool all for others.';
str1 = 'all the wise ';
str2 = 'for flesmih, a ';
str3 = '22222f22o22o22l2222';
str4 = '$$all$$for$$';
str5 = 'othmmmmmers.';

%% 修改
str1(1) = 'A'
str2(5:11) = str2(11:-1:5)
str3 = str3(6:3:15)
str4 = strrep(str4,'$$',' ')
str5(4:8) = []
std_str = [str1,str2,str3,str4,str5]

%% 验证是否有误
%1)
result1 = strcmp(standard,std_str) % 结果为logical 1,无误
%2)
result2 = sum(std_str ~= standard) % 字符数组每个元素均相同,结果为0,无误

在这里插入图片描述

思考题2-8

clc,clear all,close all
% 第一问
struct2_8(1,1).var_1 = {82,91,13,92,64,10,28,55,96,97};
struct2_8(1,1).var_2 = {'str1',41,[30 20 10],'str2',70,[10,20;30,40],'str3','str4',uint8(4)};
struct2_8(1,2).var_3 = {'a','b','c','d','e'};
struct2_8(1,2).var_4 = 'abc';
Q1_result = struct2_8

% 第二问
Q2_result1 = struct2_8(1,1).var_1(1:5)
Q2_result2 = struct2_8(1,2).var_3{4}
Q2_result3 = struct2_8(1,2).var_3{4}

% 第三问
Q3_result = cell(1,4);
j = 1;
for i = 1:length(struct2_8(1,1).var_2)
    if ischar(struct2_8(1,1).var_2{1,i})
        Q3_result{1,j} = struct2_8(1,1).var_2{1,i};
        j = j+1;
    end
end
Q3_result

在这里插入图片描述




撰写:邓云泽、林耀
审核:华中师范大学HelloWorld程序设计协会工作人员


  1. 刘浩, 韩晶. MATLAB R2018a 完全自学一本通[M]. 北京:电子工业出版社. 2019. ↩︎

  2. MATLAB 高级数据结构连载 4 containers.Map[EB/OL]. doi: https://zhuanlan.zhihu.com/p/22247363 ↩︎

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

K2SO4钾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值