这里主要关注 MATLAB 绘图的技术性内容,不讨论非技术性内容,例如论文中的绘图要求或偏好。

MATLAB 的语法学习曲线很特别:入门时用起来特别简单舒服,但是深入学下去就会感觉到语法非常混乱,无所适从。
尤其在绘图部分,MATLAB 杂揉了命令式语句和面向对象的语句,存在太多的语法糖,毫无逻辑性可言。
因此在这里记录一些关于绘图的代码,便于查找。

MATLAB 同时提供面向过程和面向对象(指定操作的 figure 或 ax)的绘图语法,
面向过程的语法通过会直接获取当前 figure 或 axes 进行操作,面向对象的语法通常是在面向过程对应函数的第一个参数中传递 figure 或 axes。

需要说明的是,下文中的一些代码写法和常见的教程不太一样,包括:

  • 将常见的命令语法都换成了可读性更高的函数语法,例如 hold on 换成 hold('on'),参考官方文档中的 选择命令语法或函数语法
  • 将早期的键值对语法换成了更简洁的形式,例如 plot(ax, x, 'DisplayName', 'energy'); 改成 plot(ax, x, DisplayName = 'energy');

绘制多条曲线

绘制多条曲线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
x = linspace(0, 2*pi, 100);
y1 = sin(x);
y2 = cos(x);
y3 = sin(2*x);

figure();
hold('on');
plot(x, y1, 'r-', LineWidth=1, DisplayName='sin(x)');
plot(x, y2, 'b--', LineWidth=1, DisplayName='cos(x)');
plot(x, y3, 'g:', LineWidth=1, DisplayName='sin(2x)');
hold('off');

title('Multiple Curves with Direct Legend');

xlabel('x');
ylabel('y');

grid('on');
box('on');
axis('tight');

axis([0 2*pi -1.5 1.5]);
% or xlim([xmin xmax])

legend();

其中

  • LineWidth=1 设置线宽,默认值为 0.5,显得很细;
  • legend 展示图例:可以在 legend 函数中提供,也可以使用 DisplayName 参数提供;
  • title 设置标题;
  • xlabel, ylabel:设置轴标签;
  • grid('on'):显示网格线;(默认不显示)
  • bxx('on'):显示边框;(默认不显示四条边,只有两个轴会显示)
  • axis('tight'):设置轴的范围紧贴数据范围;(默认可能会有一定的距离)
  • axisxlim / ylim:指定坐标轴范围

将上述代码改用面向对象语法,只需要改 plotplot(ax, ...),其它改动类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
x = linspace(0, 2*pi, 100);
y1 = sin(x);
y2 = cos(x);
y3 = sin(2*x);

fig = figure(Name = 'Demo');
ax = axes(fig);
hold(ax, 'on');
plot(ax, x, y1, 'r-', LineWidth=1, DisplayName='sin(x)');
plot(ax, x, y2, 'b--', LineWidth=1, DisplayName='cos(x)');
plot(ax, x, y3, 'g:', LineWidth=1, DisplayName='sin(2x)');
hold(ax, 'off');

title(ax, 'Multiple Curves with Direct Legend');

xlabel(ax, 'x');
ylabel(ax, 'y');

grid(ax, 'on');
box(ax, 'on');
axis(ax, 'tight');

axis(ax, [0 2*pi -1.5 1.5]);
% or xlim(ax, [xmin xmax])

legend(ax);

plot 曲线样式

plot 函数支持多种曲线样式,包括颜色、线型、标记符号、线宽等,下面记录一下细节。

plot 函数的第一个参数可以是 ax,也可以省略,此时相对于使用 gca 获取当前 axes。

线型 LineStyle

四种线型如下表

线型 符号 示例
实线 '-' plot(x, y, LineStyle='-')
虚线 '--' plot(x, y, LineStyle='--')
点线 ':' plot(x, y, LineStyle=':')
点划线 '-.' plot(x, y, LineStyle='-.')

颜色 Color

几种标准颜色如下表

