Display hover data in Bokeh scatter plots

In [7]:
"""
    Tested on bokeh 0.11.1, pandas 0.18.0
    
    First plot has no groups and correct hover data, 
    Second plot has groups but misaligned hover data.
    
"""
from bokeh.sampledata.autompg import autompg as df
from bokeh.charts import Scatter, TimeSeries, output_notebook, show
from bokeh.models import HoverTool, BoxZoomTool, ResetTool, CrosshairTool, BoxSelectTool, WheelZoomTool, PreviewSaveTool

from bokeh.models.renderers import GlyphRenderer
import copy

hover = HoverTool(
    tooltips = [
    ("cyl", "@cyl"),
    ("displ", "@displ"),
    ("Weight", "@weight"),
    ("Acceleration", "@accel"),
    ('Horsepower', '@hp'),
    ('MPG', '@mpg'),        
    ('Origin', '@origin'),        
])

tools = [hover, BoxZoomTool(), ResetTool(), CrosshairTool(), BoxSelectTool(), WheelZoomTool(), PreviewSaveTool()] 
scatter_sans_groups = Scatter(df, x='mpg', y='hp', title="Auto MPG", xlabel="Miles Per Gallon", ylabel="Horsepower", 
                              tools=tools)
In [8]:
def patch_renderer(scatter_instance, hover_instance, data_frame):
    tooltip_fields = [value[1:] for label, value in hover_instance.tooltips if value[0] == '@']
    glyph_renderer = [r for r in scatter_instance.renderers if isinstance(r, GlyphRenderer)][0]
    renderer_fields = glyph_renderer.data_source.data.keys()
    fields_to_add = [tooltip_field for tooltip_field in tooltip_fields if tooltip_field not in renderer_fields]

    print 'tooltip_fields: ', tooltip_fields
    print 'renderer_fields: ', renderer_fields
    print 'fields_to_add: ', fields_to_add
    print 'counts before adding: ', [(key, len(glyph_renderer.data_source.data[key])) for key in glyph_renderer.data_source.data]
    
    for field in fields_to_add:
        glyph_renderer.data_source.data[field] = list(data_frame[field])
        
    print 'counts after adding: ', [(key, len(glyph_renderer.data_source.data[key])) for key in glyph_renderer.data_source.data]
        
patch_renderer(scatter_sans_groups, hover, df)
tooltip_fields:  ['cyl', 'displ', 'weight', 'accel', 'hp', 'mpg', 'origin']
renderer_fields:  ['x_values', 'chart_index', 'y_values']
fields_to_add:  ['cyl', 'displ', 'weight', 'accel', 'hp', 'mpg', 'origin']
counts before adding:  [('x_values', 392), ('chart_index', 392), ('y_values', 392)]
counts after adding:  [('origin', 392), ('mpg', 392), ('chart_index', 392), ('displ', 392), ('weight', 392), ('hp', 392), ('accel', 392), ('x_values', 392), ('y_values', 392), ('cyl', 392)]
In [9]:
output_notebook()
show(scatter_sans_groups)  # this is correct output
Loading BokehJS ...
Out[9]:

<Bokeh Notebook handle for In[9]>

Demonstrate problem when groups are introduced

In [10]:
# Not sure why hover can't be reused without throwing an error in Scatter, so create hover2
hover2 = HoverTool(
    tooltips = [
    ("cyl", "@cyl"),
    ("displ", "@displ"),
    ("Weight", "@weight"),
    ("Acceleration", "@accel"),
    ('Horsepower', '@hp'),
    ('MPG', '@mpg'),        
    ('Origin', '@origin'),        
])

scatter_with_groups = Scatter(df, x='mpg', y='hp', title="Auto MPG", xlabel="Miles Per Gallon", ylabel="Horsepower", 
                              tools=[hover2],
                              color='cyl', marker='origin')
patch_renderer(scatter_with_groups, hover2, df)
output_notebook()
show(scatter_with_groups)

# The output plot is incorrect!  Mismatch between # of unique combinations of color and marker (103) 
# vs. number of total rows (392) in df causes incorrect hover output
tooltip_fields:  ['cyl', 'displ', 'weight', 'accel', 'hp', 'mpg', 'origin']
renderer_fields:  ['origin', 'x_values', 'chart_index', 'y_values', 'cyl']
fields_to_add:  ['displ', 'weight', 'accel', 'hp', 'mpg']
counts before adding:  [('origin', 103), ('x_values', 103), ('chart_index', 103), ('y_values', 103), ('cyl', 103)]
counts after adding:  [('origin', 103), ('mpg', 392), ('chart_index', 103), ('displ', 392), ('weight', 392), ('hp', 392), ('accel', 392), ('x_values', 103), ('y_values', 103), ('cyl', 103)]
Loading BokehJS ...
Out[10]:

<Bokeh Notebook handle for In[10]>