【问题标题】:How can I reset a bokeh figure to its initial state with a "reset" callback button?如何使用“重置”回调按钮将散景图形重置为其初始状态?
【发布时间】:2020-04-24 03:23:36
【问题描述】:

我一直在从事一个项目,该项目使用散景可视化来显示基于代理的模型 (ABM) 模拟的结果。在recent post 中,我得到了帮助,让我的数据在一个非常简化的模拟版本中正确流式传输。我认为不费吹灰之力的下一个任务是在我的布局中添加一个“重置”按钮,这样我就可以将我的图形恢复到其初始状态并再次从“步骤 0”运行模拟。令人惊讶的是,似乎没有一种简单的方法可以做到这一点。我尝试了几种不同的方法,包括重新初始化我的所有数据和重新填充我的 ColumnDataSources,但我无法让之前运行模拟的数据消失。这是一个说明问题的独立代码示例:

import colorcet as cc
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.plotting import figure, ColumnDataSource
from bokeh.models import Button
from bokeh.layouts import column
import random

def make_document(doc):

    # make a list of groups
    strategies = ['DD', 'DC', 'CD', 'CCDD']

    # initialize some vars
    step = 0
    callback_obj = None  
    colors = cc.glasbey_dark
    #num_colors = len(colors)
    # create a list to hold all CDSs for active strategies in next step
    sources = []

    # Create a figure container
    fig = figure(title='Streaming Line Plot - Step 0', plot_width=1400, plot_height=400)

    # get step 0 data for initial strategies
    for i in range(len(strategies)):
        step_data = dict(step=[step], 
                        strategy = [strategies[i]],
                        ncount=[random.choice(range(1, 100))])
        data_source = ColumnDataSource(step_data)
        color = colors[i]
        # this will create one fig.line renderer for each strategy & its data for this step
        fig.line(x='step', y='ncount', source=data_source, color=color, line_width=2)
        # add this CDS to the sources list
        sources.append(data_source)

    def button1_run():
        nonlocal callback_obj
        if button1.label == 'Run':
            button1.label = 'Stop'
            button1.button_type='danger'
            callback_obj = doc.add_periodic_callback(button2_step, 100)
        else:
            button1.label = 'Run'
            button1.button_type = 'success'
            doc.remove_periodic_callback(callback_obj)

    def button2_step():
        nonlocal step
        data = []
        step += 1
        fig.title.text = 'Streaming Line Plot - Step '+str(step)
        for i in range(len(strategies)):
            step_data = dict(step=[step], 
                            strategy = [strategies[i]],
                            ncount=[random.choice(range(1, 100))])
            data.append(step_data)
        for source, data in zip(sources, data):
            source.stream(data)        

    def button3_reset():
        step = 0
        fig.title.text = 'Streaming Line Plot - Step '+str(step)

        for i in range(len(strategies)):
            init_data = dict(step=[step], 
                            strategy = [strategies[i]],
                            ncount=[random.choice(range(1, 100))])
            reset_source = ColumnDataSource(init_data)
            print(init_data)
            color = colors[i]
            # this will create one fig.line renderer for each strategy & its data for this step
            fig.line(x='step', y='ncount', source=reset_source, color=color, line_width=2)
            # add this CDS to the sources list
            sources.append(reset_source)


    # add on_click callback for button widget
    button1 = Button(label="Run", button_type='success', width=390)
    button1.on_click(button1_run)
    button2 = Button(label="Step", button_type='primary', width=390)
    button2.on_click(button2_step)
    button3 = Button(label="Reset", button_type='warning', width=390)
    button3.on_click(button3_reset)

    doc.add_root(column(fig, button1, button2, button3))
    doc.title = "Now with live updating!"

apps = {'/': Application(FunctionHandler(make_document))}

server = Server(apps, port=5004)
server.start()

if __name__ == '__main__':
    server.io_loop.add_callback(server.show, "/")
    server.io_loop.start()

我在button3_reset 代码中尝试做的基本上是重复make_document 函数顶部的初始化。但是,即使该代码的工作方式相同(从卡在button3 步骤中间的打印输出可以明显看出),我无法让图形重置为其初始空状态。我已经阅读了很多堆栈溢出帖子和其他散景文档,但没有找到一个简单的答案来回答我认为是一个简单的问题:如何将散景线图重置为其原始状态,以便您可以运行再次从起点开始数据流?

我正在使用 bokeh 1.4.0(anaconda 不允许我更新)、python 3.7.6、spyder 4.0.1 以及 Chrome 和 Brave 浏览器进行可视化。

【问题讨论】:

标签: python plot server bokeh line-plot


【解决方案1】:

您的 button3_reset 代码不会清理任何内容 - 它只是在现有内容的基础上添加新内容。

相反,您应该只遍历sources 列表并将每个源的data 属性设置为代码中第一个循环中使用的初始值。这意味着,您还必须将这些数据保存在某个地方。

【讨论】:

  • 对。这就是为什么我的代码让我难过的原因。我以为我就是这么做的。在 button3 for 循环中,init_data 为我提供了我想通过重置提供给图形的数据的字典(就像在初始定义中一样)。我认为使用该数据作为源(在 fig.line 渲染器中)重置 CDS 会清除旧数据。我会很好地创建一个新人物来取代旧人物。每个模拟都是一个单独的运行,与以前的运行没有任何联系。但如果我重做 fig = figure 定义,它只会在页面中添加第二个图形,而不是替换第一个。
  • 是的,这正是您不应该重做任何散景模型的原因。只需替换数据,就是这样。更新:啊,我看到您在新功能中确实做到了 - 很好!
【解决方案2】:

通常情况下,我把事情复杂化了很多。这是完成这项工作的button3_reset 的代码。

 def button3_reset():
        nonlocal step
        step = 0
        data = []
        fig.title.text = 'Streaming Line Plot - Step '+str(step)

        for i in range(len(strategies)):
            init_data = dict(step=[step], 
                            strategy = [strategies[i]],
                            ncount=[random.choice(range(1, 100))])
            data.append(init_data)

        for source, data in zip(sources, data):
            source.data = data

我之前所做的是生成新的 CDS,但旧的 CDS 仍然嵌入在图中。再次感谢 Eugene 的提示,我意识到我只需要重新分配现有 CDS 的 .data 属性,而不是创建新的。然后,为了整理,我不得不将标题更新回“步骤 0”,然后将 step 设为非局部变量,以便 button2_step 知道在 1 处重新开始步骤编号。这样,重置它应该是什么做。再次感谢您的回复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 1970-01-01
    • 1970-01-01
    • 2021-09-05
    • 2015-02-21
    • 2016-07-10
    相关资源
    最近更新 更多