1

I would like to create a categorial scatter plot with these functions using bokeh:

  1. Hover Tooltips

  2. Legend outside the plot area with click policy = "hide"

After numerous searches, I can implement the function #1 and #2 separately.

But I don't know how to make these 2 functions work at the same time.

Tooltips only needs one glyph object, but legend outside plot area needs a for-loop to create a list of glyph objects which is used to generate legend_items.

Thanks.

Code Example:

  1. Hover Tooltips can be achieved by using ColumnDataSources (https://docs.bokeh.org/en/latest/docs/user_guide/categorical.html#heat-maps)
import pandas as pd
from bokeh.sampledata.stocks import AAPL
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, output_file, show

df = pd.DataFrame(AAPL)
output_file("test.html")
p = figure(tools="hover")
source = ColumnDataSource(df)

p.scatter("high", "low", source=source)

p.hover.tooltips = [("Date","@date")]

show(p)
  1. Legend outside the plot (Position the legend outside the plot area with Bokeh)
import pandas as pd

from bokeh.models import Legend, LegendItem
from bokeh.palettes import Spectral4
from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.stocks import AAPL, GOOG, IBM, MSFT

output_file("test2.html")
p = figure(x_axis_type="datetime")
p_list = []

for data, name, color in zip([AAPL, IBM, MSFT, GOOG], ["AAPL", "IBM", "MSFT", "GOOG"], Spectral4):
    df = pd.DataFrame(data)
    df['date'] = pd.to_datetime(df['date'])
    p_list.append(p.line(df['date'], df['close'], line_width=2, color=color, alpha=0.8))

legend_items = [LegendItem(label=x, renderers=[p_list[i]]) for i, x in enumerate(["AAPL", "IBM", "MSFT", "GOOG"])]
legend_ = Legend(items=legend_items, click_policy="hide", location="center", label_text_font_size="12px")
p.add_layout(legend_, "right")

show(p)
Nein
  • 13
  • 4
  • You can add tools as instances instead of strings. `HoverTool` accept `renderers` just as well as `LegendItem` does. – Eugene Pakhomov Apr 09 '20 at 07:49
  • With that being said, I still don't understand why you cannot just add the hover tool to the second example. Just specify `tools='hover'` in the call to `figure`. – Eugene Pakhomov Apr 09 '20 at 07:51
  • @EugenePakhomov thanks for the comment. I am not sure if the tooltips can map to the dataframe without calling the ColumnDataSource. I would like to show specific columns with respect to the data in the same row. Adding ```tools='hover'``` just gave me tooltips of the x, y and the index. – Nein Apr 09 '20 at 09:04
  • Ah, I see what you mean now. Yes, you have to use `ColumnDataSource` to be able to use data columns. Let my try to come up with an example. – Eugene Pakhomov Apr 09 '20 at 09:47

1 Answers1

0
import pandas as pd

from bokeh.models import Legend, LegendItem, ColumnDataSource
from bokeh.palettes import Spectral4
from bokeh.plotting import figure, show
from bokeh.sampledata.stocks import AAPL, GOOG, IBM, MSFT

p = figure(x_axis_type="datetime", tools='hover')
p.hover.tooltips = [("Date", "@date{%F}"),
                    ("Open", "@open"),
                    ("Close", "@close")]
p.hover.formatters = {'@date': 'datetime'}

legend_items = []
for data, name, color in zip([AAPL, IBM, MSFT, GOOG],
                             ["AAPL", "IBM", "MSFT", "GOOG"],
                             Spectral4):
    data['date'] = pd.to_datetime(data['date']).values
    ds = ColumnDataSource(data)
    renderer = p.line('date', 'close', source=ds,
                      line_width=2, color=color, alpha=0.8)
    legend_items.append(LegendItem(label=name, renderers=[renderer]))

legend = Legend(items=legend_items, click_policy="hide",
                location="center", label_text_font_size="12px")
p.add_layout(legend, "right")

show(p)

If you want to have different tooltips for different stocks, you can create one hover tool per each stock and specify them in the tools argument. The downside - there will be multiple hover tool buttons on the toolbar.

Eugene Pakhomov
  • 9,309
  • 3
  • 27
  • 53
  • Thanks for the comment. This provides me some idea, although it does not apply to my plot. I have a large dataset, so the plots went empty after using your idea. – Nein Apr 09 '20 at 10:32
  • It's impossible to tell anything without the actual code, Python errors, and JavaScript errors in your browser. – Eugene Pakhomov Apr 09 '20 at 14:01
  • yea I understand. The code is almost the same as yours, I just changed the ```p.line()``` into ```p.x()```. The dimension of the DataFrame is (700k,6). With the for loop of the ```ColumnDataSource```, the html size have increased into 80MB from 15MB. I can only see the grid of the plot, and I can see the correct tooltips information when my cursor stayed on the 'invisible' points. I think it should be kind of out-of-memory. – Nein Apr 09 '20 at 14:58