single legend entry for all lines of plotted array?

34 views (last 30 days)
Here is what I am trying to do: I have a matrix of spectra (wpsec.spec, ns x nv where ns=number of spectra) and would like to plot as two different types of lines along with a few other lines of information.
> plot(1:nv,wspec.spec(gout,:),'b',1:nv,wspec.spec(~gout,:),':k',1:nv,cent,'g',1:nv,low,'r',1:nv,up,'r');
plots all the spectra identified by logical index "gout" as blue lines ('b') and all other spectra as dotted black lines (':k') along with a green line for "cent" (1 x nv), and red lines for "low" and "up" (both 1 x nv). This works ok to produce the plot. Now I want to add a legend with 4 entries ("outliers","normal","central value" and "thresholds") as
> legend('outliers','normal','central value','thresholds')
Of course this doesn't work -- it will just put the labels on the first 5 blue lines of "wspec.spec(gout,:)" , assuming that nnz(gout)>5. What is needed is to "group" all the blue lines under a single legend entry. But you cannot just:
> p(1) = plot(1:nv,wspec.spec(~gout,:),'b','DisplayName','outliers');
since we get an error "Subscripted assignment dimension mismatch." as it wants to apply 'DisplayName' to all nnz(gout) blue lines. It would be nice if the 'DisplayName' property had this flexibility to automatically expand 'outliers' to apply to all the lines...sigh.
So, what is the best way to accomplish what I want? I know I could plot just the first line of each multiline array, generate the legend, and then plot the remaining lines of each array:
> p(1) = plot(1:nv,wspec.spec(find(gout,1,'first'),:),'b','DisplayName','outliers');
> hold on;
> p(2) = plot(1:nv,wspec.spec(find(~gout,1,'first'),:),':k','DisplayName','normal');
> p(3) = plot(1:nv,cent,'g','DisplayName','Central Value');
> p(4) = plot(1:nv,low,'r','DisplayName','Threshold');
> p(5) = plot(1:nv,up,'r');
> p(6) = plot(1:nv,wspec.spec(~gout,:),':k'); %results in duplicate plotting of first line
> p(7) = plot(1:nv,wspec.spec(gout,:),'b'); %results in duplicate plotting of first line
> hold off;
> legend(p(1:4));
but this seems un-tidy and has other problems with layering lines on top of other lines depending on the order in the plot commands. With all the changes in the graphics engine structure, I am wondering if there isn't already a way to associate legend entries to a "container" of plot lines.
  3 Comments
Dave
Dave on 27 Jan 2018
Actually, I was referring to either HG1 or HG2 -- I come from the "olden days" of Matlab before either one...never really caught up, lol.
You are most likely correct that a solution would be to expand out the "DisplayName" to match the dimensions of the number of lines plotted. I just was looking for a way to not add all that reduncency when I am really just trying to get one legend entry for all spectra (lines) in a plot group. Now if there was a way to set "DisplayName" to only the first line plotted in the group and leave the rest unset, then it may be useful. I cannot see a way to specify that other than a separate "plot" call for each line -- and that really is inefficient when each group might have hundreds, or thousands, of lines plotted.
It seems like a common enough need in these days of large data, but I guess there isn't a compact solution without customized code for each plot generated.
Dave
Dave on 27 Jan 2018
I am reposting the example code with some more comments...hopefully that will make it clearer what I want to do.
> % this line just plots the first line of the group defined by "gout"
% logical index. This single line gets a "DisplayName" defined so
% it should generate a legend entry
> p(1) = plot(1:nv,wspec.spec(find(gout,1,'first'),:),'b','DisplayName','outliers');
> hold on;
> % this line plots the first line of the second group defined by "~gout"
% logical index. This single line also gets a "DisplayName" defined
> p(2) = plot(1:nv,wspec.spec(find(~gout,1,'first'),:),':k','DisplayName','normal');
> % the next 2 lines plot the "central value" line and the "lower threshold" --
> % both get a "DisplayName" defined.
> p(3) = plot(1:nv,cent,'g','DisplayName','Central Value');
> p(4) = plot(1:nv,low,'r','DisplayName','Threshold');
> % next line plots the "upper threshold", but it does not get a "DisplayName"
% defined so it should "share" legend entry with the previous threshold
% line
> p(5) = plot(1:nv,up,'r'); %no "DisplayName" so no legend entry
> % next we plot the rest of the second group (~gout index) lines -- being
% lazy as it also re-plots the first line plotted in "p(2)" above.
% This time "DisplayName" is not defined so there should be no legend entries
% for these lines
> p(6) = plot(1:nv,wspec.spec(~gout,:),':k'); %no legend entry
> % lastly, we plot the rest of the first (gout index) group lines (first
> % line from "p(1)" above gets re-plotted), but no "DisplayName" defined
> p(7) = plot(1:nv,wspec.spec(gout,:),'b'); %no legend entry
> hold off;
> legend(p(1:4)); %legend generated from subset of plots. It may be
%enough to just use legend('show'), but I do not
%think it hurts to restrict to the first 4 plots
%where "legend()" looks for entries.
Most of the time I do not bother with setting the "DisplayName" for those lines I want legend entries for -- I just rely on the order of single line plots and just supply legend() the strings I want entries for.
Question: if "DisplayName" is not defined, is that enough to eliminate a legend entry being generated for that line? Or do I also need to set the annotation property for lines (plots) I do not want included?
> set( get( get( p(5), 'Annotation'), 'LegendInformation' ), 'IconDisplayStyle', 'off' );
> set( get( get( p(6), 'Annotation'), 'LegendInformation' ), 'IconDisplayStyle', 'off' );
> set( get( get( p(7), 'Annotation'), 'LegendInformation' ), 'IconDisplayStyle', 'off' );
Thanks

Sign in to comment.

Accepted Answer

Star Strider
Star Strider on 27 Jan 2018
I cannot run your code. Since you have 'DisplayName' defined in every plot call:
p(1) = plot(1:nv,wspec.spec(~gout,:),'b','DisplayName','outliers');
you most likely need only to use the 'show' argument in your legend call:
legend('show')
See if that works.
  6 Comments
Dave
Dave on 27 Jan 2018
The solution given by Star Strider as his "Another Option -- " works well for my needs. It is simple and requires little additional effort. I am guessing that when we index into the plot handle [i.e., h1(1) or h2(1)] we are accessing the individual Line handles? Cool. Where is this sort of thing documented? Is there a decent HG2 tutorial on-line to learn more? I think I need a little more hand-holding, or explanation, than the Matlab Help provides.
Thanks everyone.
Star Strider
Star Strider on 27 Jan 2018
As always, my pleasure.
‘I am guessing that when we index into the plot handle [i.e., h1(1) or h2(1)] we are accessing the individual Line handles?’
You are guessing correctly. In my ‘Another Option’ code, you will see that ‘h1’ and ‘h2’ are handles to several (in my code, 5) line objects. Using them without the subscript in the legend call produces the result you don’t want. Choosing only one produces the result you want.
The documentation for the line handles is in the legend section on Specify Charts to Include in Legend (link).
With respect to HG2, one source is Graphics Changes in R2014b (link). There are more discussions on the Graphics (link) page.

Sign in to comment.

More Answers (0)

Categories

Find more on Data Distribution Plots in Help Center and File Exchange

Community Treasure Hunt

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

Start Hunting!