【MATLAB】マジお世話になったを「定量化」「可視化」する技術

# はじめに
突然ですが私、数年間お世話になった部署から異動することになりました。
この部署では文字通り数えきれない方々にお世話になった訳ですが、感謝の心を
まあだいたいの事象では忘れる
決して忘れる事のない、私のような人間としては「<b>どうやってその感謝を伝えたらよいか</b>」を悩んでしまったりする訳です。困ったなぁ！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/f9f59337-1e2e-fcc5-7dba-7c1b90169c04.png)


全員に感謝のメッセージを送る、なんていうのも勿論素敵な考えですが、<b>どのぐらいのレベルでお世話になった人までを対象としたら良いのか</b>とか、<b>どれぐらい感謝すれば良いのか</b>というのは地味に悩みの種ですよね。挨拶程度の言葉を交わした、という方まで含めようとは思いませんが、じゃあ業務を大なり小なり手伝ってくれた相手だったらどうする？　とかその線引き、すなわち<b>判断</b>が難しいです。

この難しい判断をどうやってすれば良いのか。エンジニアとしての答えは簡単、そう「<b>定量化</b>」して「<b>可視化</b>」すれば良いのです。

「<b>定量化</b>」にはその材料となるデータが必要になりますが、近年はメーラー等の業務用コミュニケーションツールからデータを引っ張ってくることが割と容易に出来るため、アイディア次第で色々な定量化が出来ると思います。

「<b>可視化</b>」は、我らのMATLAB兄さんに任せればあとはなんとかしてくれるはず！

