"Consulting" ウォーターフォールプロット


ほら、あの名前が分からないけど "棒グラフが横にずれていくプロット"。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/8f7df47f-e941-b4e7-0cfb-f8984421f411.png" alt="attach:cat" title="attach:cat" width=500px>


名前が分からないのでそれらしいキーワードで Google 検索して見つけました。どうやら「滝グラフ」とか「Water Fall チャート」とか「マリオグラフ」とかと呼ばれるらしいです。別の Water Fall チャートと区別するために「Consulting Water Fall」とも呼ぶとか。（Wikipedia: [Waterfall Chart](https://en.wikipedia.org/wiki/Waterfall_chart)）

> *滝グラフ*（たきグラフ、英語：Waterfall chart）は、正負の値の累積的影響を判断する際に役立つ*[*可視化*](https://ja.wikipedia.org/wiki/%E5%8F%AF%E8%A6%96%E5%8C%96)*用*[*グラフ*](https://ja.wikipedia.org/wiki/%E3%82%B0%E3%83%A9%E3%83%95)*である。 *[*レンガ*](https://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%B3%E3%82%AC)*が宙に浮いているように列が表示されることから、飛行レンガグラフ（英語：flying bricks chart）や*[*マリオ (ゲームキャラクター)*](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%AA%E3%82%AA_(%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%AD%E3%83%A3%E3%83%A9%E3%82%AF%E3%82%BF%E3%83%BC))*グラフ（英語：Mario chart）として知られている。金融用語で「橋」と呼ばれることも多い。戦略的*[*コンサルティング*](https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%82%B5%E3%83%AB%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0)*会社である*[*マッキンゼー・アンド・カンパニー*](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%83%E3%82%AD%E3%83%B3%E3%82%BC%E3%83%BC%E3%83%BB%E3%82%A2%E3%83%B3%E3%83%89%E3%83%BB%E3%82%AB%E3%83%B3%E3%83%91%E3%83%8B%E3%83%BC)*により、顧客向け*[*プレゼンテーション*](https://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AC%E3%82%BC%E3%83%B3%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)*手法として普及した。*[*[1]*](https://ja.wikipedia.org/wiki/%E6%BB%9D%E3%82%B0%E3%83%A9%E3%83%95#cite_note-1)[*[2]*](https://ja.wikipedia.org/wiki/%E6%BB%9D%E3%82%B0%E3%83%A9%E3%83%95#cite_note-2)* インベントリ分析や*[*性能解析*](https://ja.wikipedia.org/wiki/%E6%80%A7%E8%83%BD%E8%A7%A3%E6%9E%90)*などの多様な定量分析にて利用されている。*
（Wikipedia: [滝グラフ](https://ja.wikipedia.org/wiki/%E6%BB%9D%E3%82%B0%E3%83%A9%E3%83%95)から引用）

  
# 今回やったこと

名前はともかく、残念ながら MATLAB にはそれを描く関数がない（R2022b 時点）ので bar 関数を使って描いてみようとしたお話です。

ちなみに [waterfall](https://jp.mathworks.com/help/matlab/ref/waterfall.html) という関数はありますが、こんなプロットです。これは Wikipedia: [waterfall plot](https://en.wikipedia.org/wiki/Waterfall_plot) で紹介されているタイプのプロットですね。ややこしい！

```matlab:Code
[X,Y] = meshgrid(-3:.125:3);
Z = peaks(X,Y);
waterfall(X,Y,Z)
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/49afe883-72c7-6f27-77f9-4dff514c90d7.png" alt="attach:cat" title="attach:cat" width=500px>


# いや、きっと誰かが作っているだろう・・

プロットの名前がわかれば検索できますね。まずは [File Exchange](https://jp.mathworks.com/matlabcentral/fileexchange/) でみてみると出てきたのは File Exchange: [Waterfall Chart](https://jp.mathworks.com/matlabcentral/fileexchange/22703-waterfall-chart)。MathWorks スタッフが作成した関数の様です。

こんな感じで使います。

```matlab:Code(Display)
x = [1 2 4 3 6 5 3];
waterfallchart(x);
waterfallchart(x, 'width', 0.4);
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/4c110efb-406f-c230-c637-16e9d448d62f.png" alt="attach:cat" title="attach:cat" width=500px>


各バーを線で繋いでいるところにこだわりが見られます。基点と終点は青、プラスの変化は緑、マイナスの変化は・・と色も分けられるようです。

もう一つ Qiita にも[【MATLAB】Waterfall図の作成](https://qiita.com/moko_middo/items/5efd5aff130f7f75c68e) という投稿が見つかりました。ここで [@moko_middo](https://qiita.com/moko_middo) さんは wfall という自作関数を作成されています。

```matlab:Code(Display)
y=[10, 2, -3, -3, 5]; % 基点の値，要因A/B/C/Dによる変動，終点の値は省略
xlabel={'基点','要因A','要因B','要因C','要因D','終点'};
wfall(y,xlabel);
```

こんな具合です。終点の値は自動で入る模様。そしてラベルも入れられるの点も使い勝手がよさそう！

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/dae4f2cd-aa62-b388-7082-f4f67bb39de7.png" alt="attach:cat" title="attach:cat" width=500px>


# どうやって描くのか

とはいえ、MATLAB のプロット機能を深掘りするチャンス、ということで描き方を探ってきます。基本的には棒グラフということで [`bar` 関数](https://jp.mathworks.com/help/matlab/ref/bar.html)をベースに考えてみます。

```matlab:Code
x = [1 3 4 2 5];
bar(x);
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/759e7639-de8c-cb13-05a1-4280a8cba8b1.png" alt="attach:cat" title="attach:cat" width=500px>

ここから各バーの開始位置を変更すれば基本形ができそうな気がしますね。参考：[棒グラフのベースラインの変更](https://jp.mathworks.com/help/matlab/creating_plots/modify-baseline-of-bar-graph.html)

## 大きな制約

ただ、、バーのベースの位置は、 [`bar` 関数](https://jp.mathworks.com/help/matlab/ref/bar.html)のプロパティ [`BaseValue` ](https://jp.mathworks.com/help/matlab/ref/bar.html?#bthf_ju_sep_shared-BaseValue)で設定することができます。ただ１つの座標軸上に１つの [`BaseValue`](https://jp.mathworks.com/help/matlab/ref/bar.html?#bthf_ju_sep_shared-BaseValue) しか持てない・・（R2022b 時点）

   -  MATLAB Anwers: [bar graph : How to show each bar with different base value ?](https://jp.mathworks.com/matlabcentral/answers/289839-bar-graph-how-to-show-each-bar-with-different-base-value) 

## 回避策

なので、まずは簡単な回避策として、ベース値自体も棒グラフの一部としてとして積み上げ棒グラフにしてみることにします。各バーの開始位置は [`cumsum` 関数](https://jp.mathworks.com/help/matlab/ref/cumsum.html)で計算。

```matlab:Code
basex = cumsum(x);
basex = [0, basex(1:end-1)] % 最初の開始位置は 0 なので・・
```

```text:Output
basex = 1x5    
     0     1     4     8    10

```

そして元の値と組み合わせて "stacked" オプションで積み上げ棒グラフにしてみる。

```matlab:Code
xWithbase = [basex; x]
```

```text:Output
xWithbase = 2x5    
     0     1     4     8    10
     1     3     4     2     5

```

```matlab:Code
handle_bars = bar(xWithbase',"stacked"); % 転置に注意
```


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/1eac49f8-3961-1d26-224b-8c68eee6cdf3.png" alt="attach:cat" title="attach:cat" width=500px>

これでベース部分を見えなくすれば・・

```matlab:Code
handle_bars(1).Visible = "off";
```

できました・・。あと各バーに名前を付けるなら Axes オブジェクトの XTickLabel ですね。

```matlab:Code
handle_axes = gca;
handle_axes.XTickLabel = ["A","B","C","D","E"];
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/f662de3e-4481-da2e-0d82-55f7b4c8cfdf.png" alt="attach:cat" title="attach:cat" width=500px>


あとは必要に応じて終点など追加すればいいですね。

# そうは問屋が卸さない

ここまで結構簡単に行きましたが、マイナスの変化があるデータでも試してみると・・

```matlab:Code
x = [1 3 4 -2 5];
basex = cumsum(x);
basex = [0, basex(1:end-1)];
xWithbase = [basex; x];
handle_bars = bar(xWithbase',"stacked"); % 転置に注意
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/fa57409a-f9fb-61e1-5bdd-d12fcdbbeca8.png" alt="attach:cat" title="attach:cat" width=500px>

マイナスの値がちゃんとマイナス側に表示されている。これは [`bar` 関数](https://jp.mathworks.com/help/matlab/ref/bar.html)の本来の用途を考えるとその通りなのですが、今回の用途だととっても残念。

ということで、マイナスの値が入っている場合は少し工夫します。

   -  絶対値を使用する 
   -  ベース（バーの開始位置）を下げる 

```matlab:Code
idx_neg = x < 0; % マイナス値の位置特定
basex(idx_neg) = basex(idx_neg) + x(idx_neg); % ベースをマイナス値分下げる
absx = abs(x);
absxWithbase = [basex; absx];
handle_bars = bar(absxWithbase',"stacked");
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/bbd63618-8b1a-d553-4f63-b3719a5cf028.png" alt="attach:cat" title="attach:cat" width=500px>

あとはベースを見えなくして、マイナス値の部分を別の色にすればそれらしくなりそうです。

```matlab:Code
handle_bars(1).Visible = "off";
% マイナスの値が複数合った時の為、その数にあった色行列を用意する。
handle_bars(2).CData(idx_neg,:) = repelem([0,0,1],sum(idx_neg),1); 
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/add166d7-11bc-e7c7-4c82-77e710017752.png" alt="attach:cat" title="attach:cat" width=500px>

# 色があかんがな・・

棒グラフのバーの色を個別に指定するには CData プロパティを変更すればよかったはずなのですが・・[`bar` 関数](https://jp.mathworks.com/help/matlab/ref/bar.html)の [CData に関するヘルプページ](https://jp.mathworks.com/help/matlab/ref/bar.html?#bthf_ju_sep_mw_fa485b4c-20b6-4fe4-bc5a-2e35c5783c2f)を見ると

既定では、棒グラフを作成するときに `CData` プロパティに RGB 3 成分から成る 3 列の行列が含まれます。行列内の対応する行を変更することで、特定のバーの色を変更できます。

このプロパティは、`FaceColor` または `EdgeColor` プロパティが `'flat'` に設定されている場合にのみ適用されます。

との記載が。`'flat'` にする必要があるらしい。

```matlab:Code
x = [1 3 4 -2 5];
basex = cumsum(x);
basex = [0, basex(1:end-1)];

idx_neg = x < 0; % マイナス値の位置特定
basex(idx_neg) = basex(idx_neg) + x(idx_neg); % ベースをマイナス値分下げる
absx = abs(x);
absxWithbase = [basex; absx];
handle_bars = bar(absxWithbase','stacked','FaceColor','flat');

handle_bars(1).Visible = 'off';
handle_bars(2).CData(idx_neg,:) = repelem([0,0,1],sum(idx_neg),1);
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/8f7df47f-e941-b4e7-0cfb-f8984421f411.png" alt="attach:cat" title="attach:cat" width=500px>


できた。

# さらなるハードルが・・

もしベースの値が常にプラスであればうまく行くのですが・・あくまで bar 関数は 0 スタートのバーを描こうとするため、ベース値までもがマイナスになってしまうと、ここまでの方法ではうまく行きません。

```matlab:Code
x = [1 -3 4 -2 5];
basex = cumsum(x);
basex = [0, basex(1:end-1)];

idx_neg = x < 0; % マイナス値の位置特定
basex(idx_neg) = basex(idx_neg) + x(idx_neg); % ベースをマイナス値分下げる
absx = abs(x);
absxWithbase = [basex; absx];
handle_bars = bar(absxWithbase','stacked','FaceColor','flat');
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149511/4646a315-38b0-6c59-ff63-5c03825448fd.png" alt="attach:cat" title="attach:cat" width=500px>

今回の回避策ではだめそうです。そして冒頭でも触れた通り、バーのベースの位置はプロパティ [`BaseValue`](https://jp.mathworks.com/help/matlab/ref/bar.html?#bthf_ju_sep_shared-BaseValue) で設定することができますが、１つの座標軸上に１つの [`BaseValue`](https://jp.mathworks.com/help/matlab/ref/bar.html?#bthf_ju_sep_shared-BaseValue)  しか持てないので、各バーで個別の [`BaseValue`](https://jp.mathworks.com/help/matlab/ref/bar.html?#bthf_ju_sep_shared-BaseValue)  を持たせることができない（R2022b 時点）・・詰みました。

参考：MATLAB Anwers [bar graph : How to show each bar with different base value ?](https://jp.mathworks.com/matlabcentral/answers/289839-bar-graph-how-to-show-each-bar-with-different-base-value)

  
# まとめ

ということで、**もしベースの値がマイナスにならないのであれば**という条件付きで** **bar 関数 1 つで描くことができました。

ただその条件を満たさない場合は、各バー毎に別々の座標軸（Axes オブジェクト）を用意して、個別に BaseValue を設定し棒グラフを描くという力技に頼るしかないようです。

その方法をまさに実行しているのが [@moko_middo](https://qiita.com/moko_middo) さんの wfall 関数（詳細：[【MATLAB】Waterfall図の作成](https://qiita.com/moko_middo/items/5efd5aff130f7f75c68e)）です。スゴイ。

ぜひ詳細も確認してみてください。