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


はじめに

最近よくみるこんなプロット:各データの時系列変化を順位とともに表現するプロットです。時間とともに順位が入れ替わるので Bar Chart Race とか呼ばれている(?)ようで、検索するといろんなデータの可視化がみられます。

これを MATLAB で書けないのか、、という声が答えてきた気がしましたので、やってみました。

MATLAB コードはこちら GitHub: BarChartRaceAnimation

今回は準備編として描画に必要な要素がそろっているかの確認をします。 barh 関数の機能を確認してみましょう。

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

横棒グラフのプロット

まずは棒グラフ barh を書いてみます。

clear; close all
x = 1:5;
y = (1:5)/10;
handle_bar = barh(x,y);

簡単ですね。x が縦の位置、y が棒の長さを決めます。

barh がややこしいのは x が縦軸上での位置、yが対応する棒の長さである点。直感的に xy が逆転しているように感じる点。

棒グラフの棒の位置を指定(整数)

プロパティを見てみます。

handle_bar = 
  Bar のプロパティ:

    BarLayout: 'grouped'
     BarWidth: 0.8000
    FaceColor: [0 0.4470 0.7410]
    EdgeColor: [0 0 0]
    BaseValue: 0
        XData: [1 2 3 4 5]
        YData: [0.1000 0.2000 0.3000 0.4000 0.5000]

棒グラフの位置(縦方向)は XData プロパティで変更できそうです。例えば 2 と 3 を入れ替えてみます。

handle_bar.XData = [1,3,2,4,5];

3 と 2 の位置が逆転しました。

棒グラフの棒の位置を指定(小数点)

小数点(不均等な位置指定)もできれば、順位が入れ替わる遷移を表現できそうな気がします。

2 を 2.8 に 3 を 2.2 にして、すれ違った後を表現してみます。

handle_bar.XData = [1,2.8,2.2,4,5];

ちょっと違う

位置が変わったのはいいが何かがおかしい。そう、棒の幅が細い!

これは barh関数が気を利かせてくれて、それぞれの棒が重ならないように描画するためです。ま、気持ちはわからんでもない。

ここを打開するには BarWidth プロパティです。

既定値は 0.8 であり、MATLAB で少し間隔を空けて各バーが表示されます。このプロパティを 1 に設定すると、バー同士が隙間なく表示されます。参照:Bar のプロパティ

handle_bar.BarWidth 
= 0.8000

BarWidth プロパティ

ではこれをいくつにすればいいのか。間隔がちょうど1だった時には 0.8 でいい感じの表示でした。では直感的に、、データの幅に反比例するように BarWidth の値を大きくすればいいのでは?

やってみます。現時点では

tmp = handle_bar.XData
tmp = 1x5    
    1.0000    2.8000    2.2000    4.0000    5.0000

という位置関係なので、最も近い位置にある棒同士の距離を使って計算してみましょう。

scaleWidth = min(diff(sort(tmp)))
scaleWidth = 0.6000

一度 sort しているのは、逆転してるからです。この値を使って BarWidth の値を既定値の 0.8 のような見栄えになるように変えてみます。

handle_bar.BarWidth = 0.8/scaleWidth;

おぉ、できたっぽい。

これで、y 軸の位置をランキング(順位)に該当するように XData プロパティを変更すればいいですね。ぬめぬめ遷移させるには、ランキングの入れ替え部分を内挿してちょっとずつ順位が入れ替わるようにすれば、ぬめぬめ遷移が表現できそうな気がしてきました!

軸などの表示

y 軸に少数点が表れているのが気になりますので、この辺綺麗にしてみましょう。

使うのは Axes オブジェクトの YTickYTickLabel です。それぞれの棒のラベルは

names = ["A","B","C","D","E"];

とします。まずはラベルを表示する位置 YTick を指定します。ランキング(位置)に相当する XData プロパティ値をそのまま入れてみます。

handle_axes = gca;
handle_axes.YTick = handle_bar.XData;
エラー:値は単精度型または倍精度型のベクトルで、値が増加しなければなりません

YTick は単調増加じゃないと

エラーがでみました。YTick には単調増加した値を与えないとエラーがでます。ソートしておきましょう。(小さい順)

[tmp,idx] = sort(handle_bar.XData,'ascend');

XData の並べ替えに応じて棒の名前である names の順番も変えておく必要があるので、第二出力引数の idx も使います。

handle_axes.YTick = tmp;
handle_axes.YTickLabel = names(idx);

B と C が入れ替わっている感じがでてますね!

残念な点:色の問題

barh の残念な点なんですが、1 つの bar オブジェクトに対して色は1色しか指定できません。

追記 (2020/1/22)
nonlinopt さんにご指摘 頂きまして、CData プロパティを使うことで棒は個別に色分けできることが分かりました。nonlinopt さんありがとうございました。


b = bar(1:10,'FaceColor','flat');
b.CData(2,:) = [0 0.8 0.8];

こんな感じ。参照:Bar のプロパティ

また MATLAB の Release Note で再確認したところ CData プロパティが使えるようになったのは R2017b からなので、R2017a 以前のバージョンを使っている方は以下の方法を使ってもいいかも。
追記ここまで

なので、色の種類だけ barh を実行して異なる bar オブジェクトを作ればればいいですね。

figure
x = 1:5;
y1 = [1:4,0]/10;
y2 = [0,0,0,0,5]/10;
handle_bar1 = barh(x,y1);
hold on
handle_bar2 = barh(x,y2);
hold off

まとめ

準備編はここまで!色の数だけ bar オブジェクトを作るといろいろ大変そうですが、むしろいろいろ自由にカスタマイズできていいかもしれない。

できそうな気がしてきました。次はサンプルデータを使ってランキングが推移するアニメーションを書いてみます。

やることは以下の2点

  • 複数の時系列データの各時点での値の順位付け
  • 順位が入れ替わる遷移部分を表示するためのデータの内挿

これができれば、あとは bar オブジェクトに値を入れていくだけです!(多分)