自学内容网 自学内容网

matlab学习笔记:第五章5.3.1元胞数组

5.3.1.1matlab中的数据类型

a = rand(4,5);      % 双精度浮点型数值矩阵
b = 1 + 3i;         % 复数标量
c  = a > 0.5;       % 逻辑数组
d = char('abc','def');  % 字符数组
e = {'a',[2,3]};    % 元胞数组(这一节要讲)
f = ["abc","defg"]; % 字符串数组(下一节要讲)
g = struct('a',1);  % 结构体(后续章节会讲)
h = categorical(f); % 分类数组(后续章节会讲)
i = datetime(2003,08,28);  % 日期(后续章节会讲)
j = table;          % 表格(后续章节会讲)
k = @(x) x*2;       % 函数句柄(后续章节会讲)
whos
whos a b c          % 还可以只显示指定变量的信息
class d             % 查看类 

5.3.1.2创建元胞数组

在第三章中,我们介绍了如何使用中括号[ ]来创建普通的数值数组。创建元胞数组(cell array)则需要使用英文输入模式下的大括号{ }(又称花括号)。在元胞数组中,同行元素之间可以用逗号或空格分隔,而行与行之间则通过分号或回车键分隔。我们可以在元胞数组中放入任何类型的数据。元胞数组中保存的数据非常灵活,数据大小和数据类型都没有限制。MATLAB 将元胞数组的每个元素视为一个独立的元胞,这样就可以灵活地进行修改,而不会影响数组的整体结构。这种设计大大提高了元胞数组的可操作性和灵活性,使得元胞数组成为处理各种不规则数据的理想选择。

c1 = {1:3 , 'abcd';
    char('ab','cd','e') 50;
    [5 6 7; 8 9 10] [60;70]}

c2 = cell(2)

c3 = {{1:3,'abcd'};
    [3 4;5 6]}

5.3.1.3引用元胞数组

在数值矩阵中,我们使用小括号引用所需位置的元素,例如A是一个4行2列的数值矩阵,那么 A(3,2)就能得到A矩阵中第三行第二列位置的元素。元胞数组有所区别,在MATLAB中,有两种方式来引用元胞数组:使用小括号()和使用大括号1。这两种引用方式有着不同的用途和效果。

(1)使用小括号()引用
当使用小括号()来引用元胞数组时,我们实际上是在引用元胞数组中的元胞,因此小括号引用时返回的是一个元胞数组,而不是元胞中存储的数据。

(2)使用大括号{ }引用
使用大括号{}引用元胞数组时,我们可以直接得到对应位置的元胞数组中的数据。

(3)链式索引
链式索引(chained indexing)是一种高级的数据访问技术,它允许我们在单个表达式中执行多个索引操作。如果元胞数组中包含数组数据,我们就能使用链式索引来访问该数组中的特定元素:先使用大括号来引用元胞中的数组:再使用小括号引用数组中的元素。 

(4)嵌套的元胞数组的引用
元胞数组中的数据可以是任意类型,因此它的数据也可以是另一个元胞数组,这被称为嵌套的元胞数组。如果存在嵌套的元胞数组,那么对元胞数组的引用会稍微复杂一点。尤其是当我们想访问内部的元胞数组时,我们需要借助多级链式索引来访问更深层次的数据。

(5)套用 cat、char 等函数对元胞数组中的数据进行拼接在处理元胞数组数据时,我们经常需要将独立的元胞中的数据拼接成一个统一的数组。MATLAB 提供了许多函数能帮助我们拼接数组的元素,例如我们讲过的cat和 char函数。

(6)celldisp 函数展示元胞数组的数据
上一章中我们介绍过disp函数,它可以在窗口(实时编辑器的窗口或命令行窗口)上显示变量的值。然而,disp函数对元胞数组的作用非常有限,它只能帮助我们快速了解元胞数组的整体结构。当我们需要详细查看每个元胞内的数据时,需要借助celdisp函数,它能提供更详细的信息。如果存在嵌套的元胞数组,它还会显示内部的元胞数组中每个元胞保存的数据。(注:另一个函数cellplot能以图形形式展示数据,但看起来不美观,大家可以自己测试)

C = {1:3,'abcd';
    true,50;
    [5 6 7;8 9 10],[60,70];
    char('ab','cd','e'),5+2i}
c11 = C(1,1)
c41 = C(4,1)
class(c11)
size(c11)
C(:)        %按照线性索引的顺序重新排列元胞数组

C = {1:3,'abcd';
    true,50;
    [5 6 7;8 9 10],[60;70];
    char('ab','cd','e'),5+2i}
c_11 = C{1,1}
c_41 = C{4,1}
class(c_11)
size(c_11)
C{1,:}      % 第一行元胞中的数据
[c_11,c_12] = C{1,:}
x = [C{3,:}]    %拼接元胞数组中的第三行的所有列
horzcat(C{3,:})
cat(2,horzcat(C{3,:}))

