使用 `bokeh` TapTool 打开新图
Open new plot with `bokeh` TapTool
我有一个 class Collection
,它包含一堆其他 class 对象 Thing
,它们都具有相同的属性,但值不同。 Collection.plot(x, y)
方法绘制了 x
值与所有收集到的 Thing
对象的 y
值的散点图,如下所示:
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
然后我会制作所有四个 Thing
来源的 'foo' 与 'baz' 值的散点图:
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')
我希望这里发生的是散点图上的每个点都可以单击。单击时,它将 运行 给定 Thing
的 plot
方法,单独绘制其所有 'foo'、'bar' 和 'baz' 值。
关于如何实现这一点有什么想法吗?
我知道我可以将所有对象的所有数据加载到 ColumnDataSource
中并使用这个玩具示例制作绘图,但在我的实际用例中 Thing.plot
方法做了很多复杂的计算,可能要绘制数千个点。我真的需要它来实际 运行 Thing.plot
方法并绘制新图。可行吗?
或者,我能否将所有 Thing.plot
预绘制图形的列表传递给 Collection.plot
方法,然后在单击时显示?
使用 Python>=3.6 和散景 >=2.3.0。非常感谢!
有两种方法可以做到这一点。这是基本示例。首先,您可以使用 Tap 事件来执行此操作并创建一个函数以从字形中获取信息。其次,您可以直接将源连接到函数。
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.events import Tap
from bokeh.models import TapTool, ColumnDataSource
def tapfunc():
print(source.selected.indices)
def sourcefunc(attr, old, new):
print(source.selected)
source = ColumnDataSource(data={
'x': [1,2,3,4,5],
'y': [6,7,8,9,10]
})
p = figure(width=400, height=400)
circles = p.circle(x='x', y='y', source=source, size=20, color="navy", alpha=0.5)
p.add_tools(TapTool())
p.on_event(Tap, tapfunc)
source.selected.on_change('indices', sourcefunc)
curdoc().add_root(p)
已选择 return 列表 选定值索引。所以,你应该为你的来源添加索引。您可以使用 with pandas 作为索引。有关选择 check here. So in function you could create a new figure and glyph (line etc.) and update it. Here 的更多信息,非常好的例子。您可以从您的电脑中提取并运行它。
我编辑了你的代码,很抱歉我回来得太晚了。
from bokeh.plotting import figure, show
from bokeh.models import TapTool, ColumnDataSource
from bokeh.events import Tap
from bokeh.io import curdoc
from bokeh.layouts import Row
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
t_fig = figure(width=300, height=300)
t_fig.circle([1, 2, 3], [self.foo, self.bar, self.baz])
return t_fig
def tapfunc(self):
selected_=[]
'''
here we get selected data. I select by name (foo, bar etc.) but also x/y works. There is a loop because taptool
has a multiselect option. All selected names adds to selected_
'''
for i in range(len(Collection.source.selected.indices)):
selected_.append(Collection.source.data['name'][Collection.source.selected.indices[i]])
print(selected_) # your selected data
# now create a graph according to selected_. I use only first item of list. But you can use differently.
if Collection.source.selected.indices:
if selected_[0] == "foo":
A = Thing(2, 4, 6).plot()
layout.children = [main, A]
elif selected_[0] == "bar":
B = Thing(3, 6, 9).plot()
layout.children = [main, B]
elif selected_[0] == 'baz':
C = Thing(7, 2, 5).plot()
layout.children = [main, C]
class Collection:
# Columndata source. Also could be added in __init__
source = ColumnDataSource(data={
'x': [1, 2, 3, 4, 5],
'y': [6, 7, 8, 9, 10],
'name': ['foo', 'bar', 'baz', None, None]
})
def __init__(self):
pass
def plot(self):
# Configure plot
TOOLTIPS = [
("(x,y)", "(@x, @y)"),
("name", "@name"),
]
fig = figure(width=300, height=300, tooltips=TOOLTIPS)
# Plot data
circles = fig.circle(x='x', y='y', source=self.source, size=10)
fig.add_tools(TapTool())
fig.on_event(Tap, tapfunc)
return fig
main = Collection().plot()
layout = Row(children=[main])
curdoc().add_root(layout)
问题是当你 select 每次事情 class 创建一个新人物时。不推荐。因此,您可以根据自己的意愿创建所有图表并使它们 visible/invisible 或者您可以更改图表的来源。您可以找到很多关于更改图源并制作它们的示例 visible/invisible。我希望它对你有用:)
我有一个 class Collection
,它包含一堆其他 class 对象 Thing
,它们都具有相同的属性,但值不同。 Collection.plot(x, y)
方法绘制了 x
值与所有收集到的 Thing
对象的 y
值的散点图,如下所示:
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
然后我会制作所有四个 Thing
来源的 'foo' 与 'baz' 值的散点图:
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')
我希望这里发生的是散点图上的每个点都可以单击。单击时,它将 运行 给定 Thing
的 plot
方法,单独绘制其所有 'foo'、'bar' 和 'baz' 值。
关于如何实现这一点有什么想法吗?
我知道我可以将所有对象的所有数据加载到 ColumnDataSource
中并使用这个玩具示例制作绘图,但在我的实际用例中 Thing.plot
方法做了很多复杂的计算,可能要绘制数千个点。我真的需要它来实际 运行 Thing.plot
方法并绘制新图。可行吗?
或者,我能否将所有 Thing.plot
预绘制图形的列表传递给 Collection.plot
方法,然后在单击时显示?
使用 Python>=3.6 和散景 >=2.3.0。非常感谢!
有两种方法可以做到这一点。这是基本示例。首先,您可以使用 Tap 事件来执行此操作并创建一个函数以从字形中获取信息。其次,您可以直接将源连接到函数。
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.events import Tap
from bokeh.models import TapTool, ColumnDataSource
def tapfunc():
print(source.selected.indices)
def sourcefunc(attr, old, new):
print(source.selected)
source = ColumnDataSource(data={
'x': [1,2,3,4,5],
'y': [6,7,8,9,10]
})
p = figure(width=400, height=400)
circles = p.circle(x='x', y='y', source=source, size=20, color="navy", alpha=0.5)
p.add_tools(TapTool())
p.on_event(Tap, tapfunc)
source.selected.on_change('indices', sourcefunc)
curdoc().add_root(p)
已选择 return 列表 选定值索引。所以,你应该为你的来源添加索引。您可以使用 with pandas 作为索引。有关选择 check here. So in function you could create a new figure and glyph (line etc.) and update it. Here 的更多信息,非常好的例子。您可以从您的电脑中提取并运行它。
我编辑了你的代码,很抱歉我回来得太晚了。
from bokeh.plotting import figure, show
from bokeh.models import TapTool, ColumnDataSource
from bokeh.events import Tap
from bokeh.io import curdoc
from bokeh.layouts import Row
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
t_fig = figure(width=300, height=300)
t_fig.circle([1, 2, 3], [self.foo, self.bar, self.baz])
return t_fig
def tapfunc(self):
selected_=[]
'''
here we get selected data. I select by name (foo, bar etc.) but also x/y works. There is a loop because taptool
has a multiselect option. All selected names adds to selected_
'''
for i in range(len(Collection.source.selected.indices)):
selected_.append(Collection.source.data['name'][Collection.source.selected.indices[i]])
print(selected_) # your selected data
# now create a graph according to selected_. I use only first item of list. But you can use differently.
if Collection.source.selected.indices:
if selected_[0] == "foo":
A = Thing(2, 4, 6).plot()
layout.children = [main, A]
elif selected_[0] == "bar":
B = Thing(3, 6, 9).plot()
layout.children = [main, B]
elif selected_[0] == 'baz':
C = Thing(7, 2, 5).plot()
layout.children = [main, C]
class Collection:
# Columndata source. Also could be added in __init__
source = ColumnDataSource(data={
'x': [1, 2, 3, 4, 5],
'y': [6, 7, 8, 9, 10],
'name': ['foo', 'bar', 'baz', None, None]
})
def __init__(self):
pass
def plot(self):
# Configure plot
TOOLTIPS = [
("(x,y)", "(@x, @y)"),
("name", "@name"),
]
fig = figure(width=300, height=300, tooltips=TOOLTIPS)
# Plot data
circles = fig.circle(x='x', y='y', source=self.source, size=10)
fig.add_tools(TapTool())
fig.on_event(Tap, tapfunc)
return fig
main = Collection().plot()
layout = Row(children=[main])
curdoc().add_root(layout)
问题是当你 select 每次事情 class 创建一个新人物时。不推荐。因此,您可以根据自己的意愿创建所有图表并使它们 visible/invisible 或者您可以更改图表的来源。您可以找到很多关于更改图源并制作它们的示例 visible/invisible。我希望它对你有用:)