首页 > 解决方案 > 在散景服务器应用程序中绘制多行流数据

问题描述

我正在尝试使用流数据构建一个散景应用程序,该应用程序跟踪多个“策略”,因为它们是在基于囚徒困境代理的模型中生成的。我遇到了一个问题,试图让我的线图不将所有数据点连接在一条线上。我把这个复制问题的小演示脚本放在一起。我已经阅读了大量关于散景图中线和多线渲染的文档,但我只是没有找到与我的简单案例相匹配的东西。您可以运行此代码,它会自动在 localhost:5004 打开散景服务器 ...

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

    # Create a data source
    data_source = ColumnDataSource({'step': [], 'strategy': [], 'ncount': []})

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

    # Create a figure
    fig = figure(title='Streaming Line Plot',
                 plot_width=800, plot_height=400)
    fig.line(x='step', y='ncount', source=data_source)
    global step
    step = 0

    def button1_run():
        global 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():
        global step
        step = step+1
        for i in range(len(strategies)):
            new = {'step': [step],
                   'strategy': [strategies[i]],
                   'ncount': [random.choice(range(1,100))]}
            fig.line(x='step', y='ncount', source=new)
            data_source.stream(new)


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

    doc.add_root(column(fig, button1, button2))
    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()

我希望通过循环遍历示例中的 4 个“策略”(在单击按钮 2 之后),我可以将来自模拟的新数据流式传输到该策略的线图中,并且仅执行步骤。但我得到的是一条所有四个值都垂直连接的线,然后其中一个在下一步连接到第一个值。以下是几个步骤后的样子: 在此处输入图像描述

我注意到,如果我移出data_source.stream(new)for 循环,我会得到一个很好的单线图,但当然它只适用于最后一个退出循环的策略。

在我研究过的所有散景多线绘图示例中(不是multi_line字形,我无法弄清楚它似乎与悬停工具有一些问题),说明看起来很清楚:如果你想渲染第二个行,您将另一个fig.line渲染器添加到现有的figure,它使用为该行提供的数据绘制一条source=data_source线。但即使我的 for 循环为每个策略分别收集和添加数据,我也没有得到 4 个线图,我只得到一个。

希望我错过了一些明显的东西!提前致谢。

标签: pythonplotserverbokehline-plot

解决方案


似乎您需要每个策略一行,而不是每一步一行。如果是这样,我会这样做:

import random

from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.layouts import column
from bokeh.models import Button
from bokeh.palettes import Dark2
from bokeh.plotting import figure, ColumnDataSource
from bokeh.server.server import Server

STRATEGIES = ['DD', 'DC', 'CD', 'CCDD']


def make_document(doc):
    step = 0

    def new_step_data():
        nonlocal step
        result = [dict(step=[step],
                       ncount=[random.choice(range(1, 100))])
                  for _ in STRATEGIES]
        step += 1
        return result

    fig = figure(title='Streaming Line Plot', plot_width=800, plot_height=400)
    sources = []
    for s, d, c in zip(STRATEGIES, new_step_data(), Dark2[4]):
        # Generate the very first step right away
        # to avoid having a completely empty plot.
        ds = ColumnDataSource(d)
        sources.append(ds)
        fig.line(x='step', y='ncount', source=ds, color=c)

    callback_obj = None

    def button1_run():
        nonlocal callback_obj
        if callback_obj is None:
            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():
        for src, data in zip(sources, new_step_data()):
            src.stream(data)

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

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


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

server = Server(apps, port=5004)

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

推荐阅读