颜色 符号 示例
红色 'r' plot(x, y, Color='r')
绿色 'g' plot(x, y, Color='g')
蓝色 'b' plot(x, y, Color='b')
青色 'c' plot(x, y, Color='c')
洋红 'm' plot(x, y, Color='m')
黄色 'y' plot(x, y, Color='y')
黑色 'k' plot(x, y, Color='k')
白色 'w' plot(x, y, Color='w')

除此之外,还可以直接指定 RGB 颜色,例如

1
plot(x, y, Color=[0,0.7,0.9])

R2017a 之后的版本支持通过颜色的第四个通道指定透明度,例如

1
plot(x, y, Color=[0.2,0.5,0.8,0.2])

标记 Marker

几种标记样式如下表

标记 符号 示例
圆圈 'o' plot(x, y, Marker='o')
加号 '+' plot(x, y, Marker='+')
星号 '*' plot(x, y, Marker='*')
'.' plot(x, y, Marker='.')
叉号 'x' plot(x, y, Marker='x')
方块 's' plot(x, y, Marker='s')
菱形 'd' plot(x, y, Marker='d')
向上三角 '^' plot(x, y, Marker='^')
向下三角 'v' plot(x, y, Marker='v')
向左三角 '<' plot(x, y, Marker='<')
向右三角 '>' plot(x, y, Marker='>')
五角星 'p' plot(x, y, Marker='p')
六边形 'h' plot(x, y, Marker='h')

关于 marker,还有如下几个常用属性:

  • 'MarkerSize':标记大小(默认 8),例如 MarkerSize=8
  • 'MarkerEdgeColor':标记边缘颜色,例如 MarkerEdgeColor='k'
  • 'MarkerFaceColor':标记填充颜色(默认不填充),例如 MarkerFaceColor='r'
  • 'MarkerIndices':标记显示索引,例如 MarkerIndices=1:5:length(y)

关于 MarkerIndices 的补充说明:如果数据点很多,对每一个点都显示标记符号就显得太拥挤,可以指定需要标记的索引数组,但是 MATLAB 居然没有不支持指定间隔长度设置标记。

调整 marker 样式例如

1
2
3
4
5
6
7
x = linspace(0, 10, 40);
y = sin(x);

figure();
plot(x, y, 'ro', MarkerSize=10, MarkerEdgeColor='b', MarkerFaceColor='y');
grid('on');
title('Customized Markers');

还可以直接设置 LineStyle = 'none' 来隐藏线型,只显示标记符号。(此时就没必要设置 MarkerIndices 了,因为每一个点都是实际的数据点)

线型 + 颜色 + 标记

由于线型 + 颜色 + 标记这三个属性太常用了,MATLAB支持一次性使用字符数组来配置。

例如

1
plot(x, y, 'r--o');

'r--o' 对应为:

  • Color='r' 红色
  • LineStyle='--' 虚线
  • Marker='o' 圆圈

三者顺序并不固定。

例如

1
2
3
4
5
6
7
8
9
10
11
x = linspace(0, 10, 40);
y1 = sin(x);
y2 = cos(x);

figure();
plot(x, y1, 'ro-'); % 红色 + 圆点 + 实线
hold('on');
plot(x, y2, 'bs--'); % 蓝色 + 方块 + 虚线
hold('off');
legend('sin(x)', 'cos(x)');
grid('on');

LaTeX 支持

MATLAB 绘图窗口支持使用 LaTeX 字体,需要加上参数 Interpreter = 'latex' 进行设置,例如

1
2
3
4
5
6
7
8
9
10
x = linspace(0, 1, 100);
y = sin(2*pi*x);

plot(x, y, 'LineWidth', 1.5);

title('$\sin(2\pi x)$', Interpreter = 'latex');
xlabel('$x$', Interpreter = 'latex');
ylabel('$y$', Interpreter = 'latex');

legend({'$\sin(2\pi x)$'}, Interpreter = 'latex');

显然这里要求系统中已经安装 LaTeX,否则无法使用。

细节调整

关于网格

使用 grid('on') 可以显示网格线。

下面的写法可以只显示主网格线,关闭次网格线。

1
2
3
4
grid('on');
ax = gca();
ax.XMinorGrid = 'off';
ax.YMinorGrid = 'off';

