简体   繁体   中英

Color each section in a stacked bar plot differently

I have a stacked bar plot below:-

在此处输入图片说明

This was generated using:

b = barh(1:3,rand(3,2),'stacked');

Now i have a 3x2 cell C each cell element is a 1x3 RGB array.

C = cell(3,2);
for i = 1:3
  for j = 1:2
    C{i,j} = rand(1,3);
  end
end

There are 6 boxes in the figure and 6 corresponding colors. I want to fill these specified colors in the box. I thought using this method:

b(1).Parent.Parent.Colormap = C;

... but it did not work.

Can someone suggest how to draw custom stacked bar plots with the ability to control color of each bar segment? I don't think a tweaking of MATALB's bar command will help.

You need to duplicate and manipulate the graphics objects returned by the bar (or barh ) function

bar(y, 'stacked') returns one Bar graphics object for every column in y . The graphics objects have XData and YData fields which control the position and size of the bar sections. Since all sections from the same column share the graphics objects, you cannot manipulate the colors independantly.

For example, if y is a 2x3 matrix, bar returns 3 graphics objects. Each objects has:

  • XData , a 1x2 vector containing bar indices
  • YData , a 1x2 vector with each element being the height of the section.
  • FaceColor , the color shared by all these sections

My code replicates these graphics objects, so that instead of 3 graphics objects with 1x2 XData and YData fields, you have 6 graphics objects with 1x1 XData and YData fields, each with a unique FaceColor .

Start off with a normal bar or barh graph

figure;
n_bars = 2;
n_sections = 3;

%Initialize the bar graph with default coloring
b = bar(rand(n_bars, n_sections), 'stacked');

Now, make a new figure to hold the plot with the manipulated colors

%Make new figure with new color scheme
f = figure;
a = axes('Parent', f);

%Colors
C = rand(6, 3);

For each graphics object from the original, make two copies.

for jj = 1:n_sections
  %Duplicate the bar graphics object results 
  section1 = copyobj(b(jj), a);
  section2 = copyobj(b(jj), a);

  % Remove one of the bars from each section
  section1.YData(1) = 0;
  section2.YData(2) = 0;

  %Change the color
  section1.FaceColor = C(sub2ind([n_bars, n_sections], 1, jj), :);
  section2.FaceColor = C(sub2ind([n_bars, n_sections], 2, jj), :);
end

Before and After!

默认条形图 修改后的条形图

The idea is to create a separate chart for each data row, otherwise we are unable to get single bar object handle.

ydata = rand(3,2); cdata={'r','g'; 'b','y';'c','k';'g','b'};
numplots = size(ydata,1);
h=zeros(numplots,2);
figure, hold on
for k=1:numplots,
    h(k,:) = barh(nan(size(ydata)),'stacked');
    set(h(k,1),'FaceColor',cdata{k,1});
    set(h(k,2),'FaceColor',cdata{k,2});
    tmp_ydata = get(h(k,1),'YData');
    tmp_ydata(k) = ydata(k,1); 
    set(h(k,1),'YData',tmp_ydata);
    tmp_ydata = get(h(k,2),'YData');
    tmp_ydata(k) = ydata(k,2);
    set(h(k,2),'YData',tmp_ydata);
end
hold off 

For details please see post "Highlighting Parts of Charts" by Mike on MATLAB Graphics

Based on Cecilia's answer here is a more general solution.

n_bars = 3;
n_sections = 4;
b = barh(rand(n_bars, n_sections), 'stacked');

f = figure;
a = axes('Parent', f);
C = rand(n_bars*n_sections, 3);
for jj = 1:n_sections
    for ii=1:n_bars
        section=copyobj(b(jj), a);
        dummy=1:n_bars;
        dummy(dummy==ii)=[];
        section.YData(dummy) = 0;
        section.FaceColor = C(sub2ind([n_bars, n_sections], ii, jj), :);
    end
end

For any values of n_bars and n_sections , this will work. If someone can suggest vectorized or more efficient implementation, please do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM