ぬめぬめ動く棒グラフ Bar Chart Race を描いてみよう: 実装編

# はじめに


各データの時系列変化を順位とともに表現するプロット、Bar Chart Race。前回は `barh` 関数を使って Bar Char Race が実現できそうか確認してみました。`BarWidth` プロパティの使い方が肝でしたね。

詳細：[ぬめぬめ動く棒グラフ Bar Chart Race を描いてみよう: 準備編](https://qiita.com/eigs/items/62fbc0b6bdc5e7094abf)

![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/90cecb93-2b82-5f67-b545-374433e00274.gif)


今回は実装編として実際にこの動画（GIF) を作成するところまでを解説します。

既に関数を [minoue-xx/BarChartRaceAnimation](https://github.com/minoue-xx/BarChartRaceAnimation) で公開していますので、使用例とポイントだけに絞ってご紹介します。
  
### 実行環境


R2019b で作りました。下で解説する Arguments に関わる部分を取り除けば R2017b でも動くと思います。試してないです。



# 使用例：各都道府県の推定人口推移（大正9年～平成12年）


まず [e-Stat](https://www.e-stat.go.jp/stat-search/files?page=1&layout=datalist&toukei=00200524&tstat=000000090001&cycle=0&tclass1=000000090004&tclass2=000000090005&stat_infid=000000090265) のページから該当データをダウンロードして、`05k5-5.xlsx` というファイルが本スクリプトと同じフォルダにダウンロードされたと仮定します。


## Step 1: データ読み込み


ザクっとインポートツールから読み込むスクリプトを作りました。


![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/db2ad97b-046c-c823-bc89-3f0c9f16869f.png)


「現在のフォルダ」に表示される `05k5-5.xlsx` をダブルクリックして、読み込み範囲を指定して「スクリプトの生成」をクリック。省エネ。出力タイプは：cell 配列。


生成されたスクリプトを実行すると変数 `k55` として読み込まれるはず。（importData.m は[こちら](https://github.com/minoue-xx/BarChartRaceAnimation/tree/master/example/RegionalPopulationJapan)にもおいています。）


```matlab
importData
```
## Step 2: データ整理


時系列データは `timetable` 型でまとめると便利。注：沖縄はデータが大きく欠けているところがあるので除いています。


```matlab
% k55 から必要な部分を取り出します。
years = [k55{1,3:end}]'; % 年数
names = string(k55(4:end-1,1)); % 都道府県の名前
data = cell2mat(k55(4:end-1,3:end)); % 人口（数値部分）

% 年データを datetime 型に変更
timeStamp = datetime(years,1,1);
timeStamp.Format = 'yyyy'; % 表示は yyyy年

% timetable 型のデータ作成
T = array2timetable(data','RowTimes',timeStamp);
T.Properties.VariableNames = names; % 変数名指定
```


こんな感じのデータになります。


```matlab
head(T)
```

![Capture.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/515e04f8-a5f0-7692-e4d6-d5217678c82d.png)


## Step 3: プロット


ここまでくれば


```matlab
barChartRace(T);
```


でOK!




ただ、全データプロットすると何が何だか分かりませんので、オプションをいくつか使ってみます。


```matlab
barChartRace(T,'NumDisplay',6,'NumInterp',4,...
    'Position',[500 60 470 370],'ColorGroups',repmat("g",length(names),1),...
    'XlabelName',"上位6都道府県の人口（千人）",...
    'GenerateGIF',true,"Outputfilename",'top5.gif');
```


と実行すると、冒頭の GIF が出来上がり。


## 各種オプション


詳細は GitHub の [README.md](https://github.com/minoue-xx/BarChartRaceAnimation/blob/master/README.md) か


```matlab
help barChartRace
```


で見てもらいたいんですが、例えば



   -  `'NumDisplay'`: 上位何位まで表示するか（default: 全部） 
   -  `'FontSize'`: プロットで使うのフォントサイズ（default: 15） 
   -  `'GenerateGIF'`: GIF を出力するかどうか（defaiut: false） 
   -  `'LabelNames'`: `timetable` 型または `table` 型変数の場合は変数の名前をそのまま使いますが、このオプションで指定可 
   -  `'Position'`: 作成される Figure の大きさ 



といったものを入れました。


### Arguments


R2019b から使える Arguments (詳細：[Argument Validation Functions](https://jp.mathworks.com/help/matlab/matlab_prog/argument-validation-functions.html?s_eid=PSM_29435)) を使って、こんな感じでオプションの一覧を表示されるようにしてるので、オプション選択も多少楽になっているでしょうか？



![image_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/59dba653-f5ba-ea20-676a-e579ea391271.png)


`barChartRace.m` の冒頭にある 


```matlab
arguments
    inputs {mustBeNumericTableTimetable(inputs)}
    options.Time (:,1) {mustBeTimeInput(options.Time,inputs)} = setDefaultTime(inputs)
    options.LabelNames {mustBeVariableLabels(options.LabelNames,inputs)} = setDefaultLabels(inputs)
    options.ColorGroups {mustBeVariableLabels(options.ColorGroups,inputs)} = setDefaultLabels(inputs)
    (中略)
```


部分が対応します。網羅するのは結構大変でしたが、ちゃんと意図した変数が入力されているかどうかのチェックもするようにしてます。例えば・・


```matlab
barChartRace('エラーが出るよ')
```
```
エラー: barChartRace
位置 1 の入力引数が無効です。 Input data must be either timetable, table, or numeric array (double)
```


1つ目の入力は timetable 型か time 型か double の配列でお願いしますとか、


```matlab
barChartRace(T,'LabelNames',"これも良くないよ")
```
```
エラー: barChartRace
名前と値の引数 'LabelNames' が無効です。 The size must be same as the number of variables of inputs (46)
```


`'LabelName'` というオプションは input と同じ変数の数 (46個) 用意してねとか、エラーの原因を表示させる感じ。自分で使う関数なら必要ないかもしれませんが、他の人にも使ってもらうならあったほうが便利かなと思い入れました。


  
# 描画のポイント１：データの内挿


各棒が順位入れ替えをする状況を表現するには順位データの内挿が必要です。`barChartBar.m` の中では


```matlab
%% Interpolation: Generate nInterp data point in between
time2plot = linspace(time(1),time(end),length(time)*NumInterp);
ranking2plot = interp1(time,rankings,time2plot,Method);
data2plot = interp1(time,data,time2plot,Method);
```


こんな感じ。`interp1` 関数使っています。




ここの `Method` で内挿方法を指定するんですが、シンプルに線形内挿すると上のような動画。ここで `spline` 補間なんかしてしまうと、棒の位置がオーバーシュートするダイナミックさが加わります。いりませんね（笑）


![top5Spline.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/ecfed785-abfa-4a25-5501-87a9f40ec7ee.gif)


コマンドはこんな感じ：`'Method'` に `'spline'` を指定します。


```matlab
barChartRace(T,'NumDisplay',6,'NumInterp',4,...
    'Position',[500 60 470 370],'ColorGroups',repmat("g",length(names),1),...
    'XlabelName',"上位6都道府県の人口（千人）",'Method','spline',...
    'GenerateGIF',true,"Outputfilename",'top5Spline.gif');
```
# 描画のポイント２：CData


前回も nonlinopt さんにご指摘頂いた通り各棒の色は `CData` プロパティで指定できます。`'FaceColor'` を `'flat'` に設定することにも注意。




ただ曲者なのは CData 内の色の順番は各棒の位置と連動してしまっていること。例えば `CData` をそのままにしておくと、もともとの順位（1 位：赤と 2 位：青）が逆転すると、1 位が青で 2 位が赤になっちゃいます。




例えば


```matlab
hb = barh([1,2],'FaceColor','flat');
hb.CData = [0,0,1
    1,0,0];
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/220f8348-34d9-71ab-7bff-61a74432a8ec.png" alt="attach:cat" title="attach:cat" width=500px>

もともとの設定では上が赤、下が青ですが、その位置を変更すると・・


```matlab
hb.XData = [2,1];
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/bf23d27b-022e-4088-a82e-c84084555125.png" alt="attach:cat" title="attach:cat" width=500px>

色の順番がそのまま。まぁ、、当然と言えば当然な気もしますが。ですので、ひと手間加えて `CData` 内の順番も合わせて変更する必要があります。`barChartBar.m` の中では


```matlab
    % Set YTick position by ranking
    % Set YTickLabel with variable names
    [ytickpos,idx] = sort(ranking,'ascend');
    handle_axes.YTick = ytickpos;
    handle_axes.YTickLabel = LabelNames(idx);
    
    % Fix CData
    handle_bar.CData = colorScheme(idx,:);
```


といった感じで `YTick` の順位変更に合わせて `CData` も改めて変更しています。


# 描画のポイント３：棒の横の数値表示


これは `text` オブジェクトを使っています。こちらの記事（[MATLABのプロットでアノテーションをつける](https://qiita.com/Monzo_N/items/c68f52e88fd532671a19)）のコメントでもやり取りがありますが、`text` オブジェクトのいいところは、プロットしているデータと同じ座標系で位置指定ができること。Figure 内の相対的位置とか、Axes 内の相対的位置を計算する必要がないのは、直感的に使いやすいです。`barChartRace.m` 内ではこんな感じ。


```matlab
% Add value string next to the bar
if IsInteger
    displayText = string(round(value2plot(idx)));
else
    displayText = string(value2plot(idx));
end
xTextPosition = value2plot(idx) + maxValue*0.05;
yTextPosition = ytickpos;

% NumDisplay values are used
xTextPosition = xTextPosition(1:NumDisplay);
yTextPosition = yTextPosition(1:NumDisplay);
displayText = displayText(1:NumDisplay);
handle_text = text(xTextPosition,yTextPosition,displayText,'FontSize',options.FontSize);
```


最大値の 5% だけ各数値から右にずらした位置に数値を表示しています。もともとのデータが整数の場合は整数に丸めて表示するオプションいれています。内挿してしまっているのでそのまま表示すると、人口が小数点表示になっちゃうから・・（多少苦しい）


# おまけのポイント


表示されたプロットみると "Visualized by MATLAB" とアピールがひどい部分がありますね（笑）


これは `barCharRace.m` 内では以下の一行で書いているんですが、どうぞ皆さん好きな文字に変えて使ってくださいませ。


```matlab
% Display created by MATLAB message
text(0.99,0.02,"Visualized by MATLAB",'HorizontalAlignment','right',...
    'Units','normalized','FontSize', 10,'Color',0.5*[1,1,1]);
```

# 手抜き方法

試しにパッと作ってみるだけなら、こんな方法もあります。

1. Excel からデータをコピー
2. MATLAB にコピペ
3. `barChartRace(unnamed')`

![Untitled Project.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/4e8314f4-afa2-49ca-cc24-a3f1499c02f6.gif)



# まとめ


以上、簡単ではありますが `barChartRace` 関数内でのポイント紹介でした。


人口推移、人気プログラミング言語ランキング、各企業の時価総額ランキング、各国の出生率ランキング、各大学偏差値ランキングなどなどデータさえあれば面白そうなプロットができそうな気がします。

良ければ試してくださいね～。`barChartRace` 関数で困ったことあれば遠慮なくコメントください。