关于坐标轴

默认坐标轴会显示主级和次级的坐标,但是有时次级坐标会显得太密集,可以用下面的代码关闭

1
2
3
ax = gca();
ax.XMinorTick = 'off';
ax.YMinorTick = 'off';

关于坐标轴的刻度朝向,MATLAB 默认朝内,可以用下面的命令调整当前绘图窗口的刻度朝向

1
2
3
4
ax = gca();
ax.TickDir = 'in'; % default

ax.TickDir = 'out';

可选值包括:inoutbothnone

注:这里也可以使用 set 函数实现,例如

1
set(gca(), TickDir = 'in', XMinorTick = 'off', ax.YMinorTick = 'off');

对于实际数据范围和坐标轴显示范围,如果没有手动指定,MATLAB 通常会让坐标轴范围略大于数据范围,在两侧留白,但是对于对数坐标轴等可能留白不够明显,可以用下面的两个命令调整

1
2
3
axis('tight');

axis('padded');

关于图例

可以使用 NumColumns 属性调整 legend 的列数,默认使用一列。

1
legend({'cos(x)','cos(2x)','cos(3x)','cos(4x)'}, NumColumns = 2);

可以使用 Orientation 属性调整 legend 的堆叠方向(垂直或水平方向,默认垂直)

1
legend({'cos(x)','cos(2x)','cos(3x)','cos(4x)'}, Orientation = "horizontal");

除了直接设置,也可以在获取 legend 对象后调整属性,例如

1
2
3
lgd = legend({'cos(x)','cos(2x)','cos(3x)','cos(4x)'});
lgd.NumColumns = 2;
lgd.Orientation = "horizontal";

绘图窗口控制

保持绘图窗口

默认情况下,绘制的曲线图呈现在一个绘图窗口中,但是再次调用 plot 函数绘图时,MATLAB 会自动清空已打开的绘图窗口,重新绘制。

如果我们在同一窗口中希望把旧图像保持,在此基础上继续绘图,需要使用 hold('on') 命令。hold('on') 命令会使得当前的绘图窗口的内容保持住,
直到使用 hold('off') 命令或者关闭绘图窗口。

例如

1
2
3
4
5
6
7
8
9
10
11
12
x = linspace(0, 2*pi);
y = sin(x);

figure();
plot(x,y)

hold('on')

y2 = cos(x);
plot(x, y2)

hold('off')

但是需要特别注意的是,hold('on') 会影响后续绘图的布局,将其固定为当前布局,例如改为调用对数坐标绘图时,不能先执行 hold('on'),否则轴会被直接固定;

多个绘图窗口

我们可以多次调用 figure(); 语句来创建不同的绘图窗口,分别在其中进行绘图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
x = linspace(0, 30);

% 创建第一个图形窗口并绘制正弦函数
figure();
plot(x, sin(x));
title('Sine');
xlabel('x');
ylabel('sin(x)');

% 创建第二个图形窗口并绘制余弦函数
figure();
plot(x, cos(x));
title('Cosine');
xlabel('x');
ylabel('cos(x)');

但是面向过程的方式只能依次绘图,无法做到更精细的操作——同时在多个绘图窗口绘图。

使用面向对象的语法则可以通过指定 figure 和 axes 实现同时绘图,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
x = linspace(0, 30);

fig1 = figure();
ax1 = axes(fig1);

fig1 = figure();
ax1 = axes(fig1);

plot(ax1, x, sin(x));
plot(ax2, x, cos(x));

title(ax1, 'Sine');
xlabel(ax1, 'x');
ylabel(ax1, 'sin(x)');

title(ax2, 'Cosine');
xlabel(ax2, 'x');
ylabel(ax2, 'cos(x)');

隐藏绘图窗口

对于已经打开的绘图窗口,可以使用 close 函数来关闭当前或指定的绘图窗口

1
2
3
close(gcf());

close(fig);

我们可以控制是否弹出绘图窗口

1
2
figure(Visible = 'on');
figure(Visible = 'off');

这里的参数只是决定是否隐藏窗口,无论是否隐藏,figure 对象都在内存中,不会自动释放。
仍然可以通过 gcf() 正常获取当前绘图窗口对象并进行操作。

