5
$\begingroup$

My question is how do I get dynamic legends (legends that turn data on/off) to work with Show? This is an extension of an old question here;

How to dynamically toggle curves on/off in a crowded Plot?

The application I have is to show data using a histogram and overlay many different distributions on top of it. Here is the code

randData = RandomVariate[NormalDistribution[], 10^3];
estDists = 
  EstimatedDistribution[randData, #] & /@ {NormalDistribution[mu, 
     sigma], StudentTDistribution[mu, sigma, 5]};
pdfs = PDF[#, x] & /@ estDists;
histPlot = Histogram[randData, Automatic, "ProbabilityDensity"];
distPlots = 
  Plot[pdfs, {x, Min[randData], Max[randData]}, 
   PlotLegends -> LineLegend[estDists]];
Show[{histPlot, distPlots}]

enter image description here

Now that's fine if you have 2 or 3 fitted distributions but if you have 10 it gets a little crowded. I found a very nice solution to this (see link above) that lets you click on the legend to toggle the curves on/off. I'll repeat the code here;

 (*Don't allow the Plot to evaluate yet*)
Attributes[DynamicLegend] = {HoldAll};

DynamicLegend[(plotfnc_)[f_, args___, (opts___)?OptionQ]] := 
 Module[{plot},(*evaluate the plot*)plot = plotfnc @@ {f, args, opts};
  (*if it does not have a legend,there is nothing to do*)
  If[! MatchQ[plot, _Legended], Return[plot]];
  DynamicModule[{(*get the directives and legend_text from the plot*)
    leg = plot[[2, 1]],(*by default,
    all plots are visible (not transparent)*)
    trans = Table[False, {Length[plot[[2, 1, 1, 1]]]}]}, 
   Dynamic[(*dynamically re-
    evaluate the plot with dynamic PlotStyles and PlotLegends*)
    plotfnc @@ {f, 
      args,(*prepend the directives with Transparent on dynamic \
switches*)PlotStyle -> 
       Table[With[{i = i}, 
         Flatten[Directive[
           If[TrueQ[trans[[i]]], Transparent, Directive[]], 
           leg[[1, 1, i]]], 1, Directive]], {i, 
         Length[leg[[1, 
           1]]]}],(*turn the legend_text's into Togglers and shove \
them back into the*)(*LineLegend structure.Also,
      delete the old directives in the*)(*LineLegend structure to \
force PlotLegends to pick up the new*)(*directives,
      so legend lines turn on/off with the curves*)
      PlotLegends -> 
       ReplacePart[leg, 
        Join[{{1, 1} -> Sequence[]}, 
         Table[With[{i = i}, {1, 2, i} -> 
            Toggler[
             Dynamic[trans[[i]]], {True -> leg[[1, 2, i]], 
              False -> leg[[1, 2, i]]}, FrameMargins -> 0]], {i, 
           Length[leg[[1, 2]]]}]]], 
      FilterRules[{opts}, 
       Except[{PlotStyle, 
         PlotLegends}]]},(*only need dynamic updates when trans is \
modified*)TrackedSymbols :> {trans}]]]

And can use it for a single plot

dynamicDistPlots = DynamicLegend[Plot[pdfs, {x, Min[randData], Max[randData]}, 
PlotLegends -> LineLegend[estDists]]]

But can't get it to work with the histogram

Show[{histPlot, dynamicDistPlots}]

I've had a play with applying the DynamicLegend to the Show and even tried to modify the DynamicLegend function (although I think that was a bit over by head). Any help would be appreciated as this would be a great tool to compare distributions.

$\endgroup$
2
  • $\begingroup$ The problem is that dynamicDistPlots has Head DynamicModule and not Graphics. I would recommend allowing another input into dynamicDistPlots function which will allow you to pass in another graphics object directly. $\endgroup$ Commented May 30, 2013 at 1:42
  • $\begingroup$ Thanks Jonathan. So if I add another input for DynamicLegend which contains the histogram I can do the overlaying in that function. Seems to make sense, I'll try it out. $\endgroup$
    – Cam
    Commented May 30, 2013 at 5:14

1 Answer 1

4
$\begingroup$

Will this work?

 DynamicModule[{x1 = True, x2 = True},
 randData = RandomVariate[NormalDistribution[], 10^3];
 estDists = 
  EstimatedDistribution[randData, #] & /@ {NormalDistribution[mu, 
     sigma], StudentTDistribution[mu, sigma, 5]};
 pdfs = PDF[#, x] & /@ estDists;
 histPlot = Histogram[randData, Automatic, "ProbabilityDensity"];
 Dynamic[distPlots = 
   Plot[pdfs, {x, Min[randData], Max[randData]}, 
    PlotStyle -> {If[x1, ColorData[1, 1], Opacity[0.1]], 
      If[x2, ColorData[1, 2], Opacity[0.1]]}, 
    PlotLegends -> {Button[estDists[[1]], x1 = ! x1], 
      Button[estDists[[2]], x2 = ! x2]}];
  Show[{histPlot, distPlots}]
  ]
 ]

Adding Appearance->None inside Button can make them more like just strings.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.