How do I get a "tabular" legend?

57 views (last 30 days)
820408
820408 on 10 May 2017
Answered: Steven Lord on 23 Mar 2018
In MatlabR2015b, I am plotting a 2D scatter plot of "Performance" that is a function of two independent variables "Active agent" & "Temp". I have 3 Active Agents and 4 Temps. In scatter plot, I am using a 12 colors, one for each (ActiveAgent,Temp) pair. I want to customize legend so that it appears in a tabular form like (C1 would be like marker 'o' in its respective colors)
Temp1 Temp2 Temp3 Temp4
Agent1 C1 C2 C3 C4
Agent2 C5 C6 C7 C8
Agent3 C9 C10 C11 C12
How can I do this?

Accepted Answer

Kelly Kearney
Kelly Kearney on 10 May 2017
You can hack your way to a solution with my legendflex function. It's designed to make gridded-style legends like this, though unfortunately doesn't include the row/column label function yet (I'll keep that in mind for an enhancement, though... shouldn't be too hard to add).
Scatter only creates a single object to label, so to get individually-colored marker for a legend, you can create some invisible line objects:
% Data
nrow = 3;
ncol = 4;
ngrp = nrow*ncol;
x = rand(100,1);
y = rand(100,1);
group = ceil(rand(100,1)*ngrp);
datalabel = strtrim(cellstr(num2str((1:ngrp)', 'C%d')));
rowlabel = strtrim(cellstr(num2str((1:nrow)', 'Agent%d')));
collabel = strtrim(cellstr(num2str((1:ncol)', 'Temp%d')));
% Plot
scatter(x,y,[],group);
cmap = parula(ngrp);
colormap(cmap);
set(gca, 'clim', [0 ngrp] + 0.5, 'ylim', [-0.5 1.5]);
hln = line(nan(2,ngrp), nan(2, ngrp), 'marker', 'o', 'linestyle', 'none');
set(hln, {'color'}, num2cell(cmap,2));
% A regular legend to show the hidden-marker idea
hplain = legend(hln(:), datalabel(:));
export_fig('leggrid1', '-png');
delete(hplain);
There are two ways I can think of to get the grid layout. One is to add extra no-marker objects for both the row and column labels. The second is to add extra markers just for the row labels, and create multiple legends with legendflex's title property for the column labels. The latter centers the column titles over the label and object together, which I prefer, but it means you have to keep the box off (which may or may not work for your data):
% Option 1: add extra objects for row/column labels
hr = line(nan(2,nrow), nan(2,nrow), 'linestyle', 'none');
hc = line(nan(2,ncol+1), nan(2,ncol+1), 'linestyle', 'none');
allobj = [hc'; [hr reshape(hln,nrow,ncol)]];
alllbl = ['-' collabel'; [rowlabel reshape(datalabel,nrow,ncol)]];
hleg1 = legendflex(allobj(:), alllbl(:), 'nrow', nrow+1, 'xscale', 0.5);
% Option 2, multiple legends with titles
allobj = [hr reshape(hln,nrow,ncol)];
alllbl = [rowlabel reshape(datalabel,nrow,ncol)];
ttl = ['-' collabel'];
for ii = (ncol+1):-1:1
if ii == (ncol+1)
hleg2(ii) = legendflex(allobj(:,end), alllbl(:,end), 'ref', gca, ...
'anchor', {'se','se'}, 'buffer', [-10 10], 'xscale', 0.5, ...
'box', 'off', 'title', collabel{end});
else
hleg2(ii) = legendflex(allobj(:,ii), alllbl(:,ii), 'ref', hleg2(ii+1), ...
'anchor', {'sw','se'}, 'buffer', [0 0], 'xscale', 0.5, ...
'box', 'off', 'title', ttl{ii});
end
end
  3 Comments
Kelly Kearney
Kelly Kearney on 10 May 2017
That makes things a little more complicated with legendflex, since it can't handle completely blank labels (hence the little dash I added to the corner). For just straight symbols, I would probably forgo a legend altogether and just manually build a legend-like axis:
% Data
nrow = 3;
ncol = 4;
ngrp = nrow*ncol;
x = rand(100,1);
y = rand(100,1);
group = ceil(rand(100,1)*ngrp);
datalabel = strtrim(cellstr(num2str((1:ngrp)', 'C%d')));
rowlabel = strtrim(cellstr(num2str((1:nrow)', 'Agent%d')));
collabel = strtrim(cellstr(num2str((1:ncol)', 'Temp%d')));
% Plot
scatter(x,y,[],group);
cmap = parula(ngrp);
colormap(cmap);
set(gca, 'clim', [0 ngrp] + 0.5, 'ylim', [-0.5 1.5]);
% "Legend"
[ypt, xpt] = ndgrid(1:nrow, 1:ncol);
pos = get(gca, 'position');
sz = [0.4 0.2];
legax = axes('position', [pos(1)+pos(3)-sz(1) pos(2)+pos(4)-sz(2) sz], ...
'xlim', [-1 ncol+1], 'ylim', [-1 nrow+1], 'ydir', 'reverse', ...
'xtick', [], 'ytick', [], 'box', 'on');
hold on;
scatter(xpt(:), ypt(:), [], 1:ngrp);
text(1:ncol, zeros(1,ncol), collabel, 'horiz', 'center');
text(ones(1,nrow)*0.5, 1:nrow, rowlabel, 'horiz', 'right');
820408
820408 on 11 May 2017
Thank you. This is what I was looking for.

Sign in to comment.

More Answers (2)

Shane L
Shane L on 23 Mar 2018
If you are looking for a code that is built to create a "tabular" legend, my GridLegend function on the File Exchange will create a "legend" (actually a legend-like axis) with row and column labels. My GridLegend function is not as versatile as Kelly's legendflex function, but it will give you a "tabular" legend with no additional work on the user's end.

Steven Lord
Steven Lord on 23 Mar 2018
In release R2018a, you can now control how many columns your legend has.
% Generate data and set up the axes
x = linspace(0, 2*pi, 500);
axis([0 2*pi -8 8]);
hold on
% Plot 8 sine curves, naming each n*sin(n*x) for legend purposes
for n = 1:8
plot(x, n.*sin(n*x), 'DisplayName', sprintf('%d*sin(%d*x)', n, n));
end
% Turn on the legend
L = legend('show');
% Make it 2-by-4 instead of 8-by-1
L.NumColumns = 4;
This isn't exactly what the original poster asked for, but it's close.

Tags

Community Treasure Hunt

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

Start Hunting!