見出し画像

MATLAB グラフ 関連 Tips



まえがき

主に記事掲載済みの中から、figure や plot 等、グラフ関連の Tips をピックアップしてみようかと思います。


MATLAB R2023a で追加された plot 関連 を別記事としてまとめました。

2023/03/16


Tips

Basics

plot() は、データ行列の各列に対して 1 本のラインを引きます。

指定方法は複数あるので、その例をいくつか示します。
詳細は下のスクリプトをご参照ください。

plot 自体でのプロパティ指定には制限があるので、より自由な設定をするには後述するグラフィックオブジェクトを使います。

タイトル内で改行をするにはセルで指定します。

clear
close all

n = 10;
x1 = 1:n;
x2 = 1:2:n*2;
y1C = rand(n,1);
y2C = rand(n,2);

tiledlayout(3,3)  % 3x3 のレイアウト指定

nexttile  % 新規座標軸作成
plot(y1C)
title('y データのみで plot')

nexttile
plot(y2C)
title('2 列の y データを plot')

nexttile
plot(x2,y1C)
title('x で指定した座標に y を plot')

nexttile
plot(x1,y2C)
title({'共通の 1 列の x で指定した','座標に 2 列の y を plot'})  % タイトルを改行するにはセルで指定

nexttile
yyaxis left  % 左軸指定
plot(x1,y1C)
yl = ylim;  % y 軸の範囲を取得
yyaxis right  % 右軸指定
plot(x1,y1C/2)
ylim(yl)  % 左軸の y 軸範囲に合わせる
title('2 軸 plot')

nexttile
plot([x1',x2'],y2C)
title({'異なる x 座標に','各列データを plot'})

nexttile
plot(x1,y1C,'rdiamond',x1,y1C/2,'k-.*') 
title({'色、ラインスタイル、マーカー','等は個別に指定可能'})

nexttile
plot(x1,y1C,x1,y1C/2,'--') 
title({'ラインプロパティは','個別に省略可能'})

nexttile
plot(x1,y1C,x1,y1C/2,LineWidth=1.5)
title({'ラインプロパティは','一部を除き共通指定'})

sgtitle('Plot example')
各種 plot 指定方法


リフローレイアウト

"tiledlayout flow"
とすれば、最初に座標軸数を指定する必要がなく、マウスドラッグで figure のアスペクト比を変えると自動でリフローします。

テキストはデフォルトで TeX マークアップとして解釈されます。
LaTeX を使いたい場合は title(t,'interpreter','latex')、
プレーンテキストとして表示したい場合は title(t,'interpreter','none') とします。

clear
close all

tiledlayout flow

nexttile
plot(rand(10,1))
title('G_1')  % 下付文字指定

nexttile
plot(rand(10,2))
title('G_2')

nexttile
plot(1:10,rand(10,1),'gx-', 1:10,rand(10,1),'bo--')
title('G_3')

sgtitle('Plot example')
リフロー例1
リフロー例2
リフロー例3


グラフィックオブジェクト

各オブジェクトを取っておくと、後からそれらのプロパティを変更することができます。

f1 = figure;  % figure オブジェクト

ax1 = gca;  % 座標軸オブジェクト
ax1 = subplot(2,1,1);  % 座標軸オブジェクト
ax1 = nexttile;  % 座標軸オブジェクト

p1 = plot(rand(10,2));  % グラフィックオブジェクト

各プロパティは、エディター内であれば自動で候補が出てきます。

ライブエディター画面

コマンドウィンドウでは TAB で補完候補が出ます。

コマンドウィンドウでオブジェクトを打てば、プロパティの先頭部分が表示されます。「すべてのプロパティ を表示」をクリックで続きが表示されます。

>> p=plot(rand(10,1));
>> p

p = 

  Line のプロパティ:

              Color: [0 0.4470 0.7410]
          LineStyle: '-'
          LineWidth: 0.5000
             Marker: 'none'
         MarkerSize: 6
    MarkerFaceColor: 'none'
              XData: [1 2 3 4 5 6 7 8 9 10]
              YData: [0.4046 0.4484 0.3658 0.7635 0.6279 0.7720 … ]

  すべてのプロパティ を表示
