Stacked bar graph with negative BaseValue but "positive" height

2 ビュー (過去 30 日間)
Leon Berger
Leon Berger 2023 年 8 月 21 日
コメント済み: Leon Berger 2023 年 9 月 6 日
I want to plot a waterfall-chart. Simplified, I want to show costs, revenue, and lastly profit/loss. The respective components of "costs"-bar and "revenues"-bar should also be shown, thus i need stacked bars. The "revenue" bar should have its base value, where the "costs"-bar ends. For this I work with multiple overlapping axes, and it works, when not working with stacked bars:
But when i try to stack the components of costs & revenue, it won't work for the revenue bar. I know that there are few problems regarding the use of negative values, but I tried everything and I really can't come up with a solution. In the following you'll find my code sofar:
%Data
costs_tot = -1200;
rev_tot = 900;
costs_distr = [-400 -800];
rev_distr = [300 600];
fig0 = figure('Color', 'w');
ax = gca;
hold(ax(1),"on");
%Axes Appearence
ax(1).XTick = [1:1:3];
ax(1).XAxis.TickLength = [0 0];
ax(1).XTickLabelRotation = 90;
ax(1).TickLabelInterpreter = 'none';
ax(1).Box = 'off';
ax(1).YTick = [-1500 : 500 : 500];
ax(1).YGrid = 'on';
set(ax(1), 'XLim', [0.0, 4.0],'YLim', [-1500.0, 500.0],'XAxisLocation', 'bottom',...
'XTickLabels', ["Costs" "Revenue" "Profit/Loss"]);
for i = 2:3
ax(i) = copyobj(ax(1), ax(1).Parent);
end
set(ax(2:end), 'Color', 'none', 'XColor', 'none', 'YColor', 'none');
linkprop(ax, {'XLim', 'YLim', 'DataAspectRatio'});
%plot bars
b = bar(ax(1), 1, costs_distr, 'stacked', 'BaseValue', 0, 'FaceColor', 'flat', 'EdgeColor', 'flat');
b(1).CData = [0.8500 0.3250 0.0980]; %orange
b(2).CData = [0.9290 0.6940 0.1250]; %yellow
c = bar(ax(2), 2, [costs_tot+rev_distr(1), costs_tot+rev_distr(1)+rev_distr(2)], ...
'stacked', 'BaseValue', costs_tot, 'FaceColor', 'flat', 'EdgeColor', 'flat');
c(1).CData = [0 0.4470 0.7410]; %dark blue
c(2).CData = [0.3010 0.7450 0.9330]; %light blue
d = bar(ax(3), 3, (costs_tot + rev_tot), 'BaseValue', 0, 'FaceColor', 'flat', 'EdgeColor', 'flat');
d.CData = [0.6350 0.0780 0.1840]; %dark red
I use Version R2019b

採用された回答

Voss
Voss 2023 年 8 月 21 日
編集済み: Voss 2023 年 8 月 21 日
The BaseValue is not necessarily the bottom of the bar; for bars representing negative value, the BaseValue is the top of the bar. Therefore, BaseValue for the revenue bar should not be costs_tot but rather costs_tot+rev_tot.
%Data
costs_tot = -1200;
rev_tot = 900;
costs_distr = [-400 -800];
rev_distr = [300 600];
fig0 = figure('Color', 'w');
ax = gca;
hold(ax(1),"on");
%Axes Appearance
ax(1).XTick = [1:1:3];
ax(1).XAxis.TickLength = [0 0];
ax(1).XTickLabelRotation = 90;
ax(1).TickLabelInterpreter = 'none';
ax(1).Box = 'off';
ax(1).YTick = [-1500 : 500 : 500];
ax(1).YGrid = 'on';
set(ax(1), 'XLim', [0.0, 4.0],'YLim', [-1500.0, 500.0],'XAxisLocation', 'bottom',...
'XTickLabels', ["Costs" "Revenue" "Profit/Loss"]);
for i = 2:3
ax(i) = copyobj(ax(1), ax(1).Parent);
end
set(ax(2:end), 'Color', 'none', 'XColor', 'none', 'YColor', 'none');
linkprop(ax, {'XLim', 'YLim', 'DataAspectRatio'});
%plot bars
b = bar(ax(1), 1, costs_distr, 'stacked', 'BaseValue', 0, 'FaceColor', 'flat', 'EdgeColor', 'flat');
b(1).CData = [0.8500 0.3250 0.0980]; %orange
b(2).CData = [0.9290 0.6940 0.1250]; %yellow
c = bar(ax(2), 2, [costs_tot+rev_distr(1), costs_tot+rev_distr(1)+rev_distr(2)], ...
'stacked', 'BaseValue', costs_tot+rev_tot, 'FaceColor', 'flat', 'EdgeColor', 'flat');
c(1).CData = [0 0.4470 0.7410]; %dark blue
c(2).CData = [0.3010 0.7450 0.9330]; %light blue
d = bar(ax(3), 3, (costs_tot + rev_tot), 'BaseValue', 0, 'FaceColor', 'flat', 'EdgeColor', 'flat');
d.CData = [0.6350 0.0780 0.1840]; %dark red
  5 件のコメント
Voss
Voss 2023 年 8 月 22 日
編集済み: Voss 2023 年 8 月 22 日
You're right, there's something weird (at least, unintuitive to me) going on with what changing the BaseValue of a stacked bar actually does. So, rather than spend time trying to make sense of it, I would use patch instead of bar. This approach gives you complete control. (And it also only requires one axes, which the one-bar-per-axes thing was a workaround, I gather, due to another bizarre aspect of the behavior of stacked bars.)
You should be able to change costs_distr and/or rev_distr, and it should still work, now.
%Data
costs_distr = [-400 -800];
rev_distr = [300 600];
costs_tot = sum(costs_distr);
rev_tot = sum(rev_distr);
fig0 = figure('Color', 'w');
ax = gca;
hold(ax,"on");
%Axes Appearance
ax.XTick = [1:1:3];
ax.XAxis.TickLength = [0 0];
ax.XTickLabelRotation = 90;
ax.TickLabelInterpreter = 'none';
ax.Box = 'off';
ax.YTick = [-1500 : 500 : 500];
ax.YGrid = 'on';
set(ax, 'XLim', [0.0, 4.0],'YLim', [-1500.0, 500.0],'XAxisLocation', 'bottom',...
'XTickLabels', ["Costs" "Revenue" "Profit/Loss"]);
%plot bars
bar_width = 0.8;
np = numel(costs_distr);
xd = 1+0.5*bar_width*[-1;1];
yd = [0 cumsum(costs_distr)];
colors = [0.8500 0.3250 0.0980; 0.9290 0.6940 0.1250];
b = patch(ax, ...
'XData',xd(zeros(1,np)+[1;1;2;2;1]), ...
'YData',yd((1:end-1)+[0;1;1;0;0]), ...
'FaceColor','flat', ...
'FaceVertexCData',colors, ...
'EdgeColor','none', ...
'FaceAlpha',0.8);
np = numel(rev_distr);
xd = 2+0.5*bar_width*[-1;1];
yd = costs_tot+[0 cumsum(rev_distr)];
colors = [0 0.4470 0.7410; 0.3010 0.7450 0.9330];
c = patch(ax, ...
'XData',xd(zeros(1,np)+[1;1;2;2;1]), ...
'YData',yd((1:end-1)+[0;1;1;0;0]), ...
'FaceColor','flat', ...
'FaceVertexCData',colors, ...
'EdgeColor','none', ...
'FaceAlpha',0.8);
np = 1;
xd = 3+0.5*bar_width*[-1;1];
yd = [0 costs_tot+rev_tot];
colors = [0.6350 0.0780 0.1840];
d = patch(ax, ...
'XData',xd(zeros(1,np)+[1;1;2;2;1]), ...
'YData',yd((1:end-1)+[0;1;1;0;0]), ...
'FaceColor','flat', ...
'FaceVertexCData',colors, ...
'EdgeColor','none', ...
'FaceAlpha',0.8);
Leon Berger
Leon Berger 2023 年 9 月 6 日
Thank you very much. This was a little bit difficult to implement for my original project, but it works wonderfully. very helpful...

サインインしてコメントする。

その他の回答 (0 件)

カテゴリ

Help Center および File ExchangeAnnotations についてさらに検索

製品


リリース

R2019b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by