Edited at

【MATLAB】PowerPoint スライド作成自動化


概要

MATLAB の処理結果をまとめるのに PowerPoint を使う方向けのメモです。

サンプルコードの1つとして役に立てばうれしいです。

この記事では、機械学習ではド定番のアヤメのデータを使って、

複数のアルゴリズム検証結果をパワポに纏める という体の サンプルコードを紹介します。

レポート作成に関わる単純な繰り返し作業を自動化する、そんな時のヒントになりますように。

自動生成したスライド例:

Capture3.PNG


環境


  • MATLAB R2019a

  • Statistics and Machine Learning Toolbox*)

*) こちらは機械学習部分で使用しており、パワポの作成自体は MATLAB 本体だけでOKです。


レポート生成作業は大変

処理結果を人に見せる資料にするときどうしていますか?

私は主に

を使ってます。

どちらも MATLAB のコード・実行結果がそのまま反映されるので、コードとドキュメンテーションの管理が楽です。

見せる相手によってはコードを隠して出力結果だけを表示するなどの設定にして PDF に出力+メール添付が定番です。

これらを知る前はワードに1つ1つコピペしてました。


他にもパワポに図をまとめることが求められるケースありますが、コピペでやろうとするととにかく無駄が多い。

プログラムを少し編集したら図も少し変わって、そのたびにコピペコピペ。。

しまいにはどのバージョンのコードから出てきた図なのかわからないといった事態に。。

とういうことで、パワポ作成を自動化する方法を調べてみました。


ちなみに Python では python-pptx というライブラリを使用して自動生成ができるようで、

以下の記事が MATLAB での方法をまとめてみようというきっかけになりました。

参照:Qiita: Pythonを使ったレポートの自動作成【PowerPoint】【python-pptx】


いろんな選択肢があります

検索してみると既にいくつか COM サーバーを使った泥臭い方法が紹介されています。

他にもユーザー作成の関数も見つかりました。こちら中身をよく理解できていませんが、COMサーバーは使っていないみたいです。

組織内共通の方法で本格的にやるのであれば MATLAB Report Generator という選択肢もありますが・・・。


Yet another example

特に新しい手法を紹介するわけじゃないんですが 例が多いに越したことはないと思いまして

ここでは泥臭く COM サーバーを使う方法で、各機械学習アルゴリズムの精度をパワポに纏めてみようと思います。


成果物 ExamplePresentation.pptx

パワポが張り付けられないので、キャプチャ画像でご勘弁を。

同じ要領でループを回せば、何百ケース分でもパワポに結果をまとめることができます。

Capture1.PNG


コード

表示位置の調整が最初面倒です。h.Visible = 1 として出来栄えを確認しながら調整するのが吉かと。


samplescript.m

%% PowerPointを開く

h = actxserver('PowerPoint.Application');
% PowerPointのウィンドウ表示(出来栄えの確認用)
h.Visible = 1;

%% プレゼンテーションを追加
h.Presentation.invoke;
Presentation = h.Presentation.Add;

%% カスタムレイアウト読み込み
titleSlide = Presentation.SlideMaster.CustomLayouts.Item(1);
blankSlide = Presentation.SlideMaster.CustomLayouts.Item(2);

%% タイトルページ追加
Slide1 = Presentation.Slides.AddSlide(1,titleSlide);
Slide1.Shapes.Title.TextFrame.TextRange.Text = 'MATLAB -> PowerPoint 自動化';

%% 2ページ目以降、イメージを描画
methods = ["fitcnb","fitcsvm","fitctree","fitcknn"];
methodnames = ["ナイーブベイズ",...
"サポートベクターマシン",...
"決定木",...
"k近傍法"];
for ii=1:4
newslide = Presentation.Slides.AddSlide(ii+1,blankSlide);
newslide.Shapes.Title.TextFrame.TextRange.Text = [methodnames(ii) + "-" + methods(ii)];

[loss, figH1, figH2] = checkClassificationPerformance(methods(ii));

% 1つ目のFigureをコピペ
print(figH1,'-dmeta','-r150')

Image1 = newslide.Shapes.Paste;
set(Image1, 'Left', 50) % 位置、大きさセット
set(Image1, 'Top', 120)
set(Image1, 'Width', 300)
set(Image1, 'Height',300)

% 2つ目のFigureをコピペ
print(figH2,'-dmeta','-r150')

Image2 = newslide.Shapes.Paste;
set(Image2, 'Left',450)
set(Image2, 'Top', 120)
set(Image2, 'Width', 300)
set(Image2, 'Height', 300)

% Text 挿入 (Left, Top, Width, Height は TextBox の位置とサイズ)
tmp = newslide.Shapes.AddTextbox('msoTextOrientationHorizontal',200,450,400,70);
tmp.TextFrame.TextRange.Text = '混同行列';
tmp = newslide.Shapes.AddTextbox('msoTextOrientationHorizontal',570,450,400,70);
tmp.TextFrame.TextRange.Text = '散布図(xは不正解)';
tmp = newslide.Shapes.AddTextbox('msoTextOrientationHorizontal',600,70,400,70);
tmp.TextFrame.TextRange.Text = "Loss: " + string(loss);
end

%% プレゼンテーションを保存
Presentation.SaveAs([pwd '\ExamplePresentation.pptx']);

%% PowerPointを閉じる
h.Quit;
h.delete;


上記内で使っているのが以下、method に応じた(簡易的に4種類のみ)アルゴリズムを

に対して5分割交差検定による混同行列、結果プロット、損失を返す関数


checkClassificationPerformance.m

function [loss, figH1, figH2] = checkClassificationPerformance(method)

% データ読み込み
s = load('fisheriris.mat');
X = s.meas(:,3:4);
Y = categorical(s.species);

switch method
case 'fitcnb' % ナイーブベイズ
Mdl = fitcnb(X,Y,...
'ClassNames',{'setosa','versicolor','virginica'});
case 'fitcsvm' % サポートベクタマシン
Mdl = fitcecoc(X,Y,...
'ClassNames',{'setosa','versicolor','virginica'});
case 'fitctree' % 決定木
Mdl = fitctree(X,Y,...
'ClassNames',{'setosa','versicolor','virginica'});
case 'fitcknn' % k近傍法
Mdl = fitcknn(X,Y,...
'ClassNames',{'setosa','versicolor','virginica'});
otherwise
disp('not recognized');
end

% 交差検証済み (分割された) モデルを作成
CVMdl = crossval(Mdl,'KFold',5);
loss = kfoldLoss(CVMdl);
predictedLabels = kfoldPredict(CVMdl);

figH1 = figure(1); % 混同行列
cm = confusionchart(Y,categorical(predictedLabels));
cm.FontSize = 15;

figH2 = figure(2); % 散布図プロット
idx = Y == predictedLabels;
gscatter(X(~idx,1),X(~idx,2),Y(~idx),'rgb','x',8,'on');
hold on
gscatter(X(idx,1),X(idx,2),Y(idx),'rgb','.',18,'on');
hold off
title('Fisher''s Iris Data','FontSize',15);
xlabel('Petal Length (cm)','FontSize',13);
ylabel('Petal Width (cm)','FontSize',13);

grid on