>> 

グラフィックオブジェクトプロパティ

実例を示します。

clear
close all
f1 = figure;
figure(デフォルト)

これがデフォルトの状態です。

メニューバーを消して、マウスポインターの形・タイトル・背景色を変更してみます。

f1.MenuBar = 'none';
f1.Pointer = 'hand';
f1.NumberTitle = 'off';
f1.Name = 'Test Figure';
f1.Color = [1 1 1];
figure プロパティ変更例


座標軸を追加します。
データ描画すれば自動的に座標軸が追加されるので通常は必要ありません。

ax = gca;
座標軸の追加

x 軸 の目盛りを消します。

ax.XTick = [];
x 軸目盛り消去

目盛りは自由に設定できます。

ax.XTick = [0 0.25 0.5 0.7 0.8 0.9];
目盛り指定


ラベルも変えられます。Tick より少ない場合は繰り返しになります。
ついでに Y 軸目盛りを傾け、フォントサイズを大きくしてみます。

ax.XTickLabel =[{'L_0','L_1','\pi','--'}];
ax.YTickLabelRotation = 45;
ax.FontSize = 14
ラベル指定


デフォルトに戻したい場合は 'auto' 、あるいはデフォルト値に設定します。

ax.XTickMode = 'auto';
ax.XTickLabelMode = 'auto';
ax.YTickLabelRotation = 0;
ax.FontSize = 9;


データを plot します。

dth = pi/50;
th = 0:dth:4*pi-dth;
p = plot(th,sin(th),th,cos(th));
ylim(ax,([-1.1 1.1]))
初期グラフ

ラインの色や太さを変更します。
colororder() で現在の色の順番行列が取れます。

col = colororder;
p(1).Color = [1 0.5 0];
p(1).LineWidth = 1.5;
p(2).Color = col(1,:);
プロパティのみを変更

2番目に描いたグラフを、元の1番目の色に再設定しています。


set() を使うと、セル形式で複数ラインのプロパティを一度に設定することもできます。

set(p,{'LineStyle'},{'--';'-.'})
複数ラインのプロパティをセルで設定

2ライン共通設定で戻します。

set(p,'LineStyle','-')
共通設定で元に戻す


log スケールグラフを描くには loglog(), semilogx(), semilogy() を使いますが、これもプロパティで後から変えられます。

ax.XScale = 'log';
grid(ax,'on')
grid(ax,'minor')
x 軸を log スケールに変更

grid も各プロパティがあります。

ax.GridColor = 'b';
ax.GridAlpha = 0.7;
ax.GridLineStyle = '--';
grid プロパティ


軸を消し、データだけを入れ替えてグラフをアニメーション化します。

データのみを入れ替えることにより(多少の)描画の高速化が図れフォーカスがグラフに移動しないので GUI を作るときには特に有効です。

grid(ax,'off')
axis(ax,'off')  % 軸非表示

for t = 1:length(th)
    ty = p(1).YData(1);
    p(1).YData(1:end-1) = p(1).YData(2:end);
    p(1).YData(end) = ty;
    drawnow
end
ライブエディターでエクスポートした GIF ファイル


ライブエディターの場合は、処理後に再生速度を変えてリプレイしたり、好きなフレームを再生したり、avi / mp4 / gif にエクスポートすることができます。

再生速度変更
タイムバー操作
ファイル書き出し


グラフィックオブジェクト配列


グラフィックオブジェクトをあらかじめ配列で取っておきたい場合は、gobjects を使って初期化します。あとで変数を使ってアクセスしたい場合などに便利です。

th = 0:pi/20:2*pi;
y = sin(th);
dys = 0:0.2:1;
N = length(dys);
p = gobjects(1,N);  % initialize graphic object matrix
cnt = 1;
for dy = dys
    p(cnt) = plot(th, (y+dy),'b');
    if cnt == 1; hold on; end  % 重ね描き ON
    cnt = cnt + 1;
