How to annotate multiple datasets in ListPlots

前端 未结 3 1762
时光取名叫无心
时光取名叫无心 2020-12-16 02:23

Frequently I have to visualize multiple datasets simultaneously, usually in ListPlot or its Log-companions. Since the number of datasets is usually larger than the number of

3条回答
  •  醉酒成梦
    2020-12-16 03:24

    You could try something along these lines. Make each line into a button which, when clicked, identifies itself.

    plot=Plot[{Sin[x],Cos[x]},{x,0,2*Pi}];
    sinline=plot[[1,1,3,2]];
    cosline=plot[[1,1,4,2]];
    message="";
    altplot=Append[plot,PlotLabel->Dynamic[message]];
    altplot[[1,1,3,2]]=Button[sinline,message="Clicked on the Sin line"];
    altplot[[1,1,4,2]]=Button[cosline,message="Clicked on the Cos line"];
    altplot
    

    If you add an EventHandler you can get the location where you clicked and add an Inset with the relevant positioned label to the plot. Wrap the plot in a Dynamic so it updates itself after each button click. It works fine.

    In response to comments, here is a fuller version:

    plot = Plot[{Sin[x], Cos[x]}, {x, 0, 2*Pi}];
    sinline = plot[[1, 1, 3, 2]];
    cosline = plot[[1, 1, 4, 2]];
    AddLabel[label_] := (AppendTo[plot[[1]],
        Inset[Framed[label, Background -> White], pt]];
       (* Remove buttons for final plot *)
       plainplot = plot;
       plainplot[[1, 1, 3, 2]] = plainplot[[1, 1, 3, 2, 1]];
       plainplot[[1, 1, 4, 2]] = plainplot[[1, 1, 4, 2, 1]]);
    plot[[1, 1, 3, 2]] = Button[sinline, AddLabel["Sin"]];
    plot[[1, 1, 4, 2]] = Button[cosline, AddLabel["Cos"]];
    Dynamic[EventHandler[plot,
      "MouseDown" :> (pt = MousePosition["Graphics"])]]
    

    To add a label click on the line. The final annotated chart, set to 'plainplot', is printable and copyable, and contains no dynamic elements.

    [Later in the day] Another version, this time generic, and based on the initial chart. (With parts of Mark McClure's solution used.) For different plots 'ff' and 'spec' can be edited as desired.

    ff = {Sin, Cos, Tan, Cot};
    spec = Range[0.1, 10, 0.1];
    (* Plot functions separately to obtain line counts *)
    plots = Array[ListLinePlot[ff[[#]] /@ spec] &, Length@ff];
    plots = DeleteCases[plots, Line[_?(Length[#] < 3 &)], Infinity];
    numlines = Array[Length@Cases[plots[[#]], Line[_], Infinity] &,
       Length@ff];
    (* Plot functions together for annotation plot *)
    plot = ListLinePlot[#@spec & /@ ff];
    plot = DeleteCases[plot, Line[_?(Length[#] < 3 &)], Infinity];
    lbl = Flatten@Array[ConstantArray[ToString@ff[[#]],
          numlines[[#]]] &, Length@ff];
    (* Line positions to substitute with buttons *)
    linepos = Position[plot, Line, Infinity];
    Clear[line];
    (* Copy all the lines to line[n] *)
    Array[(line[#] = plot[[Sequence @@ Most@linepos[[#]]]]) &,
      Total@numlines];
    (* Button function *)
    AddLabel[label_] := (AppendTo[plot[[1]],
        Inset[Framed[label, Background -> White], pt]];
       (* Remove buttons for final plain plot *)
       plainplot = plot;
       bpos = Position[plainplot, Button, Infinity];
       Array[(plainplot[[Sequence @@ Most@bpos[[#]]]] =
           plainplot[[Sequence @@ Append[Most@bpos[[#]], 1]]]) &,
        Length@bpos]);
    (* Substitute all the lines with line buttons *)
    Array[(plot[[Sequence @@ Most@linepos[[#]]]] = Button[line[#],
          AddLabel[lbl[[#]]]]) &, Total@numlines];
    Dynamic[EventHandler[plot,
      "MouseDown" :> (pt = MousePosition["Graphics"])]]
    

    Here's how it looks. After annotation the plain graphics object can be found set to the 'plainplot' variable.

    Annotated Chart

提交回复
热议问题