对于不可见的绘图对象,由于窗口隐藏不容易发现,更要注意绘图对象的生命周期控制,要及时释放这些对象,在操作时要使用变量接收绘图句柄,否则可能会造成内存泄漏(gcf() 只能获取最近创建的绘图窗口)

1
2
3
4
5
fig = figure(Visible = 'off');

% ...

close(fig);

可以通过 usejava('desktop') 判断当前 MATLAB 是否有 GUI 来决定是否需要弹出绘图窗口?或者是否需要自动关闭绘图窗口?

例如在有 GUI 时保留(留给用户手动处理),在无 GUI 时自动关闭

1
2
3
4
5
fig = figure();

if ~usejava('desktop')
close(fig);
end

注意:figure 对象的 Visible 参数接收的不是布尔值,而是 'on''off',因为这个接口太老了,MATLAB 官方建议的兼容性做法为

1
2
visible_flag = false;
fig = figure(Visible = matlab.lang.OnOffSwitchState(visible_flag));

多个子图

有很多种方式可以创建子图,下面分别举例。

基于 subplot 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
x = linspace(0, 30);

figure();

subplot(2, 2, 1);
plot(x, sin(x));
title('Sine');

subplot(2, 2, 2);
plot(x, cos(x));
title('Cosine');

subplot(2, 2, 3);
plot(x, tan(x));
title('Tangent');

subplot(2, 2, 4);
plot(x, sec(x));
title('Secant');

sgtitle('Trigonometric Functions');

基于 titledlayout 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
t = tiledlayout(2,2); % 2*2

title(t,'Trigonometric Functions')
x = linspace(0,30);

nexttile()
plot(x,sin(x))
title('Sine')

nexttile()
plot(x,cos(x))
title('Cosine')

nexttile()
plot(x,tan(x))
title('Tangent')

nexttile()
plot(x,sec(x))
title('Secant')

对数 / 双对数图

plot 不同,可以使用 semilogxsemilogy 把单个坐标轴改成对数坐标,也可以使用 loglog 使用双对数坐标。

1
2
3
4
5
6
7
8
9
10
t = linspace(-1,2,100);
x = 10.^t;
y = x.^2;

figure();
subplot(2,2,1); plot(x,y); title('Linear');
subplot(2,2,2); semilogx(x,y); title('Semilog-x');
subplot(2,2,3); semilogy(x,y); title('Semilog-y');
subplot(2,2,4); loglog(x,y); title('Log-Log');
sgtitle('Coordinate System Comparison');

效果如下图

坐标轴刻度定制

MATLAB 提供了两组函数来控制坐标轴的刻度位置和刻度标签文字:

  • xticks / yticks:设置刻度位置;
  • xticklabels / yticklabels:设置刻度标签文字;

配合 TickLabelInterpreter = 'latex' 可以实现更丰富的刻度显示效果,例如以 $10^m$ 形式显示对数坐标轴的刻度值。

指定刻度位置与标签

使用 xticksyticks 可以指定轴上显示哪些刻度值:

1
2
3
4
5
6
7
8
9
x = linspace(0, 2*pi, 100);
y = sin(x);

figure();
plot(x, y, LineWidth=1);
xticks([0, pi/2, pi, 3*pi/2, 2*pi]);
yticks([-1, -0.5, 0, 0.5, 1]);
grid('on');
box('on');

如果直接使用数值作为标签,$\pi$ 会显示为小数,不够直观,可以结合 xticklabels 替换为自定义文字:

1
2
3
4
5
6
7
8
9
10
11
12
13
x = linspace(0, 2*pi, 100);
y = sin(x);

figure();
plot(x, y, LineWidth=1);
xticks([0, pi/2, pi, 3*pi/2, 2*pi]);
xticklabels({'$0$', '$\pi/2$', '$\pi$', '$3\pi/2$', '$2\pi$'});
yticks([-1, -0.5, 0, 0.5, 1]);
yticklabels({'$-1$', '$-0.5$', '$0$', '$0.5$', '$1$'});
ax = gca();
ax.TickLabelInterpreter = 'latex';
grid('on');
box('on');

