8
$\begingroup$

I'm working on a Dynamic ListPlot with a few thousand plots, and it seems that wrapping the points with Tooltip slows the Dynamic responsiveness substantially. With the simple example below, dragging the Slider is sluggish. If you change the Show line from "plot1" to "plot2", the Slider response is smooth. Notice that the ListPlot and Tooltips are not being modified by the Control. Maybe Show is muddling things when it combines the graphics, but still, the slowdown doesn't make sense to me, since Tooltip isn't active during the drag, and isn't changing in any way.

x = Table[RandomVariate[NormalDistribution[0, .5], 2], {i, 5000}];
boxPts[x_] := {{x, x}, {-x, x}, {-x, -x}, {x, -x}};
plot1 = ListPlot[MapIndexed[Tooltip[#1, First[#2]] &, x]];
plot2 = ListPlot[x];
Control[{{z, .5}, 0, 1.5}]
overlay = Graphics[{Opacity[.2],
    FilledCurve[{{Line[boxPts[1.5]]}, {Dynamic[Line[boxPts[z]]]}}]}];
Show[overlay, plot1, PlotRange -> 1.5, AspectRatio -> 1]

Output of above code

I'd prefer to be able to use Tooltip, if it's fixable, or at least understand why it's so slow. I'd also be interested in any alternative methods for doing mouseover-style feedback, which could show up inside or outside the plot area. Thank you!

(This is in Mma 9.0.1 on OSX 10.8.5, on a 2012 MBP.)

Update: I noticed that if I execute the Control line after the Show line with plot1, the Slider works smoothly. I then noticed that merely moving the cursor away to a new point can make the Control work normally. This only makes a difference with the plot that has Tooltips, since the other is always smooth.

$\endgroup$
7
  • 2
    $\begingroup$ you haven't defined boxPts so this cannot be tested. $\endgroup$ May 19, 2014 at 4:32
  • $\begingroup$ Thanks, fixed it! Less thanks to whoever downvoted it. I did research it, and attempted to make it useful and clear. $\endgroup$
    – Joe Fusion
    May 19, 2014 at 4:54
  • $\begingroup$ I do not see it slowing down so much on my end. M 9.01, windows 7. Yes, the slider is tiny bit less smooth, but you have one plot with 5000 tooltips and another without? $\endgroup$
    – Nasser
    May 19, 2014 at 5:54
  • $\begingroup$ Mma 9.0.1 Mac 10.9.2 worked fine for me. I think you need to tell us what version of the operating system you are using. $\endgroup$ May 19, 2014 at 6:00
  • 1
    $\begingroup$ This is on 10.8.5, on a 2012 MBP. It appears to be a frontend UI quirk, based on the Update I added above. Having the cursor (text entry focus) next to the Tooltip'd output is slow. Move the cursor, or remove the Tooltips, and it's not slow. I don't think it's merely the number of objects (as @Nasser said), since I can move the cursor and see improvement, or have many more objects with Tooltips. For anyone not seeing the problem, you might try cranking up the number of points, to account for differences in hardware speed. $\endgroup$
    – Joe Fusion
    May 19, 2014 at 16:00

1 Answer 1

8
$\begingroup$

This is a very common problem for people who work on data analysis. Here as a solution to the problem using LocatorPane and a few other functions and tricks.

    TooltipListPlot[data_, tipFunction_, listPlotOptions___] := DynamicModule[
    {displayQ = False, yRange , xRange, pt, minX, maxX, minY, maxY, tip, threshold, tipPosition, nf, dataPoints, dataAsRulesQ = Head@data[[1]]===Rule}
,
    dataPoints := If[dataAsRulesQ, #[[1]]&/@data, data];
    nf = Nearest@data;
    {maxY, minY} = {Max[#], Min[#]} &@dataPoints[[All, 2]];
    {maxX, minX} = {Max[#], Min[#]} &@dataPoints[[All, 1]];
    pt = {maxX, maxY}*2;
    yRange = maxY - minY ;
    xRange = maxX - minX;
    tipPosition[point_] := {
        Which[point[[1]] < minX + 0.1 xRange,
            Left
        ,point[[1]] > maxX - 0.1 xRange,
            Right
        ,True,
            Center
        ]
    ,
        Which[point[[2]] < minY + 0.1 yRange,
            Bottom
        ,point[[2]] > maxY - 0.1 yRange, 
            Top
        ,True,
            Top
        ]
    };
    threshold = EuclideanDistance[{minX, minY}, {maxX, maxY}]/100.;
    LocatorPane[Dynamic[pt,
        (
        pt = #;
        tip = nf[pt, {1, threshold}];
        displayQ = tip =!= {};
        ) &
        ]
    ,
        Show[
            ListPlot[dataPoints, listPlotOptions]
        ,
            Graphics[{
                Dynamic@
                 If[displayQ, 
                    Text[
                        Framed[tipFunction@tip[[1]]
                        , Background -> Lighter@Lighter@Lighter@Yellow
                        , FrameStyle -> Directive[Opacity[0], White]
                        , FrameMargins -> 2
                        ]
                    , {pt[[1]], pt[[2]]}
                    , tipPosition[pt]
                    ]
                , {}
                ]
            }]
        ]
    , AutoAction -> True
    , Appearance -> None
    ]
]

This is a test example for 10,000 points.

data = Table[RandomVariate[NormalDistribution[0, .5], 2], {i, 10000}];
tipsFunc = 
  Labeled[Column[#, Frame -> True, FrameStyle -> Red], 
    Rotate[": )", -Pi/2]] &;
TooltipListPlot[data, tipsFunc, ImageSize -> 400, Frame -> True, 
 Axes -> False]

And this is a snapshot of the result. enter image description here

Note that with the latest imrpovement it is now also possible to input data as rules which in some cases may be more appropriate for creating the tooltips. Following is a simple example.

dataAsRules=Table[RandomVariate[NormalDistribution[0,.5], 2]->StringJoin["my tooltip info ",ToString[i]],{i,10000}];
tipsFunc = #&;
TooltipListPlot[dataAsRules,tipsFunc, Frame->True, Axes->False,ImageSize->400]
$\endgroup$
5
  • $\begingroup$ It is much more responsive if you use Nearest[data] to construct a NearestFunction and then call this within Dynamic. I am editing your otherwise-fine answer with this improvement. (and +1 of course) $\endgroup$
    – Mr.Wizard
    Aug 1, 2014 at 17:38
  • $\begingroup$ I had it originally implemented as you are suggesting as I had the same idea of making the search process as fast as possible. However, the Nearest function takes some precious evaluation time before rendering the plot, plus, it also adds more information to save inside the DynamicModule which also is against initial responsiveness. For these reasons I'm not sure that it is better to step back to the precomputation of Nearest. Indeed, the decision should be left to the user. For using the function inside user interfaces I would not precompute. For a single use I would. Thanks for the +1! $\endgroup$ Aug 1, 2014 at 17:47
  • $\begingroup$ I do not observe a significant delay in the display using Nearest[data] but I did notice the lag without it. In fact I doubt that there is a case where it could be significant without the lag being much more so. Nevertheless I will be glad to refrain from making such edits to your posts in the future if you wish. $\endgroup$
    – Mr.Wizard
    Aug 1, 2014 at 18:05
  • $\begingroup$ I just tested again with the Nearest precomputation and I agree that the speed is basically the same. I'm not sure what may have caused the delay in the past. In any case, your modifications are welcome and I appreciate them. $\endgroup$ Aug 1, 2014 at 18:22
  • $\begingroup$ Alright, and you're welcome. :-) $\endgroup$
    – Mr.Wizard
    Aug 1, 2014 at 18:25

Your Answer

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

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