I have a class Collection that holds a bunch of other class objects Thing that all have the same attributes with different values. The Collection.plot(x, y) method makes a scatter plot of the x values vs. the y values of all the collected Thing objects like so:
from bokeh.plotting import figure, show
from bokeh.models import TapTool
class Thing:
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def plot(self):
# Plot all data for thing
fig = figure()
fig.circle([1,2,3], [self.foo, self.bar, self.baz])
return fig
class Collection:
def __init__(self, things):
self.things = things
def plot(self, x, y):
# Configure plot
title = '{} v {}'.format(x, y)
fig = figure(title=title, tools=['pan', 'tap'])
taptool = fig.select(type=TapTool)
taptool.callback = RUN_THING_PLOT_ON_CLICK()
# Plot data
xdata = [getattr(th, x) for th in self.things]
ydata = [getattr(th, y) for th in self.things]
fig.circle(xdata, ydata)
return fig
Then I would make a scatter plot of all four Thing sources' 'foo' vs. 'baz' values with:
A = Thing(2, 4, 6)
B = Thing(3, 6, 9)
C = Thing(7, 2, 5)
D = Thing(9, 2, 1)
X = Collection([A, B, C, D])
X.plot('foo', 'baz')
What I would like to have happen here is have each point on the scatter plot able to be clicked. On click, it would run the plot method for the given Thing, making a separate plot of all its 'foo', 'bar', and 'baz' values.
Any ideas on how this can be accomplished?
I know I can just load ALL the data for all the objects into a ColumnDataSource and make the plot using this toy example, but in my real use case the Thing.plot method does a lot of complicated calculations and may be plotting thousands of points. I really need it to actually run the Thing.plot method and draw the new plot. Is that feasible?
Alternatively, could I pass the Collection.plot method a list of all the Thing.plot pre-drawn figures to then display on click?
Using Python>=3.6 and bokeh>=2.3.0. Thank you very much!
from Open new plot with `bokeh` TapTool
No comments:
Post a Comment