Stacked bar graph with negative BaseValue but "positive" height
6 次查看(过去 30 天)
显示 更早的评论
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
0 个评论
采纳的回答
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
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);
更多回答(0 个)
另请参阅
类别
在 Help Center 和 File Exchange 中查找有关 Annotations 的更多信息
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!