cc = {[1 2 3;4 5 6],'abc'}
cc{1,1}(1,3)        % 第一个元胞是一个2行3列的数值矩阵,引用这个矩阵中第1行第3列的元素
cc{1,2}(1,3)        % 第二个元胞是有三个元素的字符向量,引用第3个字符
cc_1 = cc{1}

cc = { {[1 2 3;4 5 6],'Good'};
    ['abc';'def'] }
cc{1}       %1×2 cell 数组 {2×3 double}    {'Good'}
cc(1)       %1×1 cell 数组 {1×2 cell}
cc{2}(1,:)
cc{1}(2)
cc{1}{2}
cc{1}{2}([1,end])
cc{1}{1}(:,1)

student = {'李华',[65 78 90];
    '清风',[95 99 93];
    '张无忌', [79 88 64];
    '苏大强', [54 96 33]}
student{:,1}
name = char(student{:,1})
name = cat(2,student{:,1})
name_char_cell = student(:,1)
score = cat(1,student{:,2})

cc = {'xyz',{1:3, 'abcd'};
    [3,4;5 6],3+2i}
disp(cc)
celldisp(cc)
cellplot(cc)    %以图形方式显示元胞数组
cellplot(cc,'legend')  % 添加图例

5.3.1.4拼接元胞数组

在数据分析任务中,有时候我们需要将不同的元胞数组拼接成为一个更大的元胞数组。MATLAB 中提供了两种不同的方法来拼接元胞数组:使用中括号[ ]和使用大括号{ }。两者拼接的结果有很大区别。使用中括号[ ]对元胞数组进行拼接时,实际上是对元胞数组中的元素进行拼接,这种操作方式和对数值矩阵的拼接方式完全相同。需要注意的是:使用中括号[ ]进行横向拼接时,要求参与拼接的元胞数组的行数相同:进行纵向拼接时,要求参与拼接的元胞数组的列数相同。使用大括号{ }拼接对参与拼接的原始元胞数组的大小没有要求。实际上,这里大括号的作用就相当于创建了一个嵌套的元胞数组。

C1 = {1, 2};
C2 = {'A', 'B'};
C3 = {10, 20};
C4 = [C1 C2 C3]     %横向拼接,也可以用逗号隔开
horzcat(C1,C2,C3)   %与上一行代码等价 
C5 = [C1;C2;C3]     %纵向拼接,必须列数一样
vertcat(C1,C2,C3)   %与上一行代码等价
cat(2,C1,C2,C3)
cat(1,C1,C2,C3)

C6 = {'abc',1};
C7 = {'def',2,3;
   'xyz',4,5};
{C6 C7}

5.3.1.5修改元胞数组

前文我们介绍过,可以将整个元胞数组类比为带有抽屉的柜子,每个抽屉都是独立的元胞数组,而每个抽屉中装的物品则代表存储在元胞中的数据。现在,我们将进一步学习如何修改元胞数组,这分为了两种情况:修改"抽屉"和修改"抽屉"中的数据。

(1)使用小括号()修改元胞数组的"抽屉"
当我们使用小括号()来引用元胞数组时,我们实际上是在操作整个"抽屉"。这意味着我们可以更换"抽屉",但是替换时必须使用另一个"抽屉",即更换为另一个元胞数组。

