1

The legend in Bokeh is a bit unwieldy. It tries to display all points along a line as a new legend entries if I use it in conjunction with HoverTools and ColumnDataSource. Below is a simple example. All code works here as of Bokeh 12.4 and standard libs with Anaconda 2.7.

# a simple time series data-set:

import pandas as pd
import numpy as np
import datetime
import matplotlib.pyplot as plt

data1, data2 = np.random.randn(365, 1)+2, np.random.randn(365, 1)-2
data = np.concatenate((data1,data2), axis=1)
start, end = datetime.datetime(2015, 1, 1), datetime.datetime(2015, 12, 31)
ts= pd.DataFrame(data=data,
                    index=pd.DatetimeIndex(start=start, end=end, freq="D"),
                    columns=["1", "2"],
                    dtype=float)
ts.plot()
plt.show()

Now use Bokeh to add hover tool tips and a more aesthetically pleasing plot:

# Now use Bokeh:

from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool
from bokeh.palettes import Spectral6
from bokeh.models.formatters import DatetimeTickFormatter
from bokeh.io import push_notebook, show, output_notebook
output_notebook()

def multiTimeSeriesPlot(ts, plotFeatures):

    # pre-processing
    ts.index.rename("datetime", inplace=True)
    ts = ts.reset_index()
    ts['tooltip'] = [x.strftime("%Y-%m-%d %H:%M:%S") for x in ts['datetime']]

    data = ColumnDataSource(ts)

    # define color palette:
    mypalette=Spectral6[0:len(plotFeatures)]
    color_ix = 0

    p = figure(width=700, height=400, tools= ['hover','pan','box_zoom', 'reset', 'wheel_zoom'])

    for feature in plotFeatures:
        p.line(x='datetime', y=feature, source=data, name='line', 
        legend=feature, line_width=2, color=mypalette[color_ix])
        color_ix += 1
        p.circle(x='datetime', y=feature, source=data, name='sample', 
        legend='sample', size=4, color='darkgrey', alpha=0.2)



    p.xaxis.formatter = DatetimeTickFormatter(hours=["%d %B %Y"],
                                               days=["%d %B %Y"],
                                              months=["%d %B %Y"],
                                              years=["%d %B %Y"])
    ## Hover tools
    hover = p.select(dict(type=HoverTool))
    hover.tooltips, hover.name, hover.mode = [('when','@tooltip'), 
    ('y','$y')], 'sample', 'mouse'

    show(p)

    # And finally, call with:

multiTimeSeriesPlot(ts=ts, plotFeatures=["1", "2"])

What am I doing incorrectly? The following code works if I don't convert the data to a ColumnDataSource. But not using ColumnDataSource breaks the tool tips module.

EB88
  • 841
  • 1
  • 10
  • 26

1 Answers1

1

I answered my own question here, having consulted the following:

In Bokeh, how do I add tooltips to a Timeseries chart (hover tool)?

The fundamental difference is that instead of turning the whole dataframe in to a ColumnDataSource, loop through each timeseries to plot and turn it in to a ColumnDataSource. Whilst this is computationally inefficient, it works:

from bokeh.io import push_notebook, show, output_notebook
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool
from bokeh.palettes import Spectral6
from bokeh.models.formatters import DatetimeTickFormatter
output_notebook()

def multiTimeSeriesPlot(ts, plotFeatures):

    # pre-processing
    ts.index.rename("datetime", inplace=True)
    ts = ts.reset_index()
    ts['tooltip'] = [x.strftime("%Y-%m-%d %H:%M:%S") for x in ts['datetime']]

    # define color palette:
    mypalette=Spectral6[0:len(plotFeatures)]
    color_ix = 0

    p = figure(width=700, height=400, tools= ['hover','pan','box_zoom', 'reset', 'wheel_zoom'])

    for count, feature in enumerate(plotFeatures):

        # bokeh can't show datetime object in tooltip properly,so we use string instead
        source = ColumnDataSource(data={
                    'dateX': ts["datetime"], # python datetime object as X axis
                    'v': list(ts[feature]),
                    'tooltip': ts['tooltip'], #string of datetime for display in tooltip
                    'name': [feature for n in xrange(len(ts))]
                })

        p.line('dateX', 'v',source=source,legend='name', color=mypalette[count])
        circle = p.circle('dateX', 'v',source=source, fill_color="white", size=5,
                          legend='name', color=mypalette[count])

    p.xaxis.formatter = DatetimeTickFormatter(hours=["%d %B %Y"],
                                               days=["%d %B %Y"],
                                              months=["%d %B %Y"],
                                              years=["%d %B %Y"])
    ## Hover tools
    hover = p.select(dict(type=HoverTool))
    hover.tooltips, hover.name, hover.mode = [('when','@tooltip'), 
    ('y','$y')], 'sample', 'mouse'

    show(p)

And call with:

multiTimeSeriesPlot(ts=test, plotFeatures=["1", "2"])
Community
  • 1
  • 1
EB88
  • 841
  • 1
  • 10
  • 26