1. 初识MATLAB:为什么这门语言值得你花时间
先说说我自己的背景。几年前我接了一个数据处理的外包小项目,甲方要求三天内从几千行测试数据里提取特征并出图。当时我用的是Python,按说也不慢,但客户那边工程师只会用MATLAB,后来我干脆把整套分析流程用MATLAB重写了一遍。从那以后我就意识到,MATLAB不是实验室里老教授才用的古董工具,它在你需要快速验证算法、处理矩阵运算、画出版级图表的时候,效率高得吓人。这篇笔记是MATLAB学习笔记系列的第一篇,聚焦基础知识,适合零基础想入手、或者用过其他编程语言但想快速切换过来的朋友。
你需要知道的第一个认知是:MATLAB的名字来自“矩阵实验室”(MATrix LABoratory),它的一切设计都围绕“矩阵”展开。数组和矩阵是这门语言的一等公民,你不需要像在C语言里那样先写一堆循环才能对数组里的每个元素做运算,直接写表达式就行。这种“面向数组”的思维方式,会让你在数值计算和数据分析类任务里走捷径。
另一个吸引我的是它的即时反馈。命令行窗口敲一个回车,结果立刻显示,没有编译环节,没有“找不到头文件”的报错。这对学习算法、调试公式、快速验证思路来说非常舒服。我见过很多学编程被编译环境劝退的朋友,在MATLAB里反而能很快找到手感。
这篇笔记覆盖四块内容:环境界面和基本操作逻辑、数组与矩阵的核心玩法、脚本开发与常见数据结构、绘图入门。每块我都会配上实际使用时的经验,尤其是那些文档里不会写、但你真的会踩进去的坑。建议你打开MATLAB跟着敲一遍,光看不练,三天就忘。
1.1 界面布局与工作目录:先把家认清楚
打开MATLAB,第一眼看到的是它的默认桌面布局。主要分区有这么几个:最上方是工具带(Ribbon),自带编辑器、命令行窗口、工作区、当前文件夹等标签页;左边默认是当前文件夹窗口,展示你工作目录下的文件;中间最大的区域是命令行窗口,也就是你输命令的地方;右下角是工作区,显示当前内存里所有变量名、尺寸和数据类型;还有详细信息窗口,显示选中变量的具体内容。
新手犯的第一个错误就是不理解“当前文件夹”到底是什么概念。你运行脚本时,MATLAB默认只在当前文件夹以及搜索路径(Path)包含的文件夹里找文件。好比你在一个图书馆里,只有在你眼前这张桌子上翻书是不行的,你得先告诉图书管理员你要去哪个区域。所以每次开新工程,我的习惯是先用cd命令切换到项目文件夹,或者直接在“当前文件夹”窗口里手动导航过去。你可以用pwd查看自己当前在哪,用cd 路径换位置。
第二个认知是命令行窗口和编辑器分工不同。命令行窗口适合敲临时命令、快速验证小表达式,好比随手拿草稿纸算一算;正式写程序必须进入编辑器,把代码存成.m文件,这样才能复用、调试和维护。很多人一开始图省事,全在命令行里敲,结果关掉窗口什么都留不下来,这是最亏的。
1.2 变量、运算符与基本语法:先懂规则再上手
变量这块继承了绝大多数语言的风格,但有一个关键区别:MATLAB变量的默认类型是双精度浮点数组。你写x = 1,x其实是一个1x1的double矩阵。这个概念从第一天上手就要刻在脑子里,因为后面做矩阵拼接、维度匹配时,很多报错都源自“把标量和矩阵混为一谈”的思维惯性。
常用运算符方面,加减乘除写作+ - * /,幂运算是^,除法还有左除\和右除/之分。左除A\b表示求解线性方程组Ax=b,和b/A表示xA=b完全不同。初学者在这里很容易栽跟头,我建议一开始就刻意记住:\左边是系数矩阵。
还有个我必须强调的细节是分号。在命令行里敲disp(1)会直接显示结果,但如果语句末尾加一个分号,执行后命令窗口就不显示输出。这是为了在脚本里避免刷屏。很多新手写的脚本运行半天屏幕上全是中间量,其实中间量可以在工作区里看,不需要全部打印出来。
注释用百分号%,这是MATLAB的注释符号。写脚本一定要写注释,过一个月你再回来看这份代码,大概率只记得思路,不记得细节。注释不是写给编译器看的,是写给三周后的自己看的。
2. 数组与矩阵操作:MATLAB的立身之本
所有语言都有“集合”的概念,但在MATLAB里,数组不是“众多类型中的一种”,而是主宰。你写统计分析要处理数据表,画像要处理像素矩阵,做信号处理要处理波形数组,本质都是“一堆数排成矩阵”。所以这一章的练习量要最大,熟练度要求最高。
2.1 创建矩阵的四种方法
方法一是直接输入。用方括号[ ]把元素括起来,同一行的元素用空格或逗号分隔,不同行的元素用分号分隔。比如:
A = [1 2 3; 4 5 6; 7 8 9]上面这行创建了一个3x3矩阵,第一行是1 2 3,第二行是4 5 6,第三行是7 8 9。注意是分号换行,不是回车换行,回车在方括号里会被当成同一行继续输入,这一点和直觉相反,我刚学的时候按回车想换行,结果全跑到一行去了。
方法二是用冒号运算符生成等差向量。格式是start:step:end,如果不写step,默认步长为1。比如:
t = 0:0.1:10; % 生成从0到10、步长0.1的等差向量这个操作太常用了,画图、采样、循环遍历里到处都是。需要留神的点是:如果 start 大于 end 且步长为正,结果为空;反向生成需要写负步长,比如5:-1:1会得到5 4 3 2 1。
方法三是使用内置函数创建特殊矩阵。zeros(m,n)生成全零矩阵,ones(m,n)生成全一矩阵,eye(n)生成n阶单位矩阵,rand(m,n)生成0到1之间的均匀分布随机数矩阵,randn(m,n)生成标准正态分布随机数矩阵。做算法原型时,这些函数是“无中生有”的利器,你不需要手工敲一长串测试数据,一行命令就有了。
方法四是从外部文件导入。load 文件名.mat可以加载MAT数据文件,readmatrix可以读Excel或CSV,比如data = readmatrix('data.csv')。注意文件路径带引号,相对路径基于当前文件夹。这块涉及导入工具的具体细节,我会在后续笔记里专门写一篇。
2.2 索引与切片:怎么取出你想要的元素
索引是新手最容易懵的地方,因为MATLAB的索引从1开始,而不是像Python、C那样从0开始。你在Python里写arr[0]取第一个元素,在MATLAB里则要写arr(1)。括号是圆括号,不是方括号,这一点也和很多语言不一致。
单个元素索引:
A = [10 20 30; 40 50 60; 70 80 90]; element = A(2, 3); % 取第2行第3列,结果是60切片索引:
row1 = A(1, :); % 取第一行全部列 col2 = A(:, 2); % 取第二列全部行 sub = A(1:2, 2:3); % 取第1到2行、第2到3列的子矩阵这里的冒号:表示“全部”,或者配合步长用来做跳跃采样,比如A(1:2:end, :)取奇数行。
还有一个高频技巧是用end关键字表示当前维度的末尾。写A(end, 1)取最后一行的第一列,写A(2, end)取第二行的最后一列。在循环里动态扩展数组时,end特别有用,比如往矩阵后面追加一行:
A(end+1, :) = [5 5 5];判断数组长度时,length(A)返回最长维度的长度,size(A)返回每个维度的长度,numel(A)返回元素总数。这三个函数的使用场景完全不同,很多新手容易混。我用一段记忆口诀:想知道“有多长”用 length,想知道“几行几列”用 size,想知道“总共多少个数”用 numel。
2.3 向量化运算:放弃循环是第一步
MATLAB最大的效率优势在于向量化运算,也就是对整个数组直接做运算,而不是用循环逐个处理。比如你要把矩阵A的每个元素都加1,写A + 1就行;要算所有元素的平方,写A.^2就行。注意这个点号^是点运算,作用在元素级,而A^2是矩阵乘法意义上的平方,二者天差地别。
元素级运算的符号规律是:.*./.^代表逐元素操作,*/^代表线性代数操作。对两个矩阵做逐元素乘法:
B = A .* 2; % 每个元素乘以2 C = A .* A; % 每个元素平方(Hadamard积)为什么要用向量化?一个原因是代码简洁,一个原因是执行速度快。MATLAB的底层针对矩阵运算做了大量优化,你用循环写一个100万次运算的代码可能要跑几秒,向量化写法往往是零点几秒。程序员的直觉是“能写循环就写循环”,但在这门语言里,你应该反过来思考:能不能不用循环。
聚合函数也很常用,sum(A)对矩阵做求和,默认是按列求和,返回一个行向量;mean(A)计算均值,max(A)和min(A)返回每列的最大值和最小值。如果想对整个矩阵所有元素求和,用sum(A, 'all')或者先A(:)把矩阵拉直成列向量再用sum。
3. 脚本开发与数据结构:从敲命令到写程序
命令行适合做实验,但做成一个完整的分析流程,必须写脚本。脚本就是把一堆命令按顺序存放的.m文件,你可以随时修改、重跑、分享。这一节讲的是如何把“试验田”里的代码整理成“正式生产线”。
3.1 创建脚本的规范流程
在编辑器中新建脚本有两种方式:工具带上有“新建脚本”按钮,或者直接在命令行输入edit myscript.m。推荐后者的原因是,你同时在命令行和编辑器之间切换,路径环境都已经通过命令行定好,比在图形界面上点来点去更顺手。
编写脚本时,我强烈建议遵循这套结构:
- 开头用注释写明脚本功能、作者、日期、输入输出说明。
- 接下来清空环境相关操作:
clear清空工作区全部变量,close all关闭所有图形窗口,clc清空命令行窗口显示。 - 然后是参数定义区,所有可调参数集中放在脚本最前面,用注释标明含义和量纲。
- 核心计算区,尽量用向量化运算,关键的中间量可以保留在工作区便于查看。
- 最后是输出与可视化区,绘图保存统一在这里。
大量编程新手习惯把数值参数散落在代码各处,真要修改的时候,得在几十行里翻找。把参数集中成一个“配置块”,从工程管理角度讲能节省非常多时间。
脚本写完运行,可以直接点编辑器里的“运行”按钮,也可以在命令行输入脚本文件名回车。注意:命令行输入脚本名时不用带.m后缀。
3.2 程序流程控制:判断与循环怎么用对
流程控制语句的基本结构和大多数语言一致,但语法细节有差异。条件判断用if、elseif、else、end配对,注意 elseif 中间没有空格。
x = 5; if x > 0 disp('positive'); elseif x < 0 disp('negative'); else disp('zero'); end循环有两个,for循环和while循环。for循环可以直接遍历数组元素,也可以用1:10这样生成索引序列:
s = 0; for k = 1:10 s = s + k; endwhile 循环适合不知道具体迭代次数、而是以条件为出口的场景:
x = 1; while x < 100 x = x * 2; end流程控制中我给的避坑建议是:循环里别频繁改变数组尺寸。动态扩展数组虽然方便,但每次扩展都会重新分配内存,循环上万次之后性能极差。更好的做法是预分配,比如result = zeros(1, 1000);在循环之前先建好空间。我见过有同事用动态扩展跑一个10万次循环,跑了五分钟没出结果,改成预分配后几秒钟结束。
3.3 cell与struct:两类进阶数据结构
基础数值矩阵要求所有元素类型一致,但真实数据分析的场景往往不是这个画风:你可能同时有文本标签、数值数组、异构长度的时间序列。这时就要用 cell数组(元胞数组)和 struct(结构体)。
cell 数组用花括号{ }创建和索引,可以存放不同类型、不同尺寸的数据:
C = {'apple', 3.14, [1 2; 3 4]; rand(3,3), 'test', 15};访问cell中的内容有两种方式,第一层用花括号取内容,第一层用圆括号取cell子数组。比如C{1,1}返回字符串'apple',而C(1,1)返回的是一个1x1的cell,里面装着'apple'。新手容易把花括号和圆括号的差别忽略,结果拿cell去参加数值计算时莫名报错。
struct 更接近字典(键值对),但语法上采用的是“域名”访问方式:
patient.name = 'Zhang San'; patient.age = 34; patient.records = rand(10, 4);访问就是patient.name。struct 还可以嵌套,在复杂数据整理中非常灵活。除了自己创建,load一个.mat文件时常常会得到几个struct变量,学会查看字段名和取值的遍历方法很重要。
4. 绘图入门:把数据变成看得懂的样子
MATLAB的绘图能力是它相比很多开发语言的核心优势,几行代码就能画出出版级别的图。绘图这块我会单独写一篇更深的笔记,这一篇先讲最核心的部分,让你能出图、保存图、理解图形对象的逻辑。
4.1 快速绘制与多图叠加
最基础的函数是plot(x, y),x是横坐标数组,y是纵坐标数组,长度必须一致。如果只传一个参数plot(y),横坐标默认为1到length(y)。举例:
x = 0:0.1:2*pi; y1 = sin(x); y2 = cos(x); plot(x, y1, x, y2);上面这段会直接在同一个坐标轴里画出正弦和余弦两条曲线。用legend('sin','cos')加图例,用xlabel('x')、ylabel('y')加坐标轴标签,用title('正弦与余弦')加标题。这些修饰性函数对新手来说非常直观,基本不需要查文档就能记住。
如果图已经画完,你又想在这个基础上加一条曲线,用hold on再重新plot:
plot(x, y1); hold on; plot(x, y2, '--r'); % 红色虚线 hold off;不要忘记hold off,否则后续的绘图命令都会叠在同一个图上,出图时才发现多个子图混在一起,又得从头来。
4.2 图形句柄与面向对象思维
MATLAB的绘图底层是面向对象的:plot返回一个图形对象句柄,你可以通过句柄精细控制它的属性。初级用法是plot(x, y, 'LineWidth', 2, 'Color', 'r')这种参数-值对方式,往后的进阶方向是拿句柄去改动属性:
h = plot(x, y1); set(h, 'LineWidth', 2); set(h, 'Color', 'r');图形窗口也可以用figure控制,figure(1)创建一个编号为1的窗口;subplot(2, 2, 1)把图窗划分为2行2列并选择第1个位置绘图。做多子图对比时,subplot是绝对的主力。
4.3 保存图片与中文乱码问题
出图后保存有多种方式。最简单的是在图形窗口的菜单里选“另存为”,或者直接用命令行导出。我常用的是saveas和exportgraphics:
saveas(gcf, 'myplot.png'); exportgraphics(gcf, 'myplot.jpg', 'Resolution', 300);gcf是当前图形窗口句柄。这里有个坑:如果图片里含中文标题,默认字体在部分平台下显示成方框,需要在标题里指定中文字体,比如:
title('正弦曲线', 'FontName', 'SimHei');不同平台可选字体不同,Win平台一般有 SimHei,macOS则要换成'PingFang SC'之类的名字。我建议把出图的dpi设置高一点,300dpi以上,后续写报告直接放进去也不糊。
5. 常见报错与新手自查清单
最后一个环节是实战中高频出现的报错和调试手段。我给每个问题配上“原因”和“解决办法”,你照着自查,能解决大部分入门阶段困扰你几个小时的难题。
| 报错/问题 | 常见原因 | 解决办法 |
|---|---|---|
| 矩阵维度不一致(Matrix dimensions must agree) | 两个矩阵做元素级运算但尺寸不一致 | 检查两个数组的size,用size()打印出来确认 |
| 索引超出矩阵维度(Index exceeds matrix dimensions) | 访问了不存在的行或列 | 先size(A)看最大维度,再用end关键字动态定位 |
| 未定义函数或变量 | 变量名拼写错误,或者文件不在搜索路径 | 检查拼写,用which 函数名看能否定位 |
| 脚本运行后命令窗口没有输出 | 语句末尾都有分号,输出被抑制 | 去掉分号或用disp()主动打印 |
| 读取CSV时数据错位 | readmatrix默认将第一行当数据而非表头 | 用readtable读取并指定表头行 |
| 中文显示为方框 | 默认字体不支持中文 | 设置FontName为中文字体 |
| 循环极慢 | 循环内动态扩展数组 | 预分配数组,或用向量化运算替代循环 |
| 路径含中文导致运行失败 | MATLAB部分版本对中文路径支持差 | 项目文件夹整体移到纯英文路径下,这个最省心 |
x = '123'和x = 123完全不是一回事 | 字符串类型和数值类型混用 | 字符串转数值用str2double,数值转字符串用num2str |
| 保存图片后图窗里显示和图文件不一致 | 没有指定分辨率或导出器 | 用exportgraphics指定分辨率和格式 |
调试经验我还有一个压箱底的推荐:用“分节运行”。在编辑器中,经常用双%%符号把脚本分割成多个节,这样你可以点“运行节”只执行当前节,而不用每次从头跑一遍。这对定位错误非常快,尤其是脚本长、数据加载慢的场景,节省的时间非常可观。
再补充一个好习惯:变量命名。MATLAB对变量名区分大小写,a和A是两个完全不同的变量。新手容易在代码里一会儿写Data一会儿写data,查错查半天。我的做法是:所有变量统一用“小写字母开头,多个单词首字母大写”的风格,比如rawData、trainSet、featureMatrix。函数名类似,但全局配置用全大写,比如CONFIG_NUM。
有一点新手必须留神:避免用i或j作为循环变量之外的特殊用途。在MATLAB里,未赋值时i和j默认代表虚数单位sqrt(-1)。如果你已经被循环把i的值改了,后面做复数运算时结果就会莫名其妙。
最后分享三个我自己的小习惯
这篇基础笔记写到这里,常规内容差不多讲完了。按惯例,我不喜欢做总结式收尾,更愿意分享几个在实际操作中反复验证过的小习惯。
第一个习惯是开新工程前先写“训练脚本”。我会创建一个setup.m,里面把路径、字体、画图风格统一配置好,每次运行项目先执行这个文件。尤其在工作环境里,不同人的搜索路径和默认参数不一样,统一入口能少很多无谓的报错。
第二个习惯是做“最小复现”。遇到奇怪的报错,我会把问题剥离成最小片段来测试。比如怀疑维度不一致,就单独构造两个小矩阵跑一下表达式;怀疑readmatrix读错列,就只读一列打印出来看。调试这件事,敲一堆输出分散注意力,不如把焦点收敛到一个变量上逐个验证。
第三个习惯是勤用doc命令。很多人遇到不懂的函数第一时间只会百度,我建议先敲doc 函数名。这份本地文档通常包含比网上帖子里更准确、更官方的说明和示例,尤其是函数的参数列表、可选属性、版本差异,网上大多说得不完整。比如readmatrix有很多可选键值对,本地文档一眼就能看到全部。
我见过太多新人学了几个月MATLAB还是只会用命令行敲几句简单命令,真正要交一个完整工程时无从下手。这篇笔记把界面、命令、数组、脚本、数据结构、绘图和常见坑串起来,就是希望你能在第一天就把“写完整脚本”的思维建立起来。下次遇到一个真实的数据文件,你会知道先readmatrix读进来,再用size和whos查看结构,然后向量化处理、绘图、导出,一气呵成。这比孤立地背命令有用得多。
下一篇我准备写数组运算的更进阶技巧,包括逻辑索引、匿名函数、以及几种常用数据预处理方法。如果你在练习中遇到了什么怪问题,不妨先按上面的自查表过一遍,多半能找到答案。