「棒グラフがソートされながらぬるぬる動くやつ」=bar chart raceをMATLABで作成してみた．

# 成果物：こんなgifアニメが作れます．

![outFile.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570966/488f1817-a0a3-e382-9a2f-2ada9e0f9565.gif)

こういう，「棒グラフで値がソートされながらぬるぬる動くやつ」，見たことないでしょうか？Bar chart raceとも呼ぶようです．順位変化がわかりやすいので作りたいなーとTwitterでつぶやいていたところ，反応をいただきました．以下の記事です．

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

こちらを参考として(実はほぼ同じなのですが)，時系列データを与えるとアニメーションgifファイルを作成するプログラムをMATLABで作成し，FileExchangeで公開しました．引用の作法など守っていただければばんばん使ってもらいたいです．
(QiitaもFileExchangeもこれが初投稿です)

[barChartRace.m](https://jp.mathworks.com/matlabcentral/fileexchange/74003-barchartrace)


## 基本のアイデア
基本アイデアは上記参考記事と同じなのですが，以下です．

- `h=barh(x,y)` で棒グラフを描く．`x`が縦座標，`y`が横座標であること注意．
- `x`, `y`をそれぞれ次の要素と補完(一次補完)して棒の位置と大きさを設定しなおし，再描画
    - ここで縦方向の幅の調整が必要．

## ソースコード

`main_barChartRace.m`

データの生成と設定を行い，本体(`barChartRace.m`)を呼び出します．`Opt`のメンバは全て設定しないと多分エラーになるので(本体内でエラー処理をサボっていますすいません)，このコードで代入する値を変えて対処してください．縦方向の文字列，出力ファイル名を設定します．

データ`y`は縦方向がデータ種ごと，横方向に時系列が並ぶようにしてください．1列目が0，全て正の値であることを想定しています．

```matlab:main_barChartRace.m
clear
clc
close all

%% sample data
y=rand(10,6);y(:,1)=zeros(size(y(:,1)));
y=filter(1,[1 -1], y')';

% options
Opt.xTickLabelStrs=cell(size(y,1),1);
Opt.outFileName='outFile.gif';
Opt.xLabelStr='x';
Opt.yLabelStr='y';
Opt.titleStr='Title';
Opt.faceColor=[0,0,1];%'blue';


for n1=1:size(y,1)
    Opt.xTickLabelStrs(n1)={char('A'+(n1)-1)};
end
```

設定が終われば，以下の関数を呼ぶだけで指定した名前でgifファイルができ上がります．

```matlab

barChartRace(y,Opt)
```



`barChartRace.m`

本体のMATLAB関数です．まずは縦方向のラベルと出力ファイル名を設定します．

```matlab:barChartRace.m
xTickLabelStrs=Opt.xTickLabelStrs;  % tick labels (vertical)

if isequal(Opt.outFileName(end-3:end),'.gif')
    outFileName=Opt.outFileName; % output gif name
else
    outFileName=[Opt.outFileName '.gif']; % output gif name
end
```

データをソートして，縦方向の位置を算出します．

```matlab
y=inData;
x=zeros(size(inData));
x(:,1)=(1:size(inData,1))';

for n1=2:size(inData,2)
    [val,ind]=sort(inData(:,n1),'ascend');
    for n2=1:size(ind,1)
        x(ind(n2),n1)=n2;
    end
end
```

新しい`figure`を作成し，描画します．時刻間を適当な実秒数とフレーム数に割り当てて，1フレームずつ描画して出力ファイルに追加します．各フレームでの位置は隣接する時刻間の値を線形補完しています．

```matlab

figure
hold on

grid on;
set(gca,'FontName','Arial','FontSize',10)
h=barh(x(:,1),inData(:,1),'stacked','FaceAlpha',0.5, 'EdgeColor','white');
h.FaceColor=Opt.faceColor;

xlim([0 max(max(h(1).YData)*1.2,1)])
ylim([0,size(inData,1)+1])
set(gca,'YTick',x(:,1),'YTickLabel',xTickLabelStrs(x(:,1)))
title(Opt.titleStr)
xlabel(Opt.xLabelStr)
ylabel(Opt.yLabelStr)

framesPerDataTick=24;
for k=2:size(y,2)
    
    for n1=0:framesPerDataTick
        tmp=((framesPerDataTick-n1)*x(:,k-1)+ (n1)*x(:,k))/framesPerDataTick;
        [val,ind]=sort(tmp);
        bWidth=min(diff(val));
        
        if bWidth>0.01
            h(1).XData= ((framesPerDataTick-n1)*x(:,k-1)+ (n1)*x(:,k))/framesPerDataTick;
            set(gca,'YTick',val,'YTickLabel',xTickLabelStrs(ind));
            
            h(1).YData=((framesPerDataTick-n1)*y(:,k-1)+ (n1)*y(:,k))/framesPerDataTick;
            set(h(1),'BarWidth',0.9/bWidth);
            % automatic scaling of horizontal axis
            xlim([0 max(max(h(1).YData)*1.2,1)])

            refresh(1)
            
        end
        [A,map] = rgb2ind(frame2im( getframe(gcf)),256);
        
        if k==2 && n1==0
            imwrite(A,map,outFileName,'gif','DelayTime',0.5);
        else
            imwrite(A,map,outFileName,'gif','writemode', 'append','delaytime',1/framesPerDataTick);
        end
        
    end
    for n1=1:4  %stop between time instant
        imwrite(A,map,outFileName,'gif','writemode', 'append','delaytime',1/framesPerDataTick);
    end
end
%padding final frame to display longer.
    imwrite(A,map,outFileName,'gif','writemode', 'append','delaytime',0.5);
end
```

1箇所ちょっとしたコツがあります．

```matlab
        if bWidth>0.01
```
この部分ですが，縦方向の棒の幅を計算するとき，幅が小さすぎると描画がおかしくなってしまったので，ある程度幅があるときのみ描画しなおすこととしています．

アニメーションgifの作成方法についてはMATLAB公式をほぼそのまま利用しています．

[imwrite](https://jp.mathworks.com/help/matlab/ref/imwrite.html)