1

I thought I found a concrete example that I could leverage here, but the same logic is not working for me, unfortunately.

I have two questions:

  • Is what I'm attempting to do even possible: send a dynamic scatter plot in a html document that allows you to control what is plotted on the x & y axes. I've succeeded with static plots, but haven't cracked dynamic plots yet.

  • Where is my code going wrong (see below?)

Here are the relevant pieces of code (in order. I have a dataframe that I've converted into "source" using ColumnDataSource.

I create an initial plot (note, at this point, there are no columns called 'x' and 'y'. I create those in the callback function later) :

plot.circle('x', 'y',
            source=source,
            color={'field': 'Picker',
                    'transform': mapper},
                    legend='Picker')

I create two dropdown menus (note that the options in each correspond to the columns from "source" I'd want people to be able to choose from)

x_menu=Select(options=['Box Office', 'Difference', 'Price Paid'],
                        value='Box Office',
                         title='What do you want to put on the x axis')

y_menu=Select(options=['Metacritic', 'Rotten Tomatoes'],
                        value='Metacritic',
                         title='What do you want to put on the y axis')

I create the callback:

callback = CustomJS (args=dict(source=source), code="""
    console.log('changed selected option', cb_obj.value)
    var data=source.data
    data['x']=data[cb_obj.value]
    data['y']=data[cb_obj.value]
    source.change.emit();
    """)

I assign the callbacks to the dropdown menus:

x_menu.callback = callback
y_menu.callback = callback

And then I try to show the plot:

show(row(widgetbox(x_menu, y_menu), plot))

But it returns the below error:

ERROR:bokeh.core.validation.check:E-1001 (BAD_COLUMN_NAME): Glyph refers to nonexistent column name: x, y [renderer: GlyphRenderer(id='df96b108-e2e4-4b8c-b0c6-12df40b4205d', ...)]

Any help is very much appreciated. Thank you!

E Larson
  • 25
  • 5

1 Answers1

0

It would be easier to help if you gave a self-contained minimal example (code that we can just copy, paste and run). With that said, here is something that might help you to get started. You were already very close to this solution, but I guess the important part is to have one callback for each menu.

I hope this helps :-)

# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd

from bokeh.layouts import row, widgetbox
from bokeh.models import CustomJS, Select
from bokeh.plotting import figure, show, ColumnDataSource

# Define some random data
dataframe = pd.DataFrame({
    'Difference': np.sin(np.linspace(0, 100, 500)),
    'Price': np.cos(np.linspace(0, 100, 500)),
    'Metacritic': np.sin(np.linspace(0, 100, 500)),
    'Rotten Tomatoes': np.cos(np.linspace(0, 200, 500)),
    })

# Set x and y-axis defaults
dataframe['x'] = dataframe['Difference']
dataframe['y'] = dataframe['Metacritic']

# Create Bokeh's ColumnDataSource
source = ColumnDataSource(data=dataframe)

# Create the plot figure
plot = figure(plot_width=400, plot_height=400)
plot.circle('x', 'y', source=source)

# Create the dropdown menus
x_menu = Select(options=['Difference', 'Price'],
                value='Difference',
                title='What do you want to put on the x axis')

y_menu = Select(options=['Metacritic', 'Rotten Tomatoes'],
                value='Metacritic',
                title='What do you want to put on the y axis')

# Create two callbacks, one for each menu
callback_x = CustomJS(args=dict(source=source), code="""
    console.log('changed selected option', cb_obj.value)
    var data=source.data
    data['x']=data[cb_obj.value]
    source.change.emit();
    """)

callback_y = CustomJS(args=dict(source=source), code="""
    console.log('changed selected option', cb_obj.value)
    var data=source.data
    data['y']=data[cb_obj.value]
    source.change.emit();
    """)

# Assign callbacks to menu widgets
x_menu.callback = callback_x
y_menu.callback = callback_y

# Show the html document with a layout
show(row(widgetbox(x_menu, y_menu), plot))
Azrael_DD
  • 171
  • 9
  • Thanks so much. Good call on adding the random data so that people can just run it. Unfortunately, I'm still having similar issues. I'll send a new snippet of code below to clarify the issue – E Larson Nov 13 '18 at 22:12
  • Never mind... I glazed right over your "set x and y defaults." That was the issue with the nonexistent columns (on top of adding the two callbacks). – E Larson Nov 13 '18 at 22:23