(2)使用大括号{修改"抽屉"中的数据当我们使用大括号{}时,我们是在修改"抽屉"中的具体物品,即元胞数组中保存的数据,使用大括号{}对数据进行修改时,对新数据的类型并没有要求这也正是元胞数组储存数据的优势所在。另外,我们也可以通过链式索引的方式对元胞数组中的数据进行修改。

易错点:使用大括号{ }修改数据时,MATLAB 不支持使用简单的赋值语句对两个或两个以上的数据进行修改。可以使用matlab内置函数deal,它的作用是将输入变量分发给输出变量。

C = {'apple', 'banana';
     'pear', 'cherry'};
C(2,1) = {'watermelon'}
C(:,1) = {[1 2 3]}
C(:,1) = {'a' 'b'}

C = {'apple', 'banana';
     'pear', 'cherry'};
C{2,1} = 'watermelon'
C{2,1} = 1:3

c = {[5 3 1];
    {10,20,[30 40]}}
c{1}([1,3]) = [8,6]
c{2}{3}(2) = 400

C = {'apple', 'banana';
  'pear', 'cherry'}
%C{:,1} = 'xyz'  %会报错:不支持使用简单赋值语句为 2 个元素赋值。请考虑使用以逗号分隔的列表赋值。
C(:,1) = {'xyz'}
[x,y,z] = deal('Mate',60,'pro')     % 为x、y、z三个变量分别赋值

C = {'apple', 'banana';
  'pear', 'cherry'};
[C{:,1}] = deal('xyz')
[C{1,1}, C{2,1}] = deal('xyz')

 练习题:随机生成8名学生,并将他们的信息储存在一个大小为8行3列的元胞数组S中。其中,S的第一列是他们的姓名,假设他们的姓名由4个随机英文字母生成,首字母大写;S的第二列是他们语数外三门科目的成绩,你可以使用长度为3的数值向量保存成绩,假设成绩是区间[0,100]上的随机整数;S的第三列是他们三门科目的总分。假设我们想将S中低于60分的成绩全部改成60,并重新计算总分。假设我们想在S中再增加一列用来表示学生的成绩等级,总分超过240分为'A',200-239为'B',低于200分时为'C'。创建一个空的元胞数组SS,用于存储等级为'A'的学生信息,并根据SS中的成绩进行降序排列。

S = cell(8,3);
for  ii = 1:8
    name = [char(randi([65,90],1,1)),char(randi([97,122],1,3))];
    score = [randi([0,100],1,3)];
    total_sorces = sum(score);
    %[S{ii,:}] = deal(name,sorces,total_sorces)     两种写法都可以
    S(ii,:) = {name,score,total_sorces}
end
for i = 1:8
    score = S{i,2};
    ind = score < 60;
    score(ind) = 60;
    S{i,2} = score;
    S{i,3} = sum(score);
end
S
for i = 1:8
    total_sorces = S{i,3};
    if total_sorces >= 240
        grade = 'A';
    elseif total_sorces >=200 && total_sorces < 240
        grade = 'B';
    else 
        grade = 'C';
    end
    S{i,4} = grade;
end
S
SS = cell(0);
for i = 1:8
    total_sorces = S{i,3};
    if total_sorces >= 240
        SS(end+1,:) = S(i,:);
    end
end
SS
SS_sort = sortrows(SS,3,'descend')

5.3.1.6删除元胞数组

我们还是将元胞数组比作带有抽屉的柜子,每个抽屉都是独立的元胞数组,而每个抽屉中装的物品则代表存储在元胞中的数据。因此,删除元胞数组有两层含义:删除"抽屉"(元胞数组)和删除"抽屉"中的物品(数据)。

(1)使用小括号( )删除元胞数组的"抽屉"借助小括号( ),我们可以将整个"抽屉"从数组中移除。

(2)使用大括号{ }删除"抽屉"中的数据使用大括号{ },我们可以删除"抽屉"中保存的具体数据,但这并不会移除"抽屉"本身,仅仅是将"抽屉"内的数据清空。

C = {'apple', 'banana';
  'pear', 'cherry'}
C(2,:) = []
C(1,1) = [] %会报错:空赋值只能具有一个非冒号索引。

C = {'apple', 'banana';
  'pear', 'cherry'}
C{2,1} = [] %这样不会报错
C{2,:} = [] %会报错:不支持使用简单赋值语句为 2 个元素赋值。请考虑使用以逗号分隔的列表赋值。
[C{2,:}] = deal([])

5.3.1.7对元胞元胞数组进行运算

和普通的数值数组不同,绝大多数的运算方法都不适用于元胞数组,例如加减乘除等算术运算、大于小于等关系运算、调用数学函数运算等都不适用。但是返回数组大小的三个函数:size、numel和 length对元胞数组仍然有效。还有几个函数比较特殊,那就是第三章集合运算中介绍的6个函数:unique(返回数组的唯一值)、ismember(判断一个数组的元素是否在另一个数组内)、intersect(交集)、union(并集)setdif(差集)和 setxor(对称差集)。它们只能用于字符向量元胞数组,即元胞数组中的数据全为字符向量时才可以使用。

C  = {65,'A',true;
     [1 2 3],'xyz','a'}
size(C)
[r_num,c_num] = size(C)
numel(C)    %返回元胞数组中的所有元素的和
length(C)   %返回行数和列数中较大的一个

C1 = {'bc','aca','bc','ab'};
unique(C1)
unique(C1,'stable')
C2 = 'bc';
ismember(C1,C2)
C3 =  {'aca','BC','Ab'};
ismember(C3,C1)

5.3.1.8元胞数据类型的转换

有时候我们需要将元胞数组中的数据与其他数据类型进行转换。

num2cell函数可以将数组转换为元胞数组,它有两种用法:用法一:C= num2cell(A)通过将 A的每个元素放置于 C的一个单独元胞中,来将数组 A 转换为元胞数组C。 用法二:C=num2cell(A,dim)将A的内容划分成 C中单独的元胞,其中 dim 表示维度。dim等于1表示沿着行方向划分,dim等于2表示沿着列方向划分。

a = reshape(1:12,3,4)
num2cell(a)
num2cell(a,1)
num2cell(a,2)
b = char(a+64)
num2cell(b)
% 下面我们结合生活实例来讲解num2cell函数的应用。
% 假设你是你们班的老师,需要管理学生在不同科目上的成绩。
% 这些成绩被存储在数值矩阵score 中,其中每一行代表一名学生,
% 每一列代表一个不同的科目。现在,你想要将这个矩阵转换为元胞数组,
% 并在元胞数组中添加上学生的姓名,这样能方便你为每名学生制定不同的学习计划。
score = [80,72;75,96;83,72;77,78];
name = {'小王','小张','小李','小刘'};
s = num2cell(score)
s_n = cat(2,name',s)

 mat2cell 函数是 nu2cell 函数的进阶版,它允许我们将一个数组分割为多个大小不同的子块,并将这些子块存储在元胞数组中。这一特性在分割矩阵时尤为有用,特别是当我们需要单独操作矩阵的特定部分时。

A = randi(10,6,5)
r = [2,2,2]
c = [2,3]
B = mat2cell(A,r,c)
celldisp(B)
% 数独是一个 9X9的方阵,它由九个宫格构成,每个宫格又由九个小格子构成。
% 请验证下面这个数独的盘面是否满足以下三点要求:
% (1)每列包含1到9的不重复数字:
% (2)每行包含1到9的不重复数字:
% (3)每个宫格内包含1到9的不重复数字。
sd = [1 9 4 3 8 5 7 2 6;
      8 3 2 7 6 9 4 5 1;
      6 5 7 4 2 1 9 3 8;
      2 6 9 8 3 7 5 1 4;
      5 8 3 1 9 4 6 7 2;
      4 7 1 2 5 6 3 8 9;
      9 1 5 6 7 2 8 4 3;
      3 2 6 5 4 8 1 9 7;
      7 4 8 9 1 3 2 6 5];
% (1)每列是否为1到9的不重复数
A = sort(sd,1)
B = repmat([1:9]',1,9)
sum(A == B,"all") == 81
% (2)每行包含1到9的不重复数字:
A = sort(sd,2)
B = repmat([1:9],9,1)
sum(A == B,"all") == 81
% (3)每个宫格内包含1到9的不重复数字。
sd_cell = mat2cell(sd,[3,3,3],[3,3,3])
pd = []
for i = 1:3
    for ii = 1:3
        a = sd_cell{i,ii}
        if sum(logical(unique(a)),"all") == 9
            pd = [pd,1]
        else
            pd = [pd,0]
        end
    end
end

 cell2mat函数是 mat2cell函数的逆操作,它主要用于将元胞数组转换为普通的数组。它的使用方法非常简单:A= cel12mat(C)将元胞数组转换为普通数组。元胞数组的元素必须全都包括相同的数据类型,并且生成的数组也是该数据类型。

C = {5,    [3 7 4];
    [6; 9], [8 7 8; 4 9 7]}
cell2mat(C)

5.3.1.9对每个元胞中保存的数据应用函数

cellfun函数:cellfun 函数的基本用法是 A= cellfun(func,C)。这里的 func是一个函数句柄,表示我们希望应用的函数,C是我们要计算的元胞数组。cellfun函数会遍历C中的每个元胞,并将每个元胞中的数据作为参数传递给 func。然后,它会收集 func 的返回值,将这些值串联起来,形成一个新的数组 A。这里出现了一个新的概念“函数句柄”。在MATLAB中,函数句柄是一种引用函数的方式。它允许我们将函数作为参数传递给其他函数,或者将函数赋值给变量。函数句柄可以通过在函数名前面加上@符号来创建,例如@min 就是min 函数的函数句柄。当'UniformOutput'设置为 false 时,cellfun 会将每次函数返回的结果存储在元胞数组中,而不是尝试将它们组合成一个常规数组。

%假设C储存的是运动员的跑步成绩
C = {[10.48,10.41,10.36];
  [11.01,10.86,10.76,10.50];
  [10.76,10.58];
  [10.49,10.53,10.56]};
n = length(C);  % 有几名运动员
best = zeros(n,1);  % 初始化最好成绩均为0
for ii = 1:n
    best(ii) = min(C{ii});
end
best

C_best = cellfun(@min,C)    %一行代码和上边for循环效果一样
C_mean = cellfun(@mean,C)
C_var = cellfun(@var,C)
C_sort = cellfun(@sort,C,'UniformOutput',false)

%拓展:函数句柄中也可以指定输入的参数
C_sort_d = cellfun(@(x) sort(x,'descend'),C,'UniformOutput',false)


原文地址:https://blog.csdn.net/weixin_63883130/article/details/143737512

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!