holoviz / holoviews

With Holoviews, your data visualizes itself.
https://holoviews.org
BSD 3-Clause "New" or "Revised" License
2.69k stars 402 forks source link

Easy specification of dimensions for hover tool #1816

Closed jlstevens closed 5 months ago

jlstevens commented 7 years ago

Instead of building an explicit hover tool to choose dimensions to show hover information for, maybe we could have a syntax such as:

tools=['hover+time|voltage']

Where + means you are building a whitelist (what to include) and - means it is a blacklist (what to exclude). E.g to exclude 'time' and 'voltage':

tools=['hover-time|voltage']
jbednar commented 7 years ago

Not sure whether I'm convinced one way or another, but I would guess a flat syntax like 'hover+time+voltage-temperature' would be simpler to work with.

philippjfr commented 7 years ago

Definitely don't like using some special syntax inside a string like that. I'd be okay with a tooltips plot option, which accepts a list of dimensions.

jlstevens commented 7 years ago

What would tooltips do if there is no hover tool? Would it complain, silently ignore the issue or enable the hover tool if not already activated?

I don't thinks tooltips is the right name (these aren't tips for using tools) but maybe something like hoverdims or hoverinfo would be ok? Maybe just call the option hover?

philippjfr commented 7 years ago

What would tooltips do if there is no hover tool?

It would enable the hover tool.

I don't thinks tooltips is the right name (these aren't tips for using tools) but maybe something like hoverdims or hoverinfo would be ok?

Tooltips is the right name imo, here's the definition of tooltips:

a message which appears when a cursor is positioned over an icon, image, hyperlink, or other element in a graphical user interface.

jlstevens commented 7 years ago

image

The key bit for me is the last point - it is normally about describing function and not giving data values. @jbednar's opinion might help us with a better suggestion and I don't mind tooltips too much if both of you don't think it is confusing.

philippjfr commented 7 years ago

tooltips is also what bokeh calls it, so it would also be consistent.

jlstevens commented 7 years ago

That is a good argument for calling it tooltips then.

jbednar commented 7 years ago

Don't ask me; I've always thought tooltips was a ridiculous and annoying name, and much prefer hover.

jlstevens commented 7 years ago

I also like hover more than tooltips. This might be one of those times where we decide we chose a better name than bokeh. :-)

kako-f commented 6 years ago

So, what was the result of this? that sintaxis proposed in the OP works? thanks for your answers đź‘Ť

jbednar commented 3 years ago

We never have made progress on this issue, and it remains a major sticking point, particularly for hvPlot users, because the Bokeh syntax for creating a custom HoverTool is miles away from the concise and convenient syntax that hvPlot and HoloViews users expect.

Basically, you can have a nice, clean notebook about your work only if you don't care what shows up in the hover tool, i.e. if what it does by default is precisely what you wanted it to do. In my experience it's rare that what it shows by default is ideal, yet I still rarely modify that default in practice, because the syntax for specifying hover options is so verbose, awkward, and distracting from the story I am trying to tell in a given notebook. Hover is useful, but it's rarely the main story, yet configuring it immediately takes over the notebook and becomes a complicated digression that I usually choose not to get into.

We don't necessarily need a DSL as proposed above, but I do think we should support some simple list syntax for specifying what fields to include in the hover without needing to import HoverTool.

jlstevens commented 3 years ago

I think a mini-DSL as proposed above fits in best with the existing plot options and would be the shortest, most concise way to do this. Unfortunately, specifying a hood hover tool is verbose and awkward as you point out and I can't think of a more concise (and backwards compatible) way to do this. I guess you could try something like:

tools=[{'hover':['col1', 'col2']}, 'pan']

But that is hardly an improvement or any clearer imho.

jbednar commented 3 years ago

I can't quite grok that syntax; seems like a tuple would work better than a dictionary there:

tools=['pan', ('hover', ['col1', 'col2'])]

And to extend that to cover the full tooltips argument of the Bokeh HoverTool when needed:

