方法一
这是使用 MATLAB 的 bar 函数的一种可能解决方案。
假设:
基本思想是使用 Bar 对象的'Baseline' 属性,它允许特定的柱从特定值开始。例如,bar([1,3,5], 'BaseValue', 2) 生成 3 个从值 2 开始的柱:第一个下降 1 个单位,第二个上升 1 个单位,最后一个上升 3 个单位。
不幸的是,从 R2019b 上的测试来看,Axes 上的所有 Bar 对象都必须共享相同的 BaseValue。因此,对于每个 Bar 对象都有自己的 Baseline 值,它们中的每一个都必须位于单独的 Axes 对象上。我们可以通过将一堆 Axes(每个 Bar 一个)叠加在一起来解决这个问题,使除一个之外的所有 Axes 都是透明的。这样所有条形图都将可见。
无论如何,这里的功能。输入是
-
ax(可选):现有 Axes 对象的句柄。如果您已经绘制了其他内容,或者您想手动设置轴的各种属性,则可能需要执行此操作。
-
y:所有增量值的向量。 注意:最终值不是必需的,即要重现问题中的情节,请使用
y=[5, 2, -5, 8, 2];
该函数将句柄输出到创建的每个 Bar 对象。您可能希望这进一步更改条形的 EdgeColor。
function h = wfall(ax, y)
if nargin == 1
y = ax;
ax = gca;
end
if ~strcmp(ax.NextPlot, 'add')
fprintf('hold on not set for current axes. Overriding.\n');
hold(ax, 'on');
end
y = y(:); % column vector
n = length(y);
cumy = cumsum(y);
set(ax, 'XLim', [0, n+1]+0.5, 'YLim', [min(min(cumy), 0), max(max(cumy), 0)]);
% colors:
% decrease - red - code as -1
% total - black - code as 0
% increase - blue - code as 1
set(ax, 'CLim', [-1, 1], 'ColorMap', [1 0 0; 0 0 0; 0 0 1]);
% copy a bunch of axes
for i = 1:n
ax(i+1) = copyobj(ax(1), ax(1).Parent);
end
% Make all subsequent axes invisible
% Make sure all axes will always be the same size by linking properties
set(ax(2:end), 'Color', 'none', 'XColor', 'none', 'YColor', 'none');
linkprop(ax, {'XLim', 'YLim', 'Position', 'DataAspectRatio'});
% define from/to of each bar (except 1st and last)
from = cumy(1:n-1);
to = cumy(2:n);
% color of each bar (except 1st and last)
c = double(y>0) - double(y<0);
c(1) = [];
% first total bar
h = bar(ax(1), 1, from(1), 'CData', 0, 'BaseValue', 0);
% 2nd to 2nd last bars
for i = 1:n-1
h(end+1) = bar(ax(i+1), i+1, to(i), 'CData', c(i), 'BaseValue', from(i), 'ShowBaseLine', 'off');
end
% last total bar
h(end+1) = bar(ax(1), n+1, cumy(n), 'CData', 0);
% setting FaceColor flat makes the Bars use the CData property
set(h, 'FaceColor', 'flat')
如下运行代码,生成如下图。
close all;
ax = gca;
h = wfall(ax, y(1:end-1));
方法二
如果您不想将 Axes 对象堆叠在一起,这是另一种解决方案。
在这种情况下,我们做一个额外的假设:
简单地说,我们绘制的每个条都可以被视为一个彩色条(蓝色/红色),部分被较短的白色条覆盖。
function h = wfall2(ax, y)
if nargin == 1
y = ax;
ax = gca;
end
if ~strcmp(ax.NextPlot, 'add')
fprintf('hold on not set for current axes. Overriding.\n');
hold(ax, 'on');
end
y = y(:); % column vector
n = length(y);
cumy = cumsum(y);
from = cumy(1:n-1);
to = cumy(2:n);
% color values:
% 1 - blue (increase)
% 0 - white
% -1 - red (decrease)
c = double(y>0) - double(y<0);
c(1) = [];
upper = max(cumy(1:n-1), cumy(2:n));
lower = min(cumy(1:n-1), cumy(2:n));
h(1) = bar(ax, 2:n, upper, 'FaceColor', 'flat', 'CData', c);
h(2) = bar(ax, 2:n, lower, 'FaceColor', 'w');
h(3) = bar(ax, 1, cumy(1), 'FaceColor', 'k');
h(4) = bar(ax, n+1, cumy(n), 'FaceColor', 'k');
set(h, 'EdgeColor', 'none')
set(ax, 'CLim', [-1, 1], 'ColorMap', [1 0 0; 0 0 0; 0 0 1]);
运行函数如下:
close all;
ax = gca;
h = wfall2(ax, y(1:end-1));
结果图:
然而,按照我的个人标准,结果有点难看,因为白条将部分覆盖 x 轴。但是,您可以通过将较低的 YLim 设置为负值来解决此问题,即 set(ax, 'YLim', [-0.5 inf])