散景交互式可视化不更新Jupyter笔记本中的数字

时间:2020-05-12 10:52:13

标签: python python-3.x jupyter-notebook bokeh

我最近刚开始使用bokeh,但我仍在努力使用它的某些功能。我要在这里实现的目标是,在Jupyter-notebook上构建交互式可视化效果,其中有两个图形和两个Select小部件。我的目标是,如果任何一个小部件中的值都发生更改,则会运行update_data()函数来更新所有必需的数据源,并且两个数字也会相应更改。在我的案例中,这两个数字在水平条形图中显示了足球的头对头统计数据,因此,如果一支球队发生变化,则整个对局都会发生变化,这就是为什么当其中一个变化时,两者都必须更新的原因。

运行代码时,图形和窗口小部件及其数据均与我想要的一样,但是问题是,当我更改其中一个窗口小部件中的值时,图形不会更新。在update_data()函数中,我可以看到调试时数据正确更改,但是图形从未更新。

我也尝试过在update_data函数中再次使用show(),但这只会导致最奇怪的结果,当值更改时在下面创建新的图形,但是即使新图形也不显示更新的数据,但是它给了我以下警告

WARNING:bokeh.embed.util:
You are generating standalone HTML/JS output, but trying to use real Python
callbacks (i.e. with on_change or on_event). This combination cannot work.

Only JavaScript callbacks may be used with standalone output. For more
information on JavaScript callbacks with Bokeh, see:

    https://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html

Alternatively, to use real Python callbacks, a Bokeh server application may
be used. For more information on building and running Bokeh applications, see:

    https://docs.bokeh.org/en/latest/docs/user_guide/server.html

我的猜测是,update_data()中的那些更新值仅在函数内更新,并且从不更改原始值。引用bokeh文档

如果不表示基础数据,则无法进行数据可视化。在本节中,将介绍为图提供数据的各种方式,从直接传递数据值到创建ColumnDataSource以及使用CDSView进行过滤

因此,看来我应该能够直接传递变量,也可以直接更改它们。那么,是否有可能使它按照我现在想要的方式工作,或者我在做什么错呢?是我定义应用程序或传递数据的方式,我应该更改什么?

#!/usr/bin/env python3

from bokeh.io import output_notebook
from bokeh.layouts import widgetbox
from bokeh.models.widgets import Select
from bokeh.layouts import row, column, layout
from bokeh.plotting import figure, show
from bokeh.application import Application
from bokeh.application.handlers import FunctionHandler

# Function returns a list of team names
def get_teamnames():
    ...
    return list_of_teamnames

# Function returns head to head statistics between two given teams
# it returns two dictionaries, for team1 and team2. The team1 dictionary
# keys are team1 player names, and their respective values are for example
# how many goals the player has scored against team2. Vice versa for the
# team2 dictionary.
def get_head2head(team1, team2, stat):
    ...
    return team1_data_dictionary, team2_data_dictionary

# Using jupyter-notebook, so output should be notebook
output_notebook()

# Team colors dictionary just associates each team with a color
# will be used later when creating charts
team_colors = {"liverpool": "#c8102E",...}

# First we plot using the following data
team1 = "manchester_united"
team2 = "liverpool"

data1, data2 = get_head2head(team1, team2, "goals")

data1_values = list(data1.values())
data1_keys = list(data1.keys())

data2_values = list(data2.values())
data2_keys = list(data2.keys())

# x_range defined so our two charts will have the same x-dimension
x_range = (min(min(data1_values), min(data2_values))-1, max(max(data1_values), max(data2_values))+1)

# To be able to fit logos in the background nicely, x and y
# positions are calculated
img_x = x_range[1] - ((abs(x_range[0])+abs(x_range[1]))/2)
img_y = len(data1_keys) / 2

# First figure configuration
fig1 = figure(y_range=data1_keys, title=team1, x_range=x_range, toolbar_location=None, tools="")
fig1.hbar(y=data1_keys, height=0.5, left=0, right=data1_values, color=team_colors[team1])
fig1.image_url(url=["../backups/logos/" + team1 + ".png"], x=img_x, y=img_y, w=20, h=20, global_alpha=0.05, anchor="center")

# Second figure configuration
fig2 = figure(y_range=data2_keys, title=team2, x_range=x_range, toolbar_location=None, tools="")
fig2.hbar(y=data2_keys, left=0, height=0.5, right=data2_values, color=team_colors[team2])
fig2.image_url(url=["../backups/logos/" + team2 + ".png"], x=img_x, y=img_y, w=20, h=20, global_alpha=0.05, anchor="center")


# Configure two Select widgets, both will have the same values that can be
# selected
select1 = Select(title="Team1:", value=get_teamnames()[0], options=get_teamnames())
select2 = Select(title="Team2:", value=get_teamnames()[1], options=get_teamnames())

# In this update_data function all data sources are updated.
# It will be run when either Select widget's value changes.
# It is important to always update both data sources, because
# we are displaying head to head statistics, so if one changes,
# both change
def update_data(attrname, old, new):
    team1 = select1.value
    team2 = select2.value

    data1, data2 = get_head2head(team1, team2, "plusminus")

    data1_values = list(data1.values())
    data1_keys = list(data1.keys())

    data2_values = list(data2.values())
    data2_keys = list(data2.keys())

    x_range = (min(min(data1_values), min(data2_values))-1, max(max(data1_values), max(data2_values))+1)
    img_x = x_range[1] - ((abs(x_range[0])+abs(x_range[1]))/2)
    img_y = len(data1_keys) / 2

# When either widget's value changes, update_data()
# will be run    
select1.on_change("value", update_data)
select2.on_change("value", update_data)


def modify_doc(doc):
    doc.add_root(l)

# Define layout and initialize the application
l = layout([fig1, fig2], [select1, select2], sizing_mode="scale_width")
handler = FunctionHandler(modify_doc)
app = Application(handler)
show(app)

为了使其具有交互性,我尝试复制此SO answer's模型。

0 个答案:

没有答案