end
hold off  % 重ね描き OFF
ylim([-1 2.5])
初期グラフ
p(1).Color = [p(1).Color 0.2];
p(1).LineWidth = 2;
p(2).Color = [0.87 0 0];
p(3).LineWidth = 2;
p(end).Color = [0.87 0.5 0];
p(end).YData = p(end).YData + 0.2;
p(end).LineStyle = '--';
p(end).Marker = 'o';
プロパティのみを変更

参考
MATLAB で透明度付きプロット~円は rectangle で

ストロークを平均化 ~ MATLAB の figure に callback を設定してお絵かき



透明度

上の例でも使っていますが、plot() 等にはラインの透明度を指定するオプションがないことになっていますが(ドキュメントに記載がなく、プロパティにも表示されない)、color の 4 次元目で指定ができます。

for alpha = 0:0.1:1
    plot(y+alpha, LineWidth=2, Color=[1 0.5 0 alpha])
    if alpha == 0; hold on; end
end
hold off
plot 関数による透明度付きプロット

グラフィックオブジェクトを使えば、描画後でも透明度を変えられます。
スクリプトは長くなるので下のリンク先をご覧ください。

一旦 α =1 で描画後、透明度を変更

参考
MATLAB で透明度付きプロット~円は rectangle で


凡例

凡例は通常、プロットされたデータ系列ごとのラベルを後から追加します。

legend('Label1','Label2')


それとは別に、プロット時に "DisplayName" を使って指定しておく方法があり、後から legend だけで表示ができます。 

プログラム上、場合によって plot するしないが変わるときも、legend 
の方を変えずに済むので便利です。

tiledlayout flow
nexttile
plot(rand(10,1),DisplayName='D1');
title('G_1')
legend

nexttile
p2 = plot(rand(10,2));
set(p2,{'DisplayName'}, {'D2';'D3'});
title('G_2')
legend

nexttile
plot(rand(10,1),'gx-',DisplayName = 'D4')
hold on
plot(rand(10,1),'bo--',DisplayName = 'D5');
hold off
title('G_3')
legend

sgtitle('Plot example')
DisplayName を使った凡例表示


凡例をまとめて出すこともできます。各グラフのデータ列が共通である時など、それぞれのグラフに出す必要がない場合にも便利ですね。

tiledlayout flow
nexttile
p1 = plot(rand(10,1),DisplayName='D1');
title('G_1')

nexttile
p2 = plot(rand(10,2));
set(p2,{'DisplayName'}, {'D2';'D3'});
title('G_2')

nexttile
p3 = plot(1:10,rand(10,1),'gx-',1:10,rand(10,1),'bo--');
set(p3,{'DisplayName'}, {'D4';'D5'});
title('G_3')

sgtitle('Plot example')

le = legend([p1;p2;p3]);
le.NumColumns = 3;
le.Orientation = "horizontal";
le.Layout.Tile = 4;
% le.Layout.Tile = "south";

下から5行目、
 le = 
が抜けていたので修正しました。

2023/07/20
凡例のタイル表示
le.Layout.Tile = "south"; の場合の例


また、一部のデータの凡例を出したくない場合、省略すると 'data?' が勝手に付いてしまいます。

y3C =rand(10,3);

plot(y3C(:,1),DisplayName='D1');
hold on
plot(y3C(:,2));
plot(y3C(:,3),DisplayName='D3');
hold off

legend
data? が自動で付く

DisplayName='' にしても、ライン部分は消えません。

plot(y3C(:,1),DisplayName='D1');
hold on
plot(y3C(:,2),DisplayName='');
plot(y3C(:,3),DisplayName='D3');
hold off

legend
ライン部分は残る

このような場合、R2021a 以降では legend での空文字指定でそのデータの凡例自体を消せるようになっています。

plot(y3C(:,1));
hold on
plot(y3C(:,2));
plot(y3C(:,3));
hold off

legend('D1', '', 'D3')
R2021a 以降

それ以前のバージョンで消したい場合は、グラフィックオブジェクトを使います。

p1 = plot(y3C(:,1),'DisplayName','D1');
hold on
p2 = plot(y3C(:,2));
p3 = plot(y3C(:,3),'DisplayName','D3');
hold off

legend([p1, p3])
旧バージョン


アノテーション

