winkmal
winkmal

Reputation: 626

Get legend right with stacked bar plot

I would like to generate a plot which contains both lines (plot, stairs) and bars (bar). For plot and stairs, I usually use 'DisplayName' property to generate the legend. With a stacked bar plot, this does not seem to work anymore. Consider this MWE:

x_max = 20;
results = [3 37 50; 7 27 25; 11 0 13; 18 45 0];
figure('Position', [470 430 1000 600]);
plot(0:x_max, polyval([3 1], 0:x_max), 'DisplayName', 'Production rate');
hold on; grid on;
bh = bar(results(:,1), results(:,2:3), 0.2, 'stacked', 'DisplayName', 'Fraction');
xlim([0 x_max]);
legend('Location', 'best');
set(gca, 'FontSize', 18);
hold off

Which produces this plot: enter image description here

I would like to get a custom legend entry for each of the two fractions, e.g., 'Fraction1', 'Fraction2'. However, both variantes produce an error:

bar(results(:,1), results(:,2:3), 0.2, 'stacked', 'DisplayName', 'Fraction1', 'Fraction2')
bar(results(:,1), results(:,2:3), 0.2, 'stacked', 'DisplayName', {'Fraction1', 'Fraction2'})

>>Error setting property 'DisplayName' of class 'Bar':
Value must be a character vector or string scalar.

But if I do

bh.get('DisplayName')

I get

ans =

  2×1 cell array

    {'getcolumn(Fraction,1)'}
    {'getcolumn(Fraction,2)'}

Which means that Matlab internally does generate a cell array for the 'DisplayName', but does not let me assign one. This fails:

bh.set('DisplayName', {'Fraction1'; 'Fraction2'})

I know I could edit the cell array of legend entries directly, but I prefer the 'DisplayName', since the legend entries never get out of order when I change the plot commands (or add or delete any of them). Any solution?

Upvotes: 4

Views: 215

Answers (1)

Wolfie
Wolfie

Reputation: 30047

As a quick workaround, you could set each bar object's DisplayName after creation. See this solution which builds on your example:

The issue you're having is that the stacked bar creates a Bar array (in this case 1x2). You can't set the DisplayName property of the Bar array, you need to set the property of each Bar in the array.

% Your example code, without trying to set bar display names
x_max = 20;
results = [3 37 50; 7 27 25; 11 0 13; 18 45 0];
figure('Position', [470 430 1000 600]);
plot(0:x_max, polyval([3 1], 0:x_max), 'DisplayName', 'Production rate');
hold on; grid on;
bh = bar(results(:,1), results(:,2:3), 0.2, 'stacked');
xlim([0 x_max]);
legend('Location', 'best');
set(gca, 'FontSize', 18);
hold off

% Set bar names
names = {'Fraction1'; 'Fraction2'};
for n = 1:numel(names)
    set( bh(n), 'DisplayName', names{n} );
end

You can do this without the loop, at the cost of slightly less explicit syntax:

names = {'Fraction1'; 'Fraction2'};
[bh(:).DisplayName] = names{:};

plot

Upvotes: 3

Related Questions