这里通过 ax.TickLabelInterpreter = 'latex' 启用 LaTeX 解释器,使 xticklabels / yticklabels$...$ 包裹的 LaTeX 命令能被正确渲染。显式用 $...$ 包裹每个标签可以避免 MATLAB 自动包裹时的潜在问题。

对数坐标轴刻度格式化

对于对数坐标轴或双对数坐标轴,MATLAB 的自动刻度标签通常以 $10^{m}$ 形式显示,但在某些情况下会切换或混杂小数显示。

可以用 format_power_ticks 函数将刻度值统一格式化为 $a^b$ 的形式,通过显式指定底数(支持任意正整数底数,如 2 或 10)确保所需刻度以幂次形式呈现。

核心的 format_power_ticks 函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function labels = format_power_ticks(tick_values, base)
%FORMAT_POWER_TICKS 将幂次形式的刻度值格式化为 LaTeX 标签。
%
% labels = format_power_ticks(tick_values, base) 返回与 tick_values
% 大小相同的字符串数组。对于恰好等于 base 的整数次幂的刻度值,将其格式化为
% "$base^{exponent}$" 形式的 LaTeX 表达式;其余值保留普通的数值格式。
%
% 示例:
% ticks = 10 .^ (-4:2);
% yticks(ticks);
% yticklabels(format_power_ticks(ticks, 10));
% set(gca, 'TickLabelInterpreter', 'latex');

labels = strings(size(tick_values));

for i = 1:numel(tick_values)

if tick_values(i) <= 0 || ~isfinite(tick_values(i))
labels(i) = sprintf("%g", tick_values(i));
continue;
end

exponent = round(log(tick_values(i)) / log(base));

if abs(tick_values(i) - base ^ exponent) <= 1e-12 * max(1, abs(tick_values(i)))
labels(i) = sprintf("$%d^{%d}$", base, exponent);
else
labels(i) = sprintf("%g", tick_values(i));
end

end

end

函数的核心逻辑:对每个刻度值,计算 $\log_{a}(b)$ 并取整,如果 value 与 $a^{\text{exponent}}$ 在相对误差 $10^{-12}$ 内一致,则认为该值是 base 的整数次幂,格式化为 $10^{m}$,否则保持 %g 格式。

下面是一个完整的双对数图刻度格式化示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
h = [1/4, 1/8, 1/16, 1/32, 1/64, 1/128]';

figure();
loglog(h, h, 'o-', DisplayName='1st order');
hold('on');
loglog(h, h.^2, 's-', DisplayName='2nd order');
loglog(h, h.^4, '^-', DisplayName='4th order');
hold('off');

xlabel('h');
ylabel('Error');
grid('on');
box('on');
axis('padded');

ax = gca();
ax.XMinorGrid = 'off';
ax.YMinorGrid = 'off';

legend(Location='northwest');

% 设置 y 轴刻度为 10 的整数次幂,并用 LaTeX 格式显示
y_lim = ylim(ax);
y_pow = ceil(log10(y_lim(1))):floor(log10(y_lim(2)));
y_ticks = 10 .^ y_pow;
yticks(ax, y_ticks);
yticklabels(ax, format_power_ticks(y_ticks, 10));
ax.TickLabelInterpreter = 'latex';

% x 轴使用 2^n(h 是 2 的整数次幂),展示 format_power_ticks 对不同底数的支持
x_lim = xlim(ax);
x_pow = ceil(log2(x_lim(1))):floor(log2(x_lim(2)));
x_ticks = 2 .^ x_pow;
xticks(ax, x_ticks);
xticklabels(ax, format_power_ticks(x_ticks, 2));

这段代码中 y 轴使用底数 10 格式化为 $10^{m}$,x 轴使用底数 2 格式化为 $2^{n}$(因为数据 h 本身是 2 的整数次幂)。format_power_ticksbase 参数接受任意正整数底数,使得刻度标签可以直接对应数据的实际尺度。

二维绘图

下面列举几种常见的二维绘图方法,注意 meshgrid 提供的网格索引满足如下关系

1
2
3
4
5
6
7
8
9
X = zeros(ny,nx);
Y = zeros(ny,nx);

for i = 1:ny
for j = 1:nx
X(i,j) = x(j);
Y(i,j) = y(i);
end
end

对应的值矩阵 $U$ 满足 $U(i,j) = f(x_j,y_i)$,而不是通常的 $U(i,j) = f(x_i,y_j)$。

imagesc

  • 将二维数组映射为彩色图像,每个元素对应一个像素颜色
  • 自动缩放颜色范围,快速可视化矩阵或标量场

示例

1
2
3
4
V = rand(5,5);
imagesc(V);
colorbar();
axis('equal');

注意:

  • 默认 y 轴正方向倒置,有时需要使用 set(gca,'YDir','normal') 调整
  • 坐标轴对应矩阵索引,而非实际物理坐标,适合离散格点数据

pcolor

  • 绘制网格彩色图,每个网格单元显示颜色
  • 支持显示二维函数或规则网格数据

示例

1
2
3
4
5
[X,Y] = meshgrid(0:0.1:1, 0:0.1:1);
V = sin(pi*X).*cos(pi*Y);
pcolor(X, Y, V);
shading('flat');
colorbar();

注意:

  • 绘图尺寸比 imagesc 少一行和一列(MATLAB 会舍弃最后一行/列)
  • 默认有网格线,可用 shading('flat')shading interp 调整
  • 更适合规则网格数据,可显示二维函数的变化趋势

surf

  • 绘制三维曲面图
  • 可通过俯视 view(2) 转为二维彩色图
  • 支持 z 轴高度与颜色映射同时显示

示例

1
2
3
4
5
6
[x,y] = meshgrid(0:0.1:2*pi);
V = sin(x).*cos(y);
surf(x, y, V);
view(2);
shading('interp');
colorbar();

注意:

  • 默认三维显示,二维显示需 view(2)
  • shading('interp') 平滑颜色,shading('flat') 保留每个网格单元颜色
  • 绘制大量点时性能较低

contourf

  • 绘制填充等高线图
  • 可指定等高线数量,连续标量场可视化

示例:

1
2
3
4
[X,Y] = meshgrid(0:0.1:2*pi);
V = sin(X).*cos(Y);
contourf(X, Y, V, 20);
colorbar();

注意:

  • NContour 参数可调整等高线数量
  • 对数据网格稀疏或离散点插值不够平滑
  • 默认填充颜色,边界线可用 LineColor 调整

图像保存与导出

MATLAB 提供了很多种保存绘图的方式,需要区分两类保存方式:

  • savefig 代表的保存,得到的是 MATLAB 绘图对象的完整信息,使用 .fig 格式,可以在 MATLAB 中重新加载和编辑图片,但是不能使用图片查看器打开;( fig 格式是 MATLAB 的私有数据格式,与之不同的是,mat 格式是开源的)
  • saveas 代表的导出,导出的是静态图像文件,以通用的图片格式保存,如 PNG、JPEG 等,无法在 MATLAB 中重新编辑。

注意:

  • 在常见的图片格式中,pdf,eps,svg 通常是矢量图,png 和 jpg 通常是位图。矢量图是绝对精准的,只有位图才有分辨率的概念。
  • MATLAB 提供的不同命令,导出的不同图片格式,可能会导致最终在图片的尺寸有所差异,因为各种格式的算法以及空白裁剪等细节处理存在差异。

savefig

使用 savefig 可以直接保存当前绘图窗口到文件中,只需要提供文件名

1
2
3
4
5
6
7
8
9
x = linspace(0, 30);

figure();
plot(x, sin(x));
title('Sine');
xlabel('x');
ylabel('sin(x)');

savefig('demo.fig')

最好指定 figure 句柄进行保存

1
2
3
4
5
6
7
8
9
x = linspace(0, 30);

fig1 = figure();
plot(x, sin(x));
title('Sine');
xlabel('x');
ylabel('sin(x)');

savefig(fig1,'demo.fig')

或者使用 gcf 获取当前绘图窗口句柄并保存

1
2
3
4
5
6
7
8
9
x = linspace(0, 30);

figure();
plot(x, sin(x));
title('Sine');
xlabel('x');
ylabel('sin(x)');

savefig(gcf(),'demo.fig')

我们可以在MATLAB中直接点击 .fig 文件来打开它,也可以在代码中使用 openfig 来打开

1
fig = openfig('demo.fig')

saveas

使用 saveas 可以导出为某个具体的图片格式,例如

1
2
3
4
5
6
7
8
9
x = linspace(0, 30);

fig1 = figure();
plot(x, sin(x));
title('Sine');
xlabel('x');
ylabel('sin(x)');

saveas(fig1, 'demo.png');

可以读取 fig 文件并导出图片

1
2
fig = openfig('demo.fig');
saveas(fig, 'demo.png');

注意:

  • 必须为 saveas 函数提供 figure 句柄,可以使用 gcf 获取当前绘图窗口句柄。
  • 导出图片的格式会根据文件后缀名推测,例如 xxx.pngxxx.jpg缺少后缀则会保存为 fig 文件。
  • 这个函数不支持加上一些选项,例如分辨率等,推荐用 exportgraphics 函数替代。

print

print 函数也可以将绘图导出为图片,图片格式必须通过选项传递。

导出 png 格式图片

1
print(fig, 'demo.png', '-dpng');

导出 eps 格式图片

1
print(fig, 'demo.eps', '-depsc');

注意这里必须使用 -depsc 选项才能导出彩色图片,-deps 只能导出黑白图片。

导出 svg 格式图片

1
print(fig, 'demo.svg', '-dsvg');

导出 pdf 格式图片

1
print(fig, 'demo.pdf', '-dpdf');

exportgraphics

对于 R2020a 之后的版本,推荐使用新的 exportgraphics 函数,这个函数可以将 图形(figure)或坐标轴(axes)导出到 EPS、PDF、PNG、JPEG 等格式,是 MATLAB 官方推出的替代旧的 printsaveas 的现代接口。

这个接口比较新,网上并没有太多资料,具体参考 官方文档

导出位图,可以指定分辨率(gcf() 获取当前 figure句柄,也可以替换为具体的 figure 句柄)

1
2
exportgraphics(gcf(), 'demo.jpg', Resolution = 300)
exportgraphics(gcf(), 'demo.png', Resolution = 600)

导出矢量图

1
exportgraphics(gcf(), 'demo.pdf', ContentType = 'vector')

对于矢量图,通常加上 ContentType = 'vector' 强制使用矢量图,否则 MATLAB 会自动选择,与之对应的是强制位图 ContentType = 'image'

这个函数直到 R2025a 才支持 svg 格式的图片导出。

exportgraphics 函数除了可以导出整个 figure,还可以按照坐标轴导出部分图形。

自定义绘图样式

与 matplotlib 一样,MATLAB 提供了接口进行全局层面的默认绘图样式定制,下面是一些例子。