annotation() はテキスト矢印をグラフに追加できます。

しかしなぜかグラフ座標ではなく、(0, 1) の正規化座標でしか指定できません!

これはたいへん面倒です。

しかし、一旦 plot してから GUI 上で挿入する手があります。

テキスト矢印の挿入

マウスで始点から終点(矢印部分)へドラッグして矢印を書き、その後テキストを挿入します。

挿入されたテキスト矢印

そしてこれを、コードに変換することもできます。

コードの生成
% textarrow を作成
annotation(figure1,'textarrow',[0.525 0.392857142857143],...
    [0.872809523809524 0.786507936507936],'String',{'Peak'});

自動生成されたコードのアノテーション部分

これはぜひ、グラフ座標で指定できるよう改善していただきたいですけどね。


MATLAB の標準関数に円を描画する関数はありません。
「は?sin/cos で描けばいいだろ?」
「sin/cos 分からないヤツなんか、いねえよなぁ!!?」
という MathWorks さんの雄叫びが聞こえてきます(?)。
(Image Processing Toolbox には "viscircles" があります)

もちろん sin/cos を使えば描けますが、"rectangle()" で "Curvature=[1 1]" と指定すれば(楕)円になります(=1でもok)

「曲率」が分かれば描かせてもらえるようです。

ただしあくまでも矩形描画関数ですので、外接矩形の左下座標を指定する必要があります。

for r=0.1:0.1:1
    pos = [0 0 r r];
    rectangle(Position=pos, LineWidth=2, EdgeColor=[0 0 1 r^3], Curvature=[1 1])
    pos = [-r/2 -r/2 r r];
    rectangle(Position=pos, LineWidth=2, EdgeColor=[1 0 0 r^3], Curvature=[1 1])
end
rectangle 関数による円の描画

参考
MATLAB で透明度付きプロット~円は rectangle で


きっちり figure

最近、tiledlayout を使うと複数グラフをかなりきちきちに並べられるようになりました。

f1 = figure;
t = tiledlayout(2,2,TileSpacing="none",Padding='tight');

nexttile
plot(rand(10,1))
nexttile
plot(rand(10,1))
nexttile([1 2])
plot(rand(10,1))
tiledlayout によるレイアウト例

しかし画像だと上下間に隙間が空いてしまうようです。

f1 = figure;
f1.MenuBar='none';

t = tiledlayout(2,2,TileSpacing="none",Padding='tight');

im1 = imread('ayaka18_007s.jpg');  % W450 x H300
im2 = imread('ayaka18_004s.jpg');  % W450 x H300
im3 = imread('ayaka18_007_004s.jpg');  % W900 x H300

nexttile
imshow(im1)
nexttile
imshow(im2)
nexttile([1 2])
imshow(im3)
tiledlayout によるレイアウト例

そこで、今のところ tiledlayout よりプロパティの多い従来の subplot を使います。
座標は、左下が (0,0) です。

f1 = figure;
hs = size(im1,1)
ws = size(im1,2)
f1.InnerPosition = [0 0 ws*2 hs*2];
ax1 = subplot(2,2,1);
ax1.Units = 'pixels';
ax1.Position = [0 hs ws hs];  % [left bottom width height] 
imshow(im1, Parent=ax1);

ax2 = subplot(2,2,2);
ax2.Units = 'pixels';
ax2.Position = [ws hs ws hs];
imshow(im2,Parent=ax2);

ax3 = subplot(2,2,3);
ax3.Units = 'pixels';
ax3.Position = [0 0 ws*2 hs];
imshow(im3,Parent=ax3);
subpot によるレイアウト例

なぜかライブエディターではうまく表示されなかったりするようですが・・。

Units = ''normalized''; (デフォルト設定)のまま、[0, 0.5]で指定してもよいですね。以下のリンク先ではそうしています。

参考
MATLABでマウスインタラクティブGUI ~ 画像を馴染ませてコピー ~ Poisson Image Editing


Simulink で plot、bar 

Simulink ではタイムスコープ表示が標準であり、スクリプトで使える plot 系( bar や scatter 等)に相当するブロックがありません。