tools=['pan', ('hover',  dict(col1='@name',  col2='@symbol',  col3='$color[hex, swatch]:CPK')]

But just like @philippjfr above, I was imagining a separate option like hover_cols=['col1','col2'] (or tooltips, if anyone likes that name!) and the equivalent dictionary version hover_cols=dict(col1='@name', col2='@symbol', col3='$color[hex, swatch]:CPK'). Not sure whether the separate option or parsing tools is easier to implement, but either of these seems like a big improvement over importing HoverTool and configuring it.

jlstevens commented 3 years ago

That final suggestion seems pretty reasonable to me. The only think I would like is for there to be a clear mechanism that maps a string (e.g. 'pan', 'wheelzoom', 'hover') to a Bokeh tool model or the tuple format (e.g. ('hover', ['col1', 'col2']) which should also support the HTML template). I'm not sure what would be customized for other tools but I would hope to make this mechanism general for all the tools...

jbednar commented 3 years ago

Sounds like we could support a general mechanism for Bokeh tools where we generalize our tools parameter to accept tuples, i.e. something like tools=['tool1', ('tool2', settings2), ('tool3', settings3)]. To implement that per tool, we'd register a handler for that tool that maps the given settings into configuration for that tool. For hover, what the handler would do is accept a string (i.e., HTML template), list (i.e., list of dimensions), or dict (i.e., list of dimension:formatter pairs) specification, and would map that into the tooltips argument of HoverTool. Other tools could be configured similarly. Something for you to attack, @jlstevens ?

droumis commented 5 months ago

Would be great to revisit this. still a major sticking point.

Especially if a user can also specify whether to include their 'label' and 'group' arg value as a hover/tooltip entry.. for instance: https://github.com/holoviz/holoviews/issues/3603

ahuang11 commented 5 months ago

Wanted to drop some feature ideas:

tools=[{"hover": {[
    ("index", "$index"),
    ("(x,y)", "(x=$x{0[.]00000}, y=$y{0[.]00000})"),
]}}]

Questions:

  1. Since bokeh tooltip formatting is different from f-strings, I'm wondering how to reconcile that; do we support both, or just Bokeh style? Leaning towards just bokeh style to keep it simple
  2. Should we go with nested dict/list/tuple or should we go for separate opts, e.g. (hover_format)
jlstevens commented 5 months ago

Over time, I have become even more hesitant to support these nested structures and I think I've come to the conclusion that we should leave tools alone and support hover_X options instead e.g:

hover_labels={"index": "Index", "x": "X-Coordinate"},
hover_format=["{index.0f}", "{x:.1f}", {"y:.1f}"]

I think that hover_format could support a dictionary to support the bokeh tooltip kwargs: this is still better than having to import the model from Bokeh (and of course, you can still supply the model directly in tools if you want).

droumis commented 5 months ago

@jlstevens could you expand on why you've become l hesitant to support the nested approach?

jlstevens commented 5 months ago

These nested representations are often opaque, hard to manipulate correctly and brittle (more chance for syntax errors among other things).

jbednar commented 5 months ago

Neither the nested nor the separate keywords are particularly discoverable ("how was I supposed to know there is a separate hover_cols keyword when I want to configure this tool?" vs "what on earth is that obscure syntax supposed to indicate"), so in the end I'm not sure I have much of an opinion other than that I just want something providing control over this behavior without needing a separate tool and separate import. That way it can start working its way into our examples and people will be able to learn about either the separate keywords or about the fancy syntax. So I'd say we should have a quick meeting about it, make a prototype, pass it around to see if it horrifies anyone, and go with it; it's better than dithering for more years!

ahuang11 commented 5 months ago

A nested dict is indeed hard to work with, and since most people agreed that tile_opts would be the better option in https://github.com/holoviz/hvplot/pull/1299, I think I'll just start going with separate keywords like

hover_labels={"index": "Index", "x": "X-Coordinate"},
hover_format=["{index.0f}", "{x:.1f}", {"y:.1f}"]

However, I think both of these should be a list potentially? I'll have to try to get a better idea

ahuang11 commented 5 months ago

I propose these three new params:


    hover_tooltips = param.ClassSelector(class_=(str, list), default=[], doc="""
        A list of strings to be displayed in the hover tooltip.
        Or a list of tuples can be provided to also additionally specify the
        label and value format. Lastly, a string can be provided if a custom
        HTML template is provided.
        """)

    hover_formatters = param.List(default=[], doc="""
        A list of formatting options for the hover tooltips.""")

    hover_mode = param.ObjectSelector(default='mouse', objects=['mouse', 'vline', 'hline'], doc="""
        The hover mode determines how the hover tool is activated.""")
image

To not deviate too much from Bokeh, it should mirror what Bokeh has:

hover_tooltips=[
        ( 'date',   '@date{%F}'            ),
        ( 'close',  '$@{adj close}{%0.2f}' ), # use @{ } for field names with spaces
        ( 'volume', '@volume{0.00 a}'      ),
    ],
hover_formatters={
        '@date'        : 'datetime', # use 'datetime' formatter for '@date' field
        '@{adj close}' : 'printf',   # use 'printf' formatter for '@{adj close}' field
                                     # use default 'numeral' formatter for other fields
},

    hover_mode='vline'

I believe this will be extremely convenient for users to have it broken up like this. Previously, users would have to import and completely build the HoverTool if they wanted to change one thing about the hover tooltips; now they just have to change one of these params.

I'd like to also propose supporting a list of strings in hover_tooltips:

hover_tooltips=["date", "close"]

And potentially a list of Python string formats(?, will decide if this is easy to implement; I think yes?)

hover_tooltips=["{date:%Y%m%d%H}", "{close:.02f}"]

In case users don't want to specify the format.

jlstevens commented 5 months ago

I like @ahuang11's suggestion: we can point to Bokeh's API and all we are doing is removing a level of nesting and an import when creating a hovertool (at least regarding the keywords and their basic functionality...we can also add some sugar if it is useful).

philippjfr commented 5 months ago

I'm +1 as well. Python string formats sound nice, but in practice may not be straightforward.

ahuang11 commented 5 months ago

Okay I started implementing it in https://github.com/holoviz/holoviews/pull/6180. As I understand, there's a lot of places that rebuild HoverTool so lots more work to add, but wanted to solicit initial feedback first before continuing.

A couple things added to the proposal

  1. hover_tooltips not only supports a list of strings, but also can support a list of a mix of strings AND tuples, giving the user extra flexibility if they want to format only a specific key (rather than tediously putting all tuples), e.g.
hover_tooltips=["x", ("Dollars", "$@{y}{0.2f}")]

VS

hover_tooltips=[("x", "@x"), ("Dollars", "$@{y}{0.2f}")]
  1. To use group and label in the hover, I added special support for @hv_label / @{hv_label} and @hv_group / @{hv_group}--basically a find and replace.

  2. If hover not in tools, but hover_tooltips is defined, then automatically add hover to tools.

https://github.com/holoviz/holoviews/assets/15331990/ca6124f0-719f-4c56-9dff-ab277332b281

In the PR above, I don't think I'll be supporting converting Python formats to Bokeh; perhaps in the future.

hover_tooltips=["{date:%Y%m%d%H}", "{close:.02f}"]

philippjfr commented 5 months ago

To use group and label in the hover, I added special support for @hv_label / @{hv_label} and @hv_group / @{hv_group}--basically a find and replace.

Don't much like the naming of these. For one the hv_ prefix is weird and secondly the @ syntax specifically means "look up the value on the data", which isn't the case here.

ahuang11 commented 5 months ago

Field names that begin with $ are “special fields”. These often correspond to values that are part of the plot, such as the coordinates of the mouse in data or screen space. These special fields are listed here:

So $label and $group? I guess that would work; if user wants to point to value in their data, @label

jlstevens commented 5 months ago

I suppose semantically that makes sense although there will now be special fields defined by bokeh as well as some additional ones defined by holoviews. Not too terrible I suppose!

ahuang11 commented 5 months ago

Okay, I think https://github.com/holoviz/holoviews/pull/6180 is in a pretty good state now for review.

See the new docs under Hover tools on usage, starting at to replicate the behavior above. (repeat of what was already described here) https://github.com/holoviz/holoviews/blob/00bc0560de4240df2a7f96b2bdea597ee040dec8/examples/user_guide/Plotting_with_Bokeh.ipynb

The only thing missing are tests now I think.