MATLAB グラフ 関連 Tips
まえがき
主に記事掲載済みの中から、figure や plot 等、グラフ関連の Tips をピックアップしてみようかと思います。
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')
リフローレイアウト
"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')
グラフィックオブジェクト
各オブジェクトを取っておくと、後からそれらのプロパティを変更することができます。
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;
これがデフォルトの状態です。
メニューバーを消して、マウスポインターの形・タイトル・背景色を変更してみます。
f1.MenuBar = 'none';
f1.Pointer = 'hand';
f1.NumberTitle = 'off';
f1.Name = 'Test Figure';
f1.Color = [1 1 1];
座標軸を追加します。
データ描画すれば自動的に座標軸が追加されるので通常は必要ありません。
ax = gca;
x 軸 の目盛りを消します。
ax.XTick = [];
目盛りは自由に設定できます。
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')
grid も各プロパティがあります。
ax.GridColor = 'b';
ax.GridAlpha = 0.7;
ax.GridLineStyle = '--';
軸を消し、データだけを入れ替えてグラフをアニメーション化します。
データのみを入れ替えることにより(多少の)描画の高速化が図れ、フォーカスがグラフに移動しないので 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
ライブエディターの場合は、処理後に再生速度を変えてリプレイしたり、好きなフレームを再生したり、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
グラフィックオブジェクトを使えば、描画後でも透明度を変えられます。
スクリプトは長くなるので下のリンク先をご覧ください。
参考
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')
凡例をまとめて出すこともできます。各グラフのデータ列が共通である時など、それぞれのグラフに出す必要がない場合にも便利ですね。
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";
また、一部のデータの凡例を出したくない場合、省略すると 'data?' が勝手に付いてしまいます。
y3C =rand(10,3);
plot(y3C(:,1),DisplayName='D1');
hold on
plot(y3C(:,2));
plot(y3C(:,3),DisplayName='D3');
hold off
legend
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')
それ以前のバージョンで消したい場合は、グラフィックオブジェクトを使います。
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
参考
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))
しかし画像だと上下間に隙間が空いてしまうようです。
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 よりプロパティの多い従来の 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);
なぜかライブエディターではうまく表示されなかったりするようですが・・。
Units = ''normalized''; (デフォルト設定)のまま、[0, 0.5]で指定してもよいですね。以下のリンク先ではそうしています。
参考
MATLABでマウスインタラクティブGUI ~ 画像を馴染ませてコピー ~ Poisson Image Editing
Simulink で plot、bar
Simulink ではタイムスコープ表示が標準であり、スクリプトで使える plot 系( bar や scatter 等)に相当するブロックがありません。
Scope でもフレーム単位の表示とすることで plot っぽい表示もできますが、 plot の方が見やすい場合があります。
実は、MATLAB Functionブロック内に書けば plot も普通に使えます。
function fcn(u)
plot(u)
plot Function
bar の場合はコード生成に対応していないので、coder.extrinsic を使って外部関数として定義します。ただし実行のたびに外部呼び出しが行われるので、実行速度はかなり遅くなります。
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 軸目色コピー
要 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')
参考
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);
参考
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
プロットカタログ
数日前まで知りませんでしたが、ワークスペースの変数を右クリックすると、プロット関連のヘルパーが出るようです。
「プロットカタログ」を選択するとさらに色々と!
データを選択して「プロット」タブで色々出るのは気付いていましたが、この右下の「カタログ」に気付いていませんでした・・。これが直接呼べるようです。
ほんと MathWorks さん、しれっと色々入れてきますよね。(;¬_¬)
あとがき
「基本過去記事からピックアップすればいいし、軽い感じでいけそう!」と思って書き始めたのに、思ったより書くこといっぱいあって、途中でくじけそうになりました。(;´Д`)
更新情報等があれば、追記するかもしれません。
耳寄り情報もお待ちしております!