Multi Colors and Legend for each bar

How do I set Multi Colors and Legend for each bar? Here I have given below my code
clc;
close all;
clear;
y = [467000, 150777, 20915, 229073, 95844];
figure;
bar(y);
set(gca,'XTickLabel',{'DG', 'PV', 'Converter', 'Battery', 'Flywheel'})
xlabel ('Components','fontweight','bold','FontSize',15)
ylabel ('Net Present Cost($)','fontweight','bold','FontSize',15)
grid on

 Accepted Answer

dpb
dpb on 5 Aug 2022
Edited: dpb on 9 Aug 2022
Slightly different, using a colorbar is nice alternative; legend is somewhat problematical owing to design of bar in that it's only a single object with one vector --
y = [467000, 150777, 20915, 229073, 95844];
x=categorical({'DG', 'PV', 'Converter', 'Battery', 'Flywheel'}); % turn x into categorical variable
figure;
hB=bar(x,y,'FaceColor','flat');
C=colororder; % retrieve default colororder vector
hB.CData=C(1:numel(y),:); % use first N
grid on
ylabel ('Net Present Cost($)','fontweight','bold','FontSize',15)
hAx=gca; % get current axes handle
hAx.YAxis.TickLabelFormat='%0.1f'; % fix up the funky numeric display
% now add a legend by faking another plot that will create the handles...
hold on
hA=area(x,nan(numel(x))); % area will be patch
hLg=legend(hA,categories(x)); % legend it...
Again, that one has to resort to workarounds to do something that should be easily done by direct action with bar continues to illustrate how poor its UI/internal design is. While such games are sometimes entertaining and intriguing to see how clever one can get, It's not productive time spent and detracts from accomplishing the real job at hand in timely manner.
The above produces(*)
(*) Sans the y axis cleanup that's not in the copy I saved/attached...
ADDENDUM:
For releases of MATLAB prior to incorporation of categorical variables on x-axis (not sure precisely when)...
figure
components={'DG', 'PV', 'Converter', 'Battery', 'Flywheel'};
y = [467000, 150777, 20915, 229073, 95844];
hB=bar(y,'FaceColor','flat');
xticklabels(components)
C=colororder; % retrieve default colororder vector
hB.CData=C(1:numel(y),:); % use first N
grid on
ylabel ('Net Present Cost($)','fontweight','bold','FontSize',15)
hAx=gca; % get current axes handle
hAx.YAxis.TickLabelFormat='%0.1f'; % fix up the funky numeric display
% now add a legend by faking another plot that will create the handles...
hold on
hA=area(nan(numel(components))); % area will be patch
set(hA,{'FaceColor'},mat2cell(hB.CData,[ones(size(y))],[3])) % set areas to match bar face colors
hLg=legend(hA,components); % legend it...

6 Comments

After running the code, the bar graph is not coming up. Rather, such a problem appears.
R2015a is pretty old; I didn't notice that before. bar() hadn't yet been updated to accept the categorical x variable by then, it appears...just revert back to the way you did the bar graph, then, using only the y values and manually labelling the xticks.
You'll have noticed that categorical orders things alphabetically on display whereas the data aren't in that order, be sure to keep them straight.
Sir,
I used MATLAB 2022a and got the following graph by running the code you gave.
But sir, there is a difference between bar color and legend color. So sir, if you kindly solve it again, it will benefit me. Here I have given below your code.
clc;
close all;
clear;
figure
components={'DG', 'PV', 'Converter', 'Battery', 'Flywheel'};
y = [467000, 150777, 20915, 229073, 95844];
hB=bar(y,'FaceColor','flat');
xticklabels(components)
C=colororder; % retrieve default colororder vector
hB.CData=C(1:numel(y),:); % use first N
grid on
ylabel ('Net Present Cost($)','fontweight','bold','FontSize',15)
hAx=gca; % get current axes handle
hAx.YAxis.TickLabelFormat='%0.1f'; % fix up the funky numeric display
% now add a legend by faking another plot that will create the handles...
hold on
hA=area(nan(numel(components))); % area will be patch
hLg=legend(hA,components);
Oh...a line got left out to set the area 'FaceColor' property to be same as resultant 'CData' from the bar to keep them in synch. I didn't notice the omission, sorry...
Add
set(hA,{'FaceColor'},mat2cell(hB.CData,[ones(1,5)],[3]))
before doing the legend() call and all will be well...
I edited the above Answer/Comment...
A lot of thanks, sir, for giving your precious time.
and
I'm also sorry for selecting the Accepted Answer lately.
No problem, glad to help...and thank you for coming back, belatedly or not... :)

