繁体   English   中英

在散景服务器应用程序中绘制多行流数据

[英]plotting multiple lines of streaming data in a bokeh server application

我正在尝试使用流数据构建一个散景应用程序,该应用程序跟踪多个“策略”,因为它们是在基于 model 的囚徒困境代理中生成的。 我遇到了一个问题,试图让我的线图不将所有数据点连接在一条线上。 我把这个复制问题的小演示脚本放在一起。 我已经阅读了大量关于散景图中线和多线渲染的文档,但我只是没有找到与我的简单案例相匹配的东西。 您可以运行此代码,它会自动在 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 后),我可以 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 将模拟出来的新数据放入一行 plot 中,仅用于该策略和步骤。 但我得到的是一条所有四个值都垂直连接的线,然后其中一个在下一步连接到第一个值。 以下是几个步骤后的样子: 在此处输入图像描述

我注意到,如果我将data_source.stream(new)移出 for 循环,我会得到一个很好的单行 plot,但当然它只是针对最后一个策略退出循环。

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

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

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

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

谢谢你,尤金。 你的解决方案让我回到了正确的轨道上。 我玩了更多,最终得到以下结果:

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
    # 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=800, 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)        


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

结果正是我想要的... 在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM