1

我有一个类Collection,它包含一堆其他类对象Thing,它们都具有不同值的相同属性。该方法绘制了值与所有收集到的对象的值Collection.plot(x, y)的散点图,如下所示:xyThing

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')

我希望在这里发生的是能够单击散点图上的每个点。单击时,它会运行plot给定的方法,生成Thing一个单独的图,其中包含所有 'foo'、'bar' 和 'baz' 值。

关于如何实现这一点的任何想法?

我知道我可以将所有对象的所有数据加载到 a 中ColumnDataSource并使用这个玩具示例制作绘图,但在我的实际用例中,该Thing.plot方法会进行很多复杂的计算,并且可能会绘制数千个点。我真的需要它来实际运行该Thing.plot方法并绘制新图。这可行吗?

或者,我可以将所有预先绘制的图形Collection.plot的列表传递给该方法,然后在点击时显示吗?Thing.plot

使用 Python>=3.6 和散景>=2.3.0。非常感谢你!

4

2 回答 2

1

我编辑了你的代码,很抱歉我回来得太晚了。

在此处输入图像描述

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)

问题是当您每次 Thing 类创建一个新图形时选择一些东西。不建议这样做。因此,您可以创建所有图表并根据您的意愿使它们可见/不可见,或者您可以更改图表的来源。您可以找到很多关于更改图形源并使它们可见/不可见的示例。我希望这个对你有用 :)

于 2021-11-14T00:37:21.560 回答
-1

有两种方法可以做到这一点。这是基本的例子。首先,您可以使用 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)

selected 返回一个列表一个选定的值索引。因此,您应该将索引添加到您的源。您可以将 pandas 用于索引。有关选择的更多信息,点击此处。所以在函数中你可以创建一个新的图形和字形(线等)并更新它。在这里,很好的例子。你可以从你的电脑上拉并运行它。

于 2021-11-03T00:23:27.430 回答