Scope でもフレーム単位の表示とすることで plot っぽい表示もできますが、 plot の方が見やすい場合があります。

実は、MATLAB Functionブロック内に書けば plot も普通に使えます。

Simulink モデル
Constant
function fcn(u)

plot(u)

plot Function

Simulink モデル実行結果

bar の場合はコード生成に対応していないので、coder.extrinsic を使って外部関数として定義します。ただし実行のたびに外部呼び出しが行われるので、実行速度はかなり遅くなります。

参考
Simulinkで高速bar描画


2軸 plot

yyaxisを使うと2 本の y 軸があるグラフが描けますが、軸は左右に分かれます。
subplot を使って狭い領域を作り、そこに軸だけを表示することにより、より自由度の高い 2 軸グラフを作成します。

x = linspace(1,10);
f = figure;

% 軸のみ用
p1 = subplot(1,10,1);
xlim(p1,[0 0.1])
p1.XTick = [];  % x 軸目盛り消去
p1.Color = f.Color;  % 座標軸背景を figure と同色に
p1.XAxis.Visible = 'off';  % x 軸消去

p2 = subplot(1,10,2:10);
yyaxis(p2,'left')  % 1 軸目描画指定
plot(p2,x,sin(x))
yyaxis(p2,'right')  % 2 軸目描画指定
plot(p2,x,exp(x))
y2Col = p2.YColor; % 2 軸目色取得
grid(p2,"on")

ty = p2.YTick; % 2 軸目 y 軸目盛り取得
p2.YTick = []; % 2 軸目 y 軸目盛り消去
p2.YAxis(2).Visible = 'off'; % 2 軸目 y 軸消去

p1.YTick = ty; % 2 軸目 y 軸目盛りコピー
ylim(p1,[ty(1) ty(end)]) % 2 軸目 y 軸範囲コピー
p1.YColor = y2Col; % 2 軸目色コピー
左端に 1/10 幅の領域を設定


要 Audio Toolbox ですが、実際に使ったもう少し複雑な例を。
xline(), yline() は、始点・終点の指定なしに垂直・水平ラインが描けます。

erbst = round(hz2erb(20));  erben = round(hz2erb(20e3));
filnum = erben - erbst + 1;
gammaFiltBank = gammatoneFilterBank('SampleRate',48000, 'NumFilters',filnum, 'FrequencyRange',[erb2hz(erbst), erb2hz(erben)]);
[h,f] = freqz(gammaFiltBank);

erbs = erbst:4:erben;

p2 = subplot(40,1,33:40);
p2.XScale = 'log';
xline(p2,erb2hz(erbs),':')  % 垂直ライン
xlim(p2,[erb2hz(erbst) erb2hz(erben)])
p2.XTick = [];
p2.YAxis.Visible = 'off';
p2.XTick = erb2hz(erbs);
p2.XTickLabel = num2cell(erbs);
xlabel(p2,'ERB')

p1 = subplot(40,1,1:32);
semilogx(p1,f,20*log10(abs(h(:,erbs))))
xline(p1,erb2hz(erbs))  % 垂直ライン
tx = p1.XTick;
xlim(p1,[erb2hz(erbst) erb2hz(erben)])
xlabel(p1,'Frequency (Hz)')
ylim(p1,[-60 6])
ylabel(p1,'Gain (dB)')
grid(p1,'on')
title(p1,'Gammatone Filter Bank')
上部に 33/40、下部に 7/40 幅の領域を設定

参考
MATLAB で Digital Lossy VST プラグインを作る ~ 聴覚心理特性とは? ~ 非同期バッファで MDCT


patch

patch() は、正式に透明度をサポートしています。

ただ、本来は多角形の描画関数なので、終点から始点へも線が引かれてしまいます。開曲線を描きたい場合は、最後に NaN を付けます。

th = 0:pi/20:2*pi;
y = sin(th);
th2 = [th NaN];  y2 = [y NaN];
patch(th2,y2,'b','LineWidth',2,'EdgeColor','b',"EdgeAlpha",0.1);
patch(th2,y2+0.2,'b','LineWidth',2,'EdgeColor','b',"EdgeAlpha",0.5);
patch 関数による透明度付きプロット

