如何在3D条形图中为条设置任意颜色? [英] How to set arbitrary colors for bars in a 3D bar plot?

查看:210
本文介绍了如何在3D条形图中为条设置任意颜色?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个带有某些值的矩阵Z,我想通过按高度绘制Z中的值来说明它.我想到的第一个解决方案是表面,但是在小的矩阵上使用surf和类似函数看起来并不好.

因此,我想到了在bar3中使用类似3D条形图的方法.但是问题在于,此功能始终按组而不是按高度设置颜色,而我无法做到这一点.

这里是一个例子:

Z = peaks(5);
subplot 121
surf(Z)
title('Surface look bad')
subplot 122
bar3(Z)
title('The color is not by height')

我试图在bar3返回的句柄中查找颜色属性(如CDataFaceColor),但是迷失了所有值以及它们与条形本身的关系.

最终,我希望有一个通用的解决方案,对于2个矩阵ZC,我可以创建一个3D条形图,其条形图的高度由Z给出,颜色由C给出.

我该怎么办?

解决方案

函数bar3返回一个 surface 对象,每个组一个(即每种颜色一个),因此所有条形一组中的一个实质上被绘制为一个破碎"表面.在此答案中对此进行了很好的解释,因此在此不再赘述.

相反,我将解决此特定问题的解决方案.表面的相关性质为CData.创建条形图时,每个表面的CData都分配有一定大小的矩阵(我们将对此进行说明),这些矩阵都等于一个值.每个表面的值都不同.这就是整个图形将其颜色映射表转换为组的颜色的方式.

如上所述(并在链接的答案中进行了详细说明),每个组都由一个表面表示,因此需要一个表面整个矩阵以定义表面每个点的颜色.我们要做的第一件事是获得此矩阵大小:

Z = peaks(5);
bar_h = bar3(Z);
% we take only the first one, but they are all the same size:
cdata_sz = size(bar_h(1).CData) 

cdata_sz =
    30     4

CData始终具有4列(请参见此处原因),并且行数始终为6 * number组.这是因为使用一个区域对象(最后一个顶点与第一个顶点相似)创建一个封闭的矩形需要5个顶点,而一条线是用于用NaN分隔条之间的间距,因此它们看起来是分开的.

接下来,我们需要放大原始的颜色图(与Z的大小相同),以正确的方式适合CData.本质上,我们只想对属于同一条的所有顶点重复相同的值.假设Z也是我们的颜色数据(即,我们按高度着色),我们这样做:

z_color = repelem(Z,6,4)

现在,我们需要将我们的z_color分成不同数量的小组.每个单元格将包含一个表面对象的着色数据:

z_color = mat2cell(z_color,cdata_sz(1),ones(1,size(Z,2))*cdata_sz(2));

最后,我们将新的颜色数据应用于条形图:

set(bar_h,{'CData'},z_color.')

作为奖励,如果我们想从条形图中删除所有零值,则可以通过将其设置为NaN轻松完成:

Z(abs(Z)<eps) = nan;
C(isnan(Z)) = nan; % if we use a colormap C different from Z

以上所有内容都可以归结为这个方便的功能:

function bar_h = Cbar3(Z,C,b,y)
% Z - The data
% C - CData (if other then Z values)
% b - Minimum absolute value to keep colored
% y - y-axis values to order the data by

if nargin<2, C = Z; end
if nargin<3 || isempty(b), b = 0; end
Z(abs(Z)<b) = nan;
C(isnan(Z)) = nan;
if nargin<4 
    bar_h = bar3(Z);
else
    bar_h = bar3(y,Z);
end
cdata_sz = size(bar_h(1).CData);
z_color = repelem(C,6,4);
z_color = mat2cell(z_color,...
    cdata_sz(1),ones(1,size(Z,2))*cdata_sz(2));
set(bar_h,{'CData'},z_color.')
end

用法示例:

subplot 121
Z = peaks(30);
Cbar3(Z,Z,0.5);
pbaspect auto
shading flat % just to get a cleaner look
title('Cbar3 using height as color')

subplot 122
Cbar3(Z,rand(size(Z)),0.5);
pbaspect auto
shading flat % just to get a cleaner look
title('Cbar3 using random as color')

结果:

Say that I have a matrix Z with some values, and I want to illustrate it by a plotting the values in Z by height. The first solution comes to mind is a surface, but using surf and similar functions with small matrices doesn't look good.

So I thought about using something like a 3D bar plot with bar3. But the problem is that this function always sets the color by the group and not by height, and I can't get it to do so.

Here is an example:

Z = peaks(5);
subplot 121
surf(Z)
title('Surface look bad')
subplot 122
bar3(Z)
title('The color is not by height')

I tried to look for the color properties in the handles returned by bar3 (like CData and FaceColor) but got lost with all the values and how they relate to the bars themselves.

Ultimately, I would like to have a general solution that for 2 matrices Z and C I can create a 3D bar plot with bars in height given by Z and color given by C.

How can I do so?

解决方案

The function bar3 returns a surface object, one for each group (i.e. one for each color), so all the bars in one group are essentially plotted as one 'broken' surface. This is explained very good in this answer, so I won't repeat it here.

Instead, I'll get to the solution for this specific problem. The relevant property of the surface is CData. When we create the bar plot, each surface's CData is assigned with a matrix in some size (we'll get to this) that is all equal one value. A different value for each surface. This is how the figure as a whole translates its color map to the color of the groups.

As written above (and elaborated in the linked answer), each group represented by a surface, so it takes a whole matrix to define the color at each point of the surface. The first thing we want to do is to get this matrix size:

Z = peaks(5);
bar_h = bar3(Z);
% we take only the first one, but they are all the same size:
cdata_sz = size(bar_h(1).CData) 

cdata_sz =
    30     4

CData has always 4 columns (see here why), and the number of rows is always 6*number of groups. This is because it takes 5 vertices to create one closed rectangle with an area object (the last vertex is like the first one) and one line is for spacing between the bars with NaNs, so they will look separated.

Next, we need to enlarge our original colormap (which is the same size of Z) to fit CData in the right way. Essentially, we just want to repeat the same value for all vertices that belong to the same bar. Assuming Z is also our color data (i.e. we color by height) we do:

z_color = repelem(Z,6,4)

Now we need to split our z_color to different cells in the number of our groups. Each cell will contain the coloring data for one surface object:

z_color = mat2cell(z_color,cdata_sz(1),ones(1,size(Z,2))*cdata_sz(2));

And finally, we apply the new color data to the bar plot:

set(bar_h,{'CData'},z_color.')

As a bonus, if we want to remove all zero values from our bar, it can be done easily by setting them to NaN:

Z(abs(Z)<eps) = nan;
C(isnan(Z)) = nan; % if we use a colormap C different from Z

All the above could be boiled down to this handy function:

function bar_h = Cbar3(Z,C,b,y)
% Z - The data
% C - CData (if other then Z values)
% b - Minimum absolute value to keep colored
% y - y-axis values to order the data by

if nargin<2, C = Z; end
if nargin<3 || isempty(b), b = 0; end
Z(abs(Z)<b) = nan;
C(isnan(Z)) = nan;
if nargin<4 
    bar_h = bar3(Z);
else
    bar_h = bar3(y,Z);
end
cdata_sz = size(bar_h(1).CData);
z_color = repelem(C,6,4);
z_color = mat2cell(z_color,...
    cdata_sz(1),ones(1,size(Z,2))*cdata_sz(2));
set(bar_h,{'CData'},z_color.')
end

Example of usage:

subplot 121
Z = peaks(30);
Cbar3(Z,Z,0.5);
pbaspect auto
shading flat % just to get a cleaner look
title('Cbar3 using height as color')

subplot 122
Cbar3(Z,rand(size(Z)),0.5);
pbaspect auto
shading flat % just to get a cleaner look
title('Cbar3 using random as color')

Result:

这篇关于如何在3D条形图中为条设置任意颜色?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