Sign in to comment.

More Answers (1)

Colorbar option
This is based on the solution from this thread.
barData = rand(1,5);
figure();
bh = bar(barData);
% set bar color
bh.FaceColor = 'flat';
bh.CData = jet(numel(barData)); % use any colormap you'd like
% Add colorbar with categorical color labels
colormap(bh.CData)
cb = colorbar();
caxis([0,numel(barData)])
cb.YTick = 0.5 : 1 : numel(barData);
cb.TickLabels = {'Plovdiv','Varna','Burgas','Ruse','Haskovo'};
cb.TickLabelInterpreter = 'none';
cb.FontSize = 8;
bh.Parent.Position(3) = 0.55; % make the axis narrower to fit the colorbar labels.
Legend option
You can create each bar separately and specify its x-values.
% barData = rand(1,5)
colors = jet(numel(barData));
names = {'Plovdiv','Varna','Burgas','Ruse','Haskovo'};
fig = figure();
ax = axes(fig);
hold(ax,'on')
h = gobjects(size(barData));
for i = 1:numel(barData)
h(i) = bar(ax, i, barData(i), ...
'FaceColor', colors(i,:), ...
'DisplayName', names{i});
end
legend(h,'location','bestoutside')
xlim(ax, [0.25, numel(barData)+0.75])
box(ax,'on')

3 Comments

@Adam Danz, if you're going to build the separate bars to create the handles anyway, why don't you just forego the first and build them to begin with?
figure
hB=gobjects(numel(barData),1);
dumData=nan(size(barData)); % a place holder won't display
x=1:numel(barData); % x values to draw full-sized bar
for i = 1:numel(barData)
dumData(i)=barData(i); % the fixup show only one bar at time
hB(i)=bar(x, dumData,'FaceColor',bh.CData(i,:)); % now can specify 'FaceColor'
if i==1, hold on, end % hold on first doesn't set overall x limits
dumData(i)=nan; % put the NaN back for next iteration
end
legend(TickLabels,'Location','northwest')
produces
This way, you can get the default color order automagically by simply leaving out the 'FaceColor' parameter or use the named color abbreviations or whatever choose...
This adds some complexity to build the dummy data array at the slight advantage of being able to set 'FaceColor' instead of futzing with the internal CData array or the one-handle object. It's that extra effort that made me use the area subterfuge to get a set of handles drawn with patches that could do in "one swell foop" instead.
I STILL say any/all of these machinations are reflections on the ill-chosen design and UI for bar() itself that should directly addressable properties for every bar, property fields for applying labels, the values and so on directly.
You don't even need the padding. I'll update my answer since this is better than my second option.
y = [2 4 3 5 2]; % bar heights
colors = lines(numel(y));
names = {'Plovdiv','Varna','Burgas','Ruse','Haskovo'};
fig = figure();
ax = axes(fig);
hold(ax,'on')
h = gobjects(size(y));
for i = 1:numel(y)
h(i) = bar(ax, i, y(i), ...
'FaceColor', colors(i,:), ...
'DisplayName', names{i});
end
legend(h,'location','bestoutside')
xlim(ax, [0.25, numel(y)+0.75])
"You don't even need the padding."
But then you must manually fix up the axes limits as
xlim(ax, [0.25, numel(y)+0.75])
which IMO is more problematical to get right. That's the reason hold on wasn't done first as well plus using the x vector expicitly--when you only plot the one by itself, then you get ticks and xlimits that don't match those from bar() for the same number of bars done natively.
It's a mess...there's no really good solution given the limitations of the internals design.

Sign in to comment.

Categories

Products

Release

R2015a

Asked:

on 5 Aug 2022

Edited:

on 13 Nov 2022

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!