使各种文本默认支持 LaTeX(相当于自动添加 Interpreter = 'latex'

1
2
3
set(groot, 'defaultTextInterpreter', 'latex');
set(groot, 'defaultAxesTickLabelInterpreter', 'latex');
set(groot, 'defaultLegendInterpreter', 'latex');

调整字号和字体

1
2
3
4
5
6
set(groot, 'defaultAxesFontSize', 14);
set(groot, 'defaultTextFontSize', 14);
set(groot, 'defaultLegendFontSize', 14);

set(groot, 'defaultAxesFontName', 'Times New Roman');
set(groot, 'defaultTextFontName', 'Times New Roman');

调整线宽(默认线宽 0.5 太细)、标记大小、坐标轴和刻度线、网格线宽度等

1
2
3
4
set(groot, 'defaultLineLineWidth', 1.0);
set(groot, 'defaultLineMarkerSize', 5);

set(groot, 'defaultAxesLineWidth', 1.0);

让所有坐标轴默认显示边框(相当于自动添加 box('on')

1
set(groot, 'defaultAxesBox', 'on');

设置默认图形渲染器为 painters(矢量渲染),默认通常是 OpenGL,前者导出矢量图质量更好,但是绘图速度会变慢

1
set(groot, 'defaultFigureRenderer', 'painters');

设置 figure 的默认尺寸单位为英寸,以及设置默认 figure 的位置与大小

1
2
set(groot, 'defaultFigureUnits', 'inches');
set(groot, 'defaultFigurePosition', [1 1 6.5 4.5]);

设置图例自动放置在“最好的”位置,默认会倾向于放在右上角。

1
set(groot, 'defaultLegendLocation', 'best');

可以通过如下命令撤销对所有全局默认样式的修改

1
reset(groot)

将上面提到的配置汇总整理如下(保存为单独的文件 set_groot_plot_style.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
reset(groot);

set(groot, 'defaultTextInterpreter', 'latex');
set(groot, 'defaultAxesTickLabelInterpreter', 'latex');
set(groot, 'defaultLegendInterpreter', 'latex');

set(groot, 'defaultAxesFontSize', 14);
set(groot, 'defaultTextFontSize', 14);
set(groot, 'defaultLegendFontSize', 14);
set(groot, 'defaultAxesFontName', 'Times New Roman');
set(groot, 'defaultTextFontName', 'Times New Roman');

set(groot, 'defaultLineLineWidth', 1.0);
set(groot, 'defaultLineMarkerSize', 5);

set(groot, 'defaultAxesBox', 'on');
set(groot, 'defaultAxesLineWidth', 1.0);

set(groot, 'defaultFigureRenderer', 'painters');
set(groot, 'defaultFigureUnits', 'inches');
set(groot, 'defaultFigurePosition', [1 1 6.5 4.5]);

set(groot, 'defaultLegendLocation', 'best');

测试脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
clc;
clear;
close all;
cd(fileparts(mfilename('fullpath')));

%% Example 1
h = [1/4, 1/8, 1/16, 1/32, 1/64, 1/128];

reset(groot);

figure();
loglog(h, h, 'o-');
hold('on');
loglog(h, h .^ 2, 's-');
loglog(h, h .^ 4, '^-');
hold('off');
xlabel('h');
ylabel('Error');
title('Default MATLAB Style');
grid('on');
axis('padded');
ax = gca();
ax.XMinorGrid = 'off';
ax.YMinorGrid = 'off';
ax.XMinorTick = 'off';
ax.YMinorTick = 'off';
legend('1st order', '2nd order', '4th order');
exportgraphics(gcf(), 'matlab-plot-1-default.png')

set_groot_plot_style

figure();
loglog(h, h, 'o-');
hold('on');
loglog(h, h .^ 2, 's-');
loglog(h, h .^ 4, '^-');
hold('off');
xlabel('h');
ylabel('Error');
title('Scientific Style');
grid('on');
axis('padded');
ax = gca();
ax.XMinorGrid = 'off';
ax.YMinorGrid = 'off';
ax.XMinorTick = 'off';
ax.YMinorTick = 'off';
legend('1st order', '2nd order', '4th order');
exportgraphics(gcf(), 'matlab-plot-1-science.png', Resolution = 300)

%% Example 2
func = @(x, p) x .^ (2 * p + 1) ./ (1 + x .^ (2 * p));
x = linspace(0.75, 1.25, 201);
plist = [10, 15, 20, 30, 50, 100];

reset(groot);

figure();
hold('on');

for p = plist
y = func(x, p);
plot(x, y, DisplayName = ['p=' num2str(p)]);
end

hold('off');
legend();
axis('tight')
xlabel('x');
ylabel('y');
title('Default MATLAB Style')
exportgraphics(gcf(), 'matlab-plot-2-default.png')

set_groot_plot_style

figure();
hold('on');

for p = plist
y = func(x, p);
plot(x, y, DisplayName = ['$p=' num2str(p) '$']);
end

hold('off');
legend();
axis('tight')
xlabel('$x$');
ylabel('$y$');
title('Scientific Style')
exportgraphics(gcf(), 'matlab-plot-2-science.png', Resolution = 300)

Example 1

matlab-plot-1-default matlab-plot-1-science

Example 2

matlab-plot-2-default matlab-plot-2-science