How to set arbitrary colors for bars in a 3D bar plot?

前端 未结 3 425
渐次进展
渐次进展 2021-01-14 08:58

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

相关标签:
3条回答
  • 2021-01-14 09:17

    This is a partial answer.

    The case of using the bar height as color is covered by the official MATLAB documentation. Essentially the example code boils down to:

    function q45423394
    hB = bar3(peaks(25)); colorbar;
    for indE = 1:numel(hB)
      hB(indE).CData = hB(indE).ZData;
    end
    

    All you need to do afterwards is make sure that the colormap is the one you want.

    0 讨论(0)
  • 2021-01-14 09:24

    While I find EBH's solution aesthetically more pleasing, here there is a simpler solution: interpolation

    z = peaks(5);
    [x,y]=meshgrid(1:0.1:size(z,1),1:0.1:size(z,2));
    zn=interp2(z,x,y,'nearest');
    % plot it
    surf(zn,'edgecolor','none','facecolor','interp')
    

    0 讨论(0)
  • 2021-01-14 09:28

    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:

    0 讨论(0)
提交回复
热议问题