はじめに
突然ですが私、数年間お世話になった部署から異動することになりました。
この部署では文字通り数えきれない方々にお世話になった訳ですが、感謝の心をまあだいたいの事象では忘れる決して忘れる事のない、私のような人間としては「どうやってその感謝を伝えたらよいか」を悩んでしまったりする訳です。困ったなぁ!
全員に感謝のメッセージを送る、なんていうのも勿論素敵な考えですが、どのぐらいのレベルでお世話になった人までを対象としたら良いのかとか、どれぐらい感謝すれば良いのかというのは地味に悩みの種ですよね。挨拶程度の言葉を交わした、という方まで含めようとは思いませんが、じゃあ業務を大なり小なり手伝ってくれた相手だったらどうする? とかその線引き、すなわち判断が難しいです。
この難しい判断をどうやってすれば良いのか。エンジニアとしての答えは簡単、そう「定量化」して「可視化」すれば良いのです。
「定量化」にはその材料となるデータが必要になりますが、近年はメーラー等の業務用コミュニケーションツールからデータを引っ張ってくることが割と容易に出来るため、アイディア次第で色々な定量化が出来ると思います。
「可視化」は、我らのMATLAB兄さんに任せればあとはなんとかしてくれるはず!
…やったー!なんとかなったー!メカ設計Cさんマジありがとー!!(※フィクションです)
(言わずもがな @eigs さんの BarChartRaceAnimation を使用しています)
1.「マジお世話になった」を定量化する
1.1 定量化の方向性
データの出所としては過去記事 【MATLAB】Outlookから予定表を取得する 同様、Outlookを用いるものとします。(Outlookに限らず、業務用コミュニケーションツールであれば何でも構いません。APIが叩ければ…)
Outlookの受信トレイの内容を取得出来れば、なんか面白そうなことが出来そうな感じがするのでその方向性で行きます。
1.2 outlookの受信トレイからデータ取得する
【MATLAB】Outlookから予定表を取得する のリピートになるためざっくり解説していきます。
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の操作 より、受信トレイはGetDefaultFolder(6)
で取ります。今回の場合、ある区間においてお世話になった人だけを抽出したいので送信時間SentOn
でフィルタをかけています。
上記にてAPIへの接続がうまく行っていれば、Cnt
にフィルタされたメールの件数が帰ってきます。
(うまく行かない場合は受信トレイのフォルダ構造が原因の場合あり、色々試してみてください)
Cnt
にゼロ以外の値が帰ってきていればapi接続はOKと思われるので続いて各メールの詳細データを取得しに行きます。
%% 各メールの詳細データ取得
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 の受信メールを取得する を参考。今回は送信者名SenderName
を使用しています。余談ですが、宛先はTo
ではなくTO
でないと取得できませんでした、MATLAB特有?
コードの中で try
catch
を使用していますが、送信者名の取得エラーとなるメールが存在するため対策として入れています。簡単ですが効果絶大。
なお、上記コマンドを実施した際にoutlook側に警告が出る場合があります。アクセス許可時間10分とかで許可しておきましょう。
上記コードを実行した結果、tableにデータが格納されます。
1.3 timetableを使う
時系列データを取り扱う場合、table型ではなくtimetable型が便利です。なぜtimetable型が便利か、は色々理由がありますがMATLABの神関数 retime が使えるから、というだけで十分だと私は思っています。(乱暴)
1.2 にて作成したtable myInboxTable
をtable2timetable
関数にてtimetableに変換します。今回の場合、「ある区間において特定のユーザーから受け取ったメールの数」をカウントしたいので、timetableへの変換ついでにcount
プロパティに1を入れています。
myInboxTimetable = table2timetable(myInboxTable,'RowTimes','Time');
myInboxTimetable.count(:) = 1;%集計用 全データに対し1を設定
2. 可視化する
2.1 最も簡単な可視化(最終ランキング)
1.3にて作成したtimetalbe myInboxTimetable
を加工して最終ランキングを作成します。ベタな手法ですが、unique
関数を使用することで差出人ごとのメール数の集計を行います。
%% メールを多く頂いた差出人順に整理する
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
プロパティにメール受信数の合計が格納されます。
なお、下記面白さを優先して匿名性を確保するため差出人名は適宜加工を加えるものとします。
2.2 「時間」のエッセンスを加えた可視化
2.2.1 単位時間ごとのデータ整理の実施
2.1の可視化結果は野球で言うところの「どのチームが優勝したか」を示したものに過ぎないため、「特定の期間に置いては滅茶苦茶お世話になった」等の、記録には残らないが記憶には残るお世話になり方をした事を可視化することが出来ません。これを実現するにはどうしたら良いかというと、差出人ごとのメール数の集計を、ある単位時間ごとに実施する必要があります。
上記のようなデータ整理を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! ということで、この関数を使うことで任意の単位時間ごとの集計が可能となっています。
地味に(いや派手に)便利なのが synchronize
関数で、例えば2月はAさんのデータがあるがBさんのデータが無い…という場合でも、Aさん用に集計したデータとBさん用に集計したデータを1つのデータとして取りまとめる事が可能です。
ただし上記のように、データが無い箇所はsynchronize
後にmissingとなるため、上記コードの一番下で実施しているように fillmissing
関数を用いてデータを再整理する必要があります。ここでは単にメールを一件も貰っていないので0を代入しています。
2.2.1 可視化およびその所感
上記を実施して得られた整理後のデータ numDataAll
をシンプルに plot
関数で可視化を行うと下記になります。
なんということでしょう。
時間の概念を導入した可視化を行うことで、マジお世話になった記録だけではなく記憶がありありと思い浮かぶようになったではないですか…!
異動直後から垂直立ち上げで問い合わせを行って頂いた顧客Bさん。
全期間を通じて多大なる支援を頂いたメカ設計Cさん。
自分より後に異動してきて大変助けて頂いた同僚Aさん。
そして余り思い出したくない、17年10月にあったインシデント…
(全てフィクションです。ほんとうだよ)
頭の中を、動揺 思い出のアルバム が駆け巡ります。
段々泣けてきました。あと胃も痛くなってきました。
この記事を書いた当初は「メールデータを可視化したら何か面白いかも」ぐらいの適当な感じでこの記事を書き始めた訳ですが、可視化した結果として想定外にエモい事になって個人的にも驚きました。データって凄いですね~。
2.2.2 最終ランキングに至るまでの経緯を可視化する(BarChartRaceを使用)
上記では単位時間ごとのデータ可視化を行いましたが、今度は最終ランキングに至るまでの経緯を可視化してみましょう。ここでは@eigs さんの BarChartRaceAnimation を使用させて頂きます。
(qiita記事は こちら。一度使ってみたかったんだよね~)
2.2.1 データ整理の実施 では各月ごとのメール件数を集計した訳ですが、最終ランキングに至るまでの経緯を可視化するには各月ごとのメール件数を累計する必要があります。
これにはMATLABのスーパー便利関数である cumsum を使用します。
timetableの各要素に対し cumsumを実施するため、varfun
を用いる点がミソとなります。
numDataAll_cumsum = varfun(@cumsum, numDataAll);
% データ名にcumsum_が追加されて邪魔なので除去する
tmp = erase( string(numDataAll_cumsum.Properties.VariableNames), "cumsum_");
numDataAll_cumsum.Properties.VariableNames = tmp;
得られたnumDataAll_cumsum
を barChartRace
関数に突っ込むと下記が得られます。
同僚Aさんの颯爽登場ぶりが目立つ感じがしますね。
メカ設計Cさんと顧客Bさんは最初から最後までデッドヒートしていますね。
ハードに何か問題のあるプロジェクトだったのでしょうか。
(フィクションです)
終わりに
上記は色々と茶化して書いていますが、メール送受信件数の分析を行うことで人間関係の定量的評価が可能となるだけでなく、業務負荷の定量化やプロジェクトにおける問題個所の抽出等にも活用できる可能性があるな…と真面目に思ったりもしています。(既にやられているかもですが)
今回は単純に送受信数だけの分析ですが、メール内容のテキストマイニングまで踏み込んでいくと更に面白い世界が広がっていそうです。