LoginSignup
3
0

More than 1 year has passed since last update.

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

Last updated at Posted at 2023-02-24

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

attach:cat

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

滝グラフ(たきグラフ、英語:Waterfall chart)は、正負の値の累積的影響を判断する際に役立つ*可視化グラフである。 レンガが宙に浮いているように列が表示されることから、飛行レンガグラフ(英語:flying bricks chart)やマリオ (ゲームキャラクター)グラフ(英語:Mario chart)として知られている。金融用語で「橋」と呼ばれることも多い。戦略的コンサルティング会社であるマッキンゼー・アンド・カンパニーにより、顧客向けプレゼンテーション手法として普及した。[1][2] インベントリ分析や性能解析などの多様な定量分析にて利用されている。
(Wikipedia: 滝グラフから引用)

今回やったこと

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

ちなみに waterfall という関数はありますが、こんなプロットです。これは Wikipedia: waterfall plot で紹介されているタイプのプロットですね。ややこしい!

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

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

プロットの名前がわかれば検索できますね。まずは File Exchange でみてみると出てきたのは File Exchange: Waterfall Chart。MathWorks スタッフが作成した関数の様です。

こんな感じで使います。

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

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

もう一つ Qiita にも【MATLAB】Waterfall図の作成 という投稿が見つかりました。ここで @moko_middo さんは wfall という自作関数を作成されています。

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

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

attach:cat

どうやって描くのか

とはいえ、MATLAB のプロット機能を深掘りするチャンス、ということで描き方を探ってきます。基本的には棒グラフということで bar 関数をベースに考えてみます。

Code
x = [1 3 4 2 5];
bar(x);
attach:cat

ここから各バーの開始位置を変更すれば基本形ができそうな気がしますね。参考:棒グラフのベースラインの変更

大きな制約

ただ、、バーのベースの位置は、 bar 関数のプロパティ BaseValue で設定することができます。ただ1つの座標軸上に1つの BaseValue しか持てない・・(R2022b 時点)

回避策

なので、まずは簡単な回避策として、ベース値自体も棒グラフの一部としてとして積み上げ棒グラフにしてみることにします。各バーの開始位置は cumsum 関数で計算。

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

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

Code
xWithbase = [basex; x]
Output
xWithbase = 2x5    
     0     1     4     8    10
     1     3     4     2     5

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

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

Code
handle_bars(1).Visible = "off";

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

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

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

そうは問屋が卸さない

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

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

マイナスの値がちゃんとマイナス側に表示されている。これは bar 関数の本来の用途を考えるとその通りなのですが、今回の用途だととっても残念。

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

  • 絶対値を使用する
  • ベース(バーの開始位置)を下げる
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");
attach:cat

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

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

色があかんがな・・

棒グラフのバーの色を個別に指定するには CData プロパティを変更すればよかったはずなのですが・・bar 関数CData に関するヘルプページを見ると

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

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

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

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);
attach:cat

できた。

さらなるハードルが・・

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

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');
attach:cat

今回の回避策ではだめそうです。そして冒頭でも触れた通り、バーのベースの位置はプロパティ BaseValue で設定することができますが、1つの座標軸上に1つの BaseValue しか持てないので、各バーで個別の BaseValue を持たせることができない(R2022b 時点)・・詰みました。

参考:MATLAB Anwers bar graph : How to show each bar with different base value ?

まとめ

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

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

その方法をまさに実行しているのが @moko_middo さんの wfall 関数(詳細:【MATLAB】Waterfall図の作成)です。スゴイ。

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

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0