参考
MATLAB で透明度付きプロット~円は rectangle で


似たような関数で "polyshape" があります。こちらも透明度は指定可能ですが「囲まれた領域」が含まれている必要があり、上記のような指定はできません。

参考
MATLAB で計算幾何学 ~ polyshape() ~ 多角形オブジェクトで一括座標変換


タブ表示

タブにグラフを表示します。タブをクリックすれば表示が切り替わります。

f = figure;
tabgp = uitabgroup(f);

tab(1) = uitab(tabgp,'Title','plot1');
axes(tab(1));
plot(rand(10,1))

tab(2) = uitab(tabgp,'Title','plot2');
axes(tab(2));
plot(rand(10,2))

tab(3) = uitab(tabgp,'Title','plot3');
axes(tab(3));
plot(rand(10,3))

tab(4) = uitab(tabgp,'Title','plot4');
axes(tab(4));
plot(rand(10,4))
タブグラフ

参考
MATLABでMAPを表示する~地図上に経路ログ表示~無料アプリでもOK!


アニメーションライン

自由度はあまり高くはないですが、アニメーション化されたラインが簡単に作成できます。
animatedline() でアニメーション化ラインを作成し、addpoints() で点を追加します。点の最大数を設定することで、点を追加するたびに自動で古い点が消去されます。

ライブエディターでのエクスポートではフレームレートが設定できないため、下の例では自前ルーチンで GIF ファイルを書き込んでいます。

ax = axes;
axis(ax,'off')
cla
h1 = animatedline(Marker = "pentagram", ...
    MarkerEdgeColor = "m", ...
    MarkerFaceColor = "m", ... 
    MarkerSize = 20, MaximumNumPoints = 1);  % star
h2 = animatedline(LineWidth = 2, Color = [0.4940 0.1840 0.5560], ...
     MaximumNumPoints = 10);  % tail
axis([-1,1,-1,1])

dth = pi/100;
th = 0:dth:2*pi;

we = 1;
filename = 'ShootingStar.gif';
x = sin(5*th);
y = sin(6*th);
for k = 1:length(th)
    addpoints(h2,x(k),y(k))
    addpoints(h1,x(k),y(k))
    drawnow
    if we  % GIF ファイル書き込み
        frame = getframe(gca);  % take image only
        gr = frame2im(frame);
        [bA,map] = rgb2ind(gr,256);
        if (k==1)
            imwrite(bA,map,filename,'gif','LoopCount',Inf,'DelayTime',1/30);
        else
            imwrite(bA,map,filename,'gif','WriteMode','append','DelayTime',1/30);
        end
    end
end
アニメーションライン


MarkerIndices

プロットは滑らかにしたいのでポイント数は増やしたいがそれだとマーカーが密になりすぎて見にくい場合等、マーカーの間隔を指定することができます。アニメーションラインでは設定できません。

dth = pi/20;
th = 0:dth:2*pi;
y = sin(th);
plot(th,y,'b-p','MarkerIndices',1:4:length(th),'MarkerSize',10);
hold on
plot(th,y,'r*','MarkerIndices',3:4:length(th),'MarkerSize',8);
hold off
Maker 間隔を指定
plot3 を使った例

参考
Christmas Tree Illusion


プロットカタログ

数日前まで知りませんでしたが、ワークスペースの変数を右クリックすると、プロット関連のヘルパーが出るようです。

変数を右クリック


「プロットカタログ」
を選択するとさらに色々と!

プロットカタログ


データを選択して「プロット」タブで色々出るのは気付いていましたが、この右下の「カタログ」に気付いていませんでした・・。これが直接呼べるようです。

「プロット」タブ


ほんと MathWorks さん、しれっと色々入れてきますよね。(;¬_¬) 


あとがき

「基本過去記事からピックアップすればいいし、軽い感じでいけそう!」と思って書き始めたのに、思ったより書くこといっぱいあって、途中でくじけそうになりました。(;´Д`)

更新情報等があれば、追記するかもしれません。

耳寄り情報もお待ちしております!


いいなと思ったら応援しよう!