…やったー！なんとかなったー！メカ設計Cさんマジありがとー！！(※フィクションです)
![top10.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/828a2dbd-c9b3-190d-140f-fce4d6fb1acd.gif)
(言わずもがな @eigs さんの [BarChartRaceAnimation](https://github.com/minoue-xx/BarChartRaceAnimation) を使用しています)


# 1.「マジお世話になった」を定量化する
## 1.1 定量化の方向性
データの出所としては過去記事 [【MATLAB】Outlookから予定表を取得する](https://qiita.com/motorcontrolman/items/03ea4d2a978d626f4337) 同様、Outlookを用いるものとします。(Outlookに限らず、業務用コミュニケーションツールであれば何でも構いません。APIが叩ければ…)

Outlookの受信トレイの内容を取得出来れば、なんか面白そうなことが出来そうな感じがするのでその方向性で行きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/a44edb05-8d90-6f3b-3852-8e32e3e06d9f.png)


## 1.2 outlookの受信トレイからデータ取得する
[【MATLAB】Outlookから予定表を取得する](https://qiita.com/motorcontrolman/items/03ea4d2a978d626f4337) のリピートになるためざっくり解説していきます。

```matlab
outlook = actxserver('Outlook.Application');
mapi = outlook.GetNamespace('mapi');

myInbox = mapi.GetDefaultFolder(6);%.GetExplorerは不要
StartDate_MIN = '2016/02/05 00:00';
StartDate_MAX = '2020/02/06 23:00';

%[SentOn]は送信時間
filter = {['[SentOn] >= ''',StartDate_MIN,''' AND [SentOn] <= ''', StartDate_MAX, '''']};

myInboxItems = myInbox.Items;
myInboxItems.Sort('[SentOn]');
myInboxItems_Restrict = myInboxItems.Restrict(filter{1});
Cnt = myInboxItems_Restrict.Count;
```
[Baccho Log [WSH]Outlookの操作](https://baccholog.com/archives/128) より、受信トレイは`GetDefaultFolder(6)`で取ります。今回の場合、ある区間においてお世話になった人だけを抽出したいので送信時間`SentOn`でフィルタをかけています。
上記にてAPIへの接続がうまく行っていれば、`Cnt`にフィルタされたメールの件数が帰ってきます。
(うまく行かない場合は受信トレイのフォルダ構造が原因の場合あり、色々試してみてください)

`Cnt`にゼロ以外の値が帰ってきていればapi接続はOKと思われるので続いて各メールの詳細データを取得しに行きます。

```matlab
%% 各メールの詳細データ取得
myInboxTable = table;

% メール送信者名および送信時間を取得
for i=1:Cnt
    try
        SenderName = myInboxItems_Restrict.Item(i).SenderName;
    catch
        SenderName = [];
    end
    
    if ~isempty(SenderName)
        SenderName = string(SenderName);
        myInboxTable.SenderName(i) = SenderName;
        SentOn = string(myInboxItems_Restrict.Item(i).SentOn);
        Time = datetime(SentOn,'InputFormat','yyyy/MM/dd HH:mm:ss');
        myInboxTable.Time(i) = Time;
    end
end

```

取り込み可能な詳細データについては[【備忘メモ】VBA で Outlook の受信メールを取得する](https://qiita.com/t-yama-3/items/37545fa9a00c140fb657) を参考。今回は送信者名`SenderName`を使用しています。余談ですが、宛先は`To`ではなく`TO`でないと取得できませんでした、MATLAB特有？

コードの中で `try` `catch` を使用していますが、送信者名の取得エラーとなるメールが存在するため対策として入れています。簡単ですが効果絶大。 

なお、上記コマンドを実施した際にoutlook側に警告が出る場合があります。アクセス許可時間10分とかで許可しておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/8af7e0bb-9ee6-a04a-20ed-bb0abd090245.png)

上記コードを実行した結果、tableにデータが格納されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/2b338bfe-fe94-04a0-97f5-28477fa39de0.png)

## 1.3 timetableを使う
時系列データを取り扱う場合、table型ではなくtimetable型が便利です。なぜtimetable型が便利か、は色々理由がありますがMATLABの<b>神</b>関数 [retime](https://jp.mathworks.com/help/matlab/ref/timetable.retime.html) が使えるから、というだけで十分だと私は思っています。(乱暴)


1.2 にて作成したtable `myInboxTable` を`table2timetable`関数にてtimetableに変換します。今回の場合、「ある区間において特定のユーザーから受け取ったメールの数」をカウントしたいので、timetableへの変換ついでに`count`プロパティに１を入れています。

```matlab
myInboxTimetable = table2timetable(myInboxTable,'RowTimes','Time');
myInboxTimetable.count(:) = 1;%集計用 全データに対し1を設定
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/64a72254-d9fc-a246-4597-c833a5247739.png)

# 2. 可視化する

## 2.1 最も簡単な可視化(最終ランキング)

1.3にて作成したtimetalbe `myInboxTimetable`を加工して最終ランキングを作成します。ベタな手法ですが、`unique`関数を使用することで差出人ごとのメール数の集計を行います。

```matlab

%% メールを多く頂いた差出人順に整理する
uniqueSenderName = table;
uniqueSenderName.Name = unique(myInboxTimetable.SenderName);
for i = 1:height(uniqueSenderName)
    uniqueSenderName.cnt(i) =  sum( matches(myInboxTimetable.SenderName, uniqueSenderName.Name(i)) );
end

uniqueSenderName = sortrows(uniqueSenderName,"cnt",'descend');

```

上記コードを実行することで、`Name`プロパティに差出人名、`cnt`プロパティにメール受信数の合計が格納されます。
なお、下記
面白さを優先して
匿名性を確保するため差出人名は適宜加工を加えるものとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/c38ec331-c015-deb3-6c8c-74b6483b5f51.png)



## 2.2 「時間」のエッセンスを加えた可視化
### 2.2.1 単位時間ごとのデータ整理の実施

2.1の可視化結果は野球で言うところの「どのチームが優勝したか」を示したものに過ぎないため、「特定の期間に置いては滅茶苦茶お世話になった」等の、<b>記録には残らないが記憶には残る</b>お世話になり方をした事を可視化することが出来ません。これを実現するにはどうしたら良いかというと、<b>差出人ごとのメール数の集計を、ある単位時間ごとに実施する</b>必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/770dc826-b07d-55a9-c612-2199e9ac5a0e.png)


上記のようなデータ整理をMATLAB関数一発では実施できないため、まずは差出人ごとの集計を実施、その上で単位時間ごとの集計を改めて行うものとします。

```matlab
%% 週ごとや月ごとなど、単位時間ごとのメール数を算出する
numDataAll = timetable;
for i = 1:10 %最終ランキング上位10位までに限定して実施
    % まずは差出人ごとに集計
    myInboxTimetable_tmp = myInboxTimetable(matches(myInboxTimetable.SenderName, uniqueSenderName.Name(i)),:);

    % 単位時間ごとに集計
    vt = vartype('numeric');
    numData = retime(myInboxTimetable_tmp(:,vt),'monthly','sum');
    numData.Properties.VariableNames(1) = uniqueSenderName.JName(i);
    numDataAll = synchronize(numDataAll,numData);
end
% synchronizeした結果、データが無かった月には0を入れる
numDataAll = fillmissing(numDataAll, 'constant',0);
```
出た！神関数 [retime](https://jp.mathworks.com/help/matlab/ref/timetable.retime.html)！　ということで、この関数を使うことで任意の単位時間ごとの集計が可能となっています。

地味に(いや派手に)便利なのが `synchronize` 関数で、例えば２月はAさんのデータがあるがBさんのデータが無い…という場合でも、Aさん用に集計したデータとBさん用に集計したデータを１つのデータとして取りまとめる事が可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/fdb4e486-bc04-0e34-d246-676edef49bdc.png)

ただし上記のように、データが無い箇所は`synchronize`後にmissingとなるため、上記コードの一番下で実施しているように `fillmissing` 関数を用いてデータを再整理する必要があります。ここでは単にメールを一件も貰っていないので0を代入しています。

### 2.2.1 可視化およびその所感

上記を実施して得られた整理後のデータ `numDataAll` をシンプルに `plot`関数で可視化を行うと下記になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/4b53ea6b-8909-46e4-1b53-bd497ad4c0f7.png)

<b>なんということでしょう。</b>
時間の概念を導入した可視化を行うことで、マジお世話になった記録だけではなく記憶がありありと思い浮かぶようになったではないですか…！

異動直後から垂直立ち上げで問い合わせを行って頂いた顧客Bさん。
全期間を通じて多大なる支援を頂いたメカ設計Cさん。
自分より後に異動してきて大変助けて頂いた同僚Aさん。

そして余り思い出したくない、17年10月にあったインシデント…

（全てフィクションです。ほんとうだよ）

頭の中を、動揺 [思い出のアルバム](https://youtu.be/DP68_ZaB5BE)　が駆け巡ります。
段々泣けてきました。あと胃も痛くなってきました。

この記事を書いた当初は「メールデータを可視化したら何か面白いかも」ぐらいの適当な感じでこの記事を書き始めた訳ですが、可視化した結果として想定外にエモい事になって個人的にも驚きました。データって凄いですね~。

### 2.2.2 最終ランキングに至るまでの経緯を可視化する(BarChartRaceを使用)

上記では単位時間ごとのデータ可視化を行いましたが、今度は最終ランキングに至るまでの経緯を可視化してみましょう。ここでは@eigs さんの [BarChartRaceAnimation](https://github.com/minoue-xx/BarChartRaceAnimation) を使用させて頂きます。
(qiita記事は [こちら](https://qiita.com/eigs/items/c1675e6dc6fd497e714a)。一度使ってみたかったんだよね~)

2.2.1 データ整理の実施　では各月ごとのメール件数を集計した訳ですが、最終ランキングに至るまでの経緯を可視化するには<b>各月ごとのメール件数を累計する</b>必要があります。
これにはMATLABのスーパー便利関数である [cumsum](https://jp.mathworks.com/help/matlab/ref/cumsum.html) を使用します。
timetableの各要素に対し cumsumを実施するため、`varfun` を用いる点がミソとなります。

```matlab
numDataAll_cumsum = varfun(@cumsum, numDataAll);
% データ名にcumsum_が追加されて邪魔なので除去する
tmp = erase( string(numDataAll_cumsum.Properties.VariableNames), "cumsum_");
numDataAll_cumsum.Properties.VariableNames = tmp;
```

得られた`numDataAll_cumsum` を　`barChartRace` 関数に突っ込むと下記が得られます。

![top10.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302506/828a2dbd-c9b3-190d-140f-fce4d6fb1acd.gif)

同僚Aさんの颯爽登場ぶりが目立つ感じがしますね。
メカ設計Cさんと顧客Bさんは最初から最後までデッドヒートしていますね。

ハードに何か問題のあるプロジェクトだったのでしょうか。

(フィクションです)

## 終わりに
上記は色々と茶化して書いていますが、メール送受信件数の分析を行うことで人間関係の定量的評価が可能となるだけでなく、業務負荷の定量化やプロジェクトにおける問題個所の抽出等にも活用できる可能性があるな…と真面目に思ったりもしています。(既にやられているかもですが)

今回は単純に送受信数だけの分析ですが、メール内容のテキストマイニングまで踏み込んでいくと更に面白い世界が広がっていそうです。