How to make groups of horizontal bars have the same color?

北战南征 提交于 2019-12-13 03:32:39

问题


I need to plot horizontal bars using the grouped style such that all the bars belonging to each group have the same color but different from other groups (i.e. all bars of the top group are red, the group below it - green, and so on...).

Also, how can I put the values on the top of each bar horizontally? How can I control the position of such values?

This is my code:

y = [91.9 8.1 94.4 5.6; 84.9 15.1 90.12 9.88; 89.4 10.6 91.2 8.8; 72 28 50.9 49.1];

h = barh(y,'grouped'); 
job = {'group1','group2 ','group 3','group4'};
legend(job,'location','northeast');

This is my current figure:


回答1:


Here's a hack:
Plot bar graph for 1 row at a time and fill the space with NaNs while specifying a single color for all rows. Plotting NaNs will plot nothing.

[ry, cy] = size(y);   %Number of rows and columns of y
hold on;
for k = 1:ry
    tmp = [NaN(k-1,cy); y(k,:); NaN(4-k,cy)];   %Filling with NaNs
    h{k} = barh(tmp, 'FaceColor', rand(1,3));   %Plotting bar graph in random color
    h{k} = h{k}(1);   %Store handle of any of the rows (Required for legend)
end
job = {'group1', 'group2', 'group3', 'group4'}; %Required legend entries
legend([h{:}], job);  %Default location is already 'northeast' (so can be skipped)

Output:




回答2:


This is another type of hack; tested on R2017b.

The reason why what you're asking is difficult to do is because each group in a bar plot is actually a Quadrilateral object (i.e. polygon). For example, let's take the 1stBar object:

>> h(1).Face.VertexData

ans =

  3×16 single matrix

  Columns 1 through 9

         0   91.9000   91.9000         0         0   84.9000   84.9000         0         0
    0.6545    0.6545    0.8000    0.8000    1.6545    1.6545    1.8000    1.8000    2.6545
         0         0         0         0         0         0         0         0         0

  Columns 10 through 16

   89.4000   89.4000         0         0   72.0000   72.0000         0
    2.6545    2.8000    2.8000    3.6545    3.6545    3.8000    3.8000
         0         0         0         0         0         0         0

And if we add this line to the code:

drawnow; hold on; scatter(h(1).Face.VertexData(1,:), h(1).Face.VertexData(2,:));

we get (notice the green circles):

What's interesting is that the points in VertexData are not read-only, which means we can just intelligently "rearrange" the VertexData matrices of the N bar groups to get the desired result. Here's one way to do it (notice I renamed h to hBar):

% Obtain the old VertexData:
vd_old = cell2mat(reshape(get([hBar.Face],'VertexData'),1,1,[]));
% Rearrange VertexData:
nB = numel(hBar);
vd_new = cell2mat(permute(mat2cell(vd_old,3,repelem(4,nB),repelem(1,nB)),[1,3,2]));
% Assign the new VertexData back into the Bar objects' Edge and Face fields:
for indB = 1:nB
  hBar(indB).Edge.VertexData = vd_new(:,:,indB);
  hBar(indB).Face.VertexData = vd_new(:,:,indB);
end

Finally, we manually add labels using the text command:

xOffset = 1;

for indB = 1:nB
  text(double(vd_new(1,2:4:end,indB)) + xOffset, double(tmpY(2:4:end)), ...
    string(vd_new(1,2:4:end,indB)),'VerticalAlignment','middle');
end

The end result:

The final code:

function q47978293
close all force;

y = [91.9 8.1 94.4 5.6; 84.9 15.1 90.12 9.88; 89.4 10.6 91.2 8.8; 72 28 50.9 49.1];

figure(47978293); set(gcf,'Position',[786,556,887,420]); hBar = barh(y,'grouped');
legend("group" + (1:4),'location','northeast');
drawnow;
% hold on; scatter(h(1).Face.VertexData(1,:), h(1).Face.VertexData(2,:));

% Obtain the old VertexData:
vd_old = cell2mat(reshape(get([hBar.Face],'VertexData'),1,1,[]));
% Rearrange VertexData:
nB = numel(hBar);
vd_new = cell2mat(permute(mat2cell(vd_old,3,repelem(4,nB),repelem(1,nB)),[1,3,2]));
% Assign the new VertexData back into the Bar objects' Edge and Face fields:
xOffset = 1; % < Adjust as you see fit
for indB = 1:nB
  hBar(indB).Edge.VertexData = vd_new(:,:,indB);
  hBar(indB).Face.VertexData = vd_new(:,:,indB);
  try
    text( y(indB,:) + xOffset, mean(reshape(vd_new(2,1:2:end,indB),2,[]),1,'double'), ...
      string(vd_new(1,2:4:end,indB)),'VerticalAlignment','middle');
  catch % Compatibility fix for R2016b & R2017a. Credit @SardarUsama
    text( y(indB,:) + xOffset, mean(reshape(vd_new(2,1:2:end,indB),2,[]),1,'double'), ...
      split(num2str(vd_new(1,2:4:end,indB))),'VerticalAlignment','middle');
  end
end

end

Note: due to the hacky nature of this solution, if the figure is redrawn (due to e.g. resizing), the bar colors will return to their original state - so make sure that the part that changes colors is the very last thing you do.


P.S.
The R2017b release notes state that this customization should be officially supported now (through the CData property of Bar objects), but I couldn't get the legend to display the correct colors. If you want to try it, just plot using the following command (R2017b+):

barh(y,'grouped','FaceColor','flat','CData',lines(size(y,1)));



回答3:


The two requests overlap making everything kinda complex, since using uniform colors for each group implies using a workaround that needs another workaround to male bar text work. Here is the code:

y = [
  91.90  8.10 94.40  5.60;
  84.90 15.10 90.12  9.88;
  89.40 10.60 91.20  8.80;
  72.00 28.00 50.90 49.10
];

[rows,cols] = size(y);
n = NaN(rows,cols);

c = {'r' 'g' 'b' 'y'};

bh = cell(rows,1);
lh = cell(rows,1);

hold on;

for k = 1:rows
    curr = n;
    curr(k,:) = y(k,:);

    bar = barh(curr,'FaceColor',c{k});

    bh{k} = bar;
    lh{k} = bar(1);
end

hold off;

legend([lh{:}],{'G1','G2','G3','G4'},'Location','southoutside');

yl = get(gca,'XLim');
yl_up = yl(2);
xoff = yl_up * 0.01;
set(gca,'XLim',[yl(1) (yl_up + (yl_up * 0.1))]);

set(gcf,'Units','normalized','Position',[0.1 0.1 0.8 0.8]);

for i = 1:numel(bh)
    bh_i = bh{i};

    for j = 1:numel(bh_i)
        bh_ij = bh_i(j);
        hx = bh_ij.YData + xoff;
        hy = bh_ij.XData + bh_ij.XOffset;

        text(hx,hy,num2str(hx.','%.2f'), ...
             'FontSize',10, ...
             'HorizontalAlignment','left', ...
             'VerticalAlignment','middle');
    end
end

And here is the output:



来源:https://stackoverflow.com/questions/47978293/how-to-make-groups-of-horizontal-bars-have-the-same-color

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!