我正在尝试构建一个具有流数据的bokeh应用程序,该应用程序跟踪多个“策略”,因为它们是在基于囚徒困境代理的模型中生成的。我遇到了一个问题,试图让我的线状图不在一条线上连接所有的数据点。我把这个小演示脚本放在一起,复制了这个问题。我已经阅读了很多关于bokeh plots中的在线和multi_line渲染的文档,但我就是找不到与我的简单情况相匹配的东西。您可以运行此代码&它将自动打开一个位于localhost:5004的bokeh服务器...
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个“策略”(在单击button2之后),我可以将来自模拟的新数据流式传输到该策略和步骤的线状图中。但我得到的是一条线,所有四个值都垂直连接,然后其中一个连接到下一步的第一个值。下面是几个步骤之后的样子:
我注意到,如果我将data_source.stream(new)
移出for循环,我会得到一个漂亮的单线图,但当然这只适用于循环中的最后一个策略。
在我研究过的所有bokeh多线绘制示例中(不是multi_line
字形,我无法理解它,而且悬停工具似乎有一些问题),说明似乎非常清楚:如果您想呈现第二条线,可以在现有的figure
中添加另一个fig.line
渲染器,它将使用source=data_source
中为这条线提供的数据绘制一条线。但是,即使我的for循环分别为每个策略收集和添加数据,我也没有得到4个线状图,我只得到了一个。
希望我错过了一些明显的东西!提前谢谢。
发布于 2020-04-19 06:16:41
似乎每个策略都需要一行,而不是每一步一行。如果是这样的话,我会这样做:
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()
发布于 2020-04-22 21:45:04
谢谢你尤金。你的解决方案让我回到了正确的轨道上。我对它进行了更多的尝试,最终得到了以下结果:
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()
结果就是我想要的……
https://stackoverflow.com/questions/61297247
复制