如何在MATLAB的bar3图中隐藏零值

10

我有一个二维直方图(图形是3D的 - 由几个直方图并排绘制而成),使用bar3绘图命令生成。但是,所有的零值都显示为x-y平面上的平面正方形。有没有办法防止MATLAB显示这些值?我已经尝试将所有零替换为NaN,但它并没有改变绘图的任何内容。以下是我尝试过的代码:

x1=normrnd(50,15,100,1); %generate random data to test code
x2=normrnd(40,13,100,1);
x3=normrnd(65,12,100,1);

low=min([x1;x2;x3]);
high=max([x1;x2;x3]);
y=linspace(low,high,(high-low)/4); %establish consistent bins for histogram
z1=hist(x1,y);
z2=hist(x2,y);
z3=hist(x3,y);
z=[z1;z2;z3]';
bar3(z)

如您所见,图中有相当多的零值。关闭图形并在将零值替换为NaN后重新绘制似乎没有任何改变:

close
z(z==0)=NaN;
bar3(z)

你好,stem也是相同的,也是1D,以防万一。 - Filippo Mazza
3个回答

13

一种解决方法是修改由bar3创建的图形对象。首先,您必须获取从bar3返回的句柄:

h = bar3(z);
在您的情况下,h 将是一个 3 元素向量,每个元素对应于一组彩色条形图的 handle。然后,以下代码应该使计数为零的柱子不可见:

在您的情况下,h将是一个包含三个句柄的向量,每个句柄对应着一组有颜色的条形图。接下来的代码将会使得计数为零的条柱不可见:

for i = 1:numel(h)
  index = logical(kron(z(:, i) == 0, ones(6, 1)));
  zData = get(h(i), 'ZData');
  zData(index, :) = nan;
  set(h(i), 'ZData', zData);
end

下面是一个示意图(带有必要的手绘圆圈):

enter image description here

它的工作原理...

如果您的柱状图向量是 N-by-1,那么 bar3 将绘制 6*N 个矩形补丁(即每个柱体的 6 个面)。因此,h 中每组补丁对象的 'ZData' 属性将是 (6*N)-by-4,因为每个矩形面有 4 个角。每个 'ZData' 属性的 6 行数据集合因此就是一个柱子的 6 个面的 z 坐标。

上面的代码首先创建了一个逻辑向量,在该向量的每个地方都等于 0 的位置,并使用 kron 函数将该向量的每个元素复制了 6 次。这成为 'ZData' 属性的行的索引,该索引用于将空柱子的矩形面的 z 坐标设置为 nan。这将导致不渲染那些面。


编辑:

下面是稍加修改后的代码版本,通过从绘制的条形图的 'ZData' 属性 中获取条形高度使其更通用,所以需要的只是从 bar3 返回的句柄。我还将代码封装在一个函数中(省略了错误和输入检查):

function remove_empty_bars(hBars)
  for iSeries = 1:numel(hBars)
    zData = get(hBars(iSeries), 'ZData');  % Get the z data
    index = logical(kron(zData(2:6:end, 2) == 0, ones(6, 1)));  % Find empty bars
    zData(index, :) = nan;                 % Set the z data for empty bars to nan
    set(hBars(iSeries), 'ZData', zData);   % Update the graphics objects
  end
end

在我提出问题后,我想到修改对象属性的想法,但我不知道从哪里开始。您的代码非常好用 - 我将其放入一个名为bar3nonzero的m文件中,现在我有了一个无需麻烦的方法来生成图表。 - Doresoom
@gnovice:+1...你又成功地找到了kron函数的好用之处 :) - Amro
1
如果我可以建议的话,我会用以下代码替换 index = logical(kron(z(:,i) == 0,ones(6,1))); index = logical(kron(abs(z(:,i)) < eps,ones(6,1))); 因为有时由于数值误差,z实际上并不是0,而是接近0。 - Nicola
@Nicola:如果你处理的是任意浮点数值,那么这是正确的。然而,在这种情况下,OP正在绘制直方图条形计数,这些计数应该只包含精确的整数值。 - gnovice

7
以下是关于如何隐藏零值柱形图的示例。我们先从一个普通的BAR3绘图开始:
x = 1:7;
Y = jet(numel(x));
h = bar3(x,Y,'detached');
xlabel x; ylabel y; zlabel z; box on;

before

请注意,变量h包含一个surface句柄数组(在本例中为3个,每个“组”对应一组条形图。组对应于Y矩阵的列,每个都用不同的颜色表示)。

现在来看如何隐藏零值的代码:

for i=1:numel(h)
    %# get the ZData matrix of the current group
    Z = get(h(i), 'ZData');

    %# row-indices of Z matrix. Columns correspond to each rectangular bar
    rowsInd = reshape(1:size(Z,1), 6,[]);

    %# find bars with zero height
    barsIdx = all([Z(2:6:end,2:3) Z(3:6:end,2:3)]==0, 2);

    %# replace their values with NaN for those bars
    Z(rowsInd(:,barsIdx),:) = NaN;

    %# update the ZData
    set(h(i), 'ZData',Z)
end

after

解释:

对于每组条形图,创建一个 surface 图形对象(存储在h(i)句柄中)。 它的Z坐标矩阵ZData表示为一个6*N-by-4矩阵(对于XDataYDataCData矩阵也是如此),其中N是每组矩形条的数量或在上面的示例中为7。

这样,每个矩形都用6x4矩阵表示(分别对应X/Y/Z坐标的每个矩阵)。 例如,一个这样的矩形的坐标看起来像:

>> xx = get(h(3),'XData'); yy = get(h(3),'YData'); zz = get(h(3),'ZData');

>> xx(1:6,:)
ans =
          NaN          2.6          3.4          NaN
          2.6          2.6          3.4          3.4
          2.6          2.6          3.4          3.4
          NaN          2.6          3.4          NaN
          NaN          2.6          3.4          NaN
          NaN          NaN          NaN          NaN

>> yy(1:6,:)
ans =
          NaN          0.6          0.6          NaN
          0.6          0.6          0.6          0.6
          1.4          1.4          1.4          1.4
          NaN          1.4          1.4          NaN
          NaN          0.6          0.6          NaN
          NaN          NaN          NaN          NaN

>> zz(1:6,:)
ans =
          NaN            0            0          NaN
            0            1            1            0
            0            1            1            0
          NaN            0            0          NaN
          NaN            0            0          NaN
          NaN          NaN          NaN          NaN

每个矩阵的第二列沿左侧面跟踪点,第三列沿右侧面跟踪点,连接两者将绘制矩形的4个面:
>> surface(xx(1:6,2:3), yy(1:6,2:3), zz(1:6,2:3), cc(1:6,2:3))
>> view(3)

rectangle_surface

第一列和最后一列通过关闭矩形的两侧绘制出剩下的两个面。

所有这样的矩阵都被连接成一个高矩阵,使用一个单一的表面对象来绘制所有的矩形。这是通过使用NaN值来分隔不同的部分实现的,既在同一个矩形点内部,也在不同矩形之间。

因此,上述代码的作用是查找Z-高度为零的矩形,并用NaN值替换其所有值,从而有效地告诉MATLAB不要绘制由这些点形成的表面。


1

我的问题不是零值,而是 NaN 值(它们在 bar3 内部转换为零值)。 我希望继续显示值为零的元素,但不包括值为 NaN 的元素。 我稍微调整了代码,现在它完美地工作了:

for i = 1:numel(h)
  index = logical(kron(isnan(z(:,i)),ones(6,1)));
  zData = get(h(i),'ZData');
  zData(index,:) = nan;
  set(h(i),'ZData',zData);
end

谢谢!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接