【问题标题】:How to pass the slider value in Bokeh back to Python code如何将 Bokeh 中的滑块值传递回 Python 代码
【发布时间】:2019-03-26 17:22:16
【问题描述】:

我想将一个滑块值(我使用 Bokeh 构建的)传回我的 Python 代码。该代码在绘图上生成 2 条线,并允许我更改其中一条的斜率和截距。但是当我引入回调 javascript 以将滑块值作为 "ff" 传递回我的 Python 代码时,它失败了。
你能帮我把滑块值返回给 python 的回调语法吗(例如,参见代码的最后一行 print(ff)) - 我确实想做一些比最终打印出来更有趣的事情!
来自回调的错误消息是:

ValueError: 期望一个 Dict(String, Instance(Model)) 的元素,得到 {'my_dict': {'s': 0.5}}

我的代码是:-

from ipywidgets import interact  
import numpy as np  
from bokeh.io import push_notebook, show, output_notebook  
from bokeh.plotting import figure  
from bokeh.models import ColumnDataSource  
from bokeh.models.callbacks import CustomJS  
output_notebook()  
x = np.linspace(0, 20, 200) # create equally spaced points.  
s = 0.5 # slope.  
i = 3 # intercept.  
y = s * x + i # straight line.  
my_dict = dict(s=s) # need to create a dict object to hold what gets passed in the callback.  

callback = CustomJS(args=dict(my_dict=my_dict), code="""  
  var ff = cb_obj.value  
  my_dict.change.emit()  
""")  
// ff should be the slider value.  

p = figure(title="simple line example", plot_height=300, plot_width=600, y_range=(-20,20),  
           background_fill_color='#efefef')  
r = p.line(x, y, color="#8888cc", line_width=1.5, alpha=0.8) # 1st line. This line can be controlled by sliders.  
q = p.line(x, 2*x+1.2, color="#0088cc", line_width=1.9, alpha=0.2) # 2nd line.  
def update(w=s, a=i):  
    r.data_source.data['y'] = w * x + a  # allow updates for the line r.  
    push_notebook()  
show(p, notebook_handle=True)  
interact(update, w=(-10,10), a=(-12,12) )  
print(ff)  # Return what the slider value is. I want ff accessible back in my python code.

【问题讨论】:

    标签: python callback bokeh


    【解决方案1】:

    我没有 Jupyter Notebook,所以这两个示例是纯 Bokeh 应用程序,第一个使用 JS 回调,第二个使用 Python 回调(Bokeh v1.0.4)。

    import numpy as np
    from bokeh.plotting import figure, show
    from bokeh.models import ColumnDataSource, Slider, CustomJS, Range1d
    from bokeh.layouts import column
    
    slider_slope = Slider(start = 0, end = 1, value = 0.5, step = 0.1)
    slider_intercept = Slider(start = 0, end = 20, value = 10, step = 1)
    
    slider_code = '''   i = slider_intercept.value
                        s = slider_slope.value
                        x = r.data_source.data['x'];
                        y = [];
    
                        for (index = 0; index < x.length; index ++)
                            y.push((s * x[index]) + i);
    
                        r.data_source.data['y'] = y
                        r.data_source.change.emit(); '''
    
    s = slider_slope.value  # slope.
    i = slider_intercept.value  # intercept.
    
    x = np.linspace(-40, 20, 200)
    y = [(s * xx + i) for xx in x]
    
    p = figure(title = "simple line example", plot_height = 500, plot_width = 600, y_range = Range1d(start = -80, end = 40), background_fill_color = '#efefef')
    r = p.line(x, y, color = "red", line_width = 1.5, alpha = 0.8)  # 1st line. This line can be controlled by sliders.
    q = p.line(x, 2 * x + 1.2, color = "blue", line_width = 1.9, alpha = 0.2)  # 2nd line.
    
    slider_callback = CustomJS(args = dict(slider_slope = slider_slope,
                                    slider_intercept = slider_intercept,
                                    r = r), code = slider_code)
    
    slider_slope.callback = slider_callback
    slider_intercept.callback = slider_callback
    
    layout = column(p, slider_slope, slider_intercept)
    show(layout, notebook_handle = True)
    

    您可以使用 Python 回调轻松地将其转换为 Bokeh 服务器应用程序:

    import numpy as np
    from bokeh.plotting import figure, show, curdoc
    from bokeh.models import Slider, CustomJS
    from bokeh.layouts import column
    
    slider_slope = Slider(title = 'Slope', start = 0, end = 1, value = 0.5, step = 0.1)
    slider_intercept = Slider(title = 'Intercept', start = 0, end = 20, value = 10, step = 1)
    
    s = slider_slope.value  # slope.
    i = slider_intercept.value  # intercept.
    
    x = np.linspace(-40, 20, 200)
    y = [(s * xx + i) for xx in x]
    
    p = figure(title = "simple line example", plot_height = 500, plot_width = 600, y_range = Range1d(start = -80, end = 40), background_fill_color = '#efefef')
    r = p.line(x, y, color = "red", line_width = 1.5, alpha = 0.8)  # 1st line. This line can be controlled by sliders.
    q = p.line(x, 2 * x + 1.2, color = "blue", line_width = 1.9, alpha = 0.2)  # 2nd line.
    
    def update(attr, old, new):
        s = slider_slope.value  # slope.
        i = slider_intercept.value  # intercept
        x = r.data_source.data['x'];
        y = []
    
        for value in x:
            y.append((s * value) + i)
    
        r.data_source.data['y'] = y
    
    slider_slope.on_change('value', update)
    slider_intercept.on_change('value', update)
    
    layout = column(p, slider_slope, slider_intercept)
    curdoc().add_root(layout)
    

    结果:

    【讨论】:

    • 嗨托尼。我已经运行了你的代码,但我仍然无法让它做我想做的事——即在 Python 中返回用户选择的滑块值。我在代码末尾所做的是在 print(s,i) print(slider_intercept.value,slider_slope.value) print(slider_slope.callback , slider_intercept.callback) 中添加行,它们只返回起始值(在我移动滑块值之前)或一些奇怪的 javascript 值。
    • 第一个代码是带有 JS 回调的纯 BokehJS 应用程序,因此您可以使用 python app.py 在终端中运行它。第二个代码是带有 Python 回调的 Bokeh 服务器应用程序,因此您需要将其作为 bokeh serve --show app.py 运行。您可以在 Bokeh 服务器应用程序中使用 Python 回调。它正在按照图像上显示的方式工作。
    • 非常感谢 Tony 的解释和您的帮助 - 是的,您的代码运行良好! (虽然我没有使用服务器)。我有点担心服务器的复杂性(对于代码用户 [和我!]),但我会看看它们。
    • 我有一些工作代码可以将滑块值保存回 csv 文件:
    【解决方案2】:

    这就是解决方案。它创建了一个散景服务器应用程序。它通过使用名为的文件运行(从 spyder):20190328_start_bokeh_server.py 有一条由滑块绘制和控制的直线。单击该按钮会将滑块值保存到 csv 文件中。

    要运行以下代码,请在控制台中使用此代码(包含在 20190404_start_bokeh_server.py 中):

    导入操作系统 os.chdir("C:\Users") # 将工作目录更改为脚本位置。 os.system("start call bokeh serve --show 20190404_bokeh_server.py ") # 或者:一旦我导航到保存 .py 文件的目录,可以在 anacondas 提示符中键入此命令。 """

    import pandas as pd
    import numpy as np
    from random import random
    from numpy.random import randn
    
    from bokeh.plotting import figure, show, curdoc
    from bokeh.models import Slider, CustomJS, Range1d, Button
    from bokeh.layouts import column
    from bokeh.plotting import figure, curdoc
    import os
    
    slider_slope = Slider(title = 'Slope', start = 0, end = 1, value = 0.5, step = 0.1)
    slider_intercept = Slider(title = 'Intercept', start = 0, end = 20, value = 10, step = 1)
    
    s = slider_slope.value  # slope.
    i = slider_intercept.value  # intercept.
    
    x = np.linspace(-40, 20, 200)
    y = [(s * xx + i) for xx in x]
    
    p = figure(title = "simple line example", plot_height = 500, plot_width = 600, y_range = Range1d(start = -80, end = 40), background_fill_color = '#efefef')
    r = p.line(x, y, color = "red", line_width = 1.5, alpha = 0.8)  # 1st line. This line can be controlled by sliders.
    q = p.line(x, 2 * x + 1.2, color = "blue", line_width = 1.9, alpha = 0.2)  # 2nd line. This could be actuals.
    
    def update(attr, old, new):
        s = slider_slope.value  # slope.
        i = slider_intercept.value  # intercept
        x = r.data_source.data['x'];
        y = []
    
        for value in x:
            y.append((s * value) + i)
    
        r.data_source.data['y'] = y
    
    # create a callback that will save the slider settings to a csv file when the button is clicked.
    def callback():
        os.chdir("C:\\Users") # Change the working directory to where I want to save the csv.
        mydf = pd.DataFrame.from_dict({'slope':[0],'intercept':[0]}) # Create a DataFrame using pandas, based on a dictionary definition. Set the values to be 0 by default.
        mydf.loc[0] = [slider_slope.value, slider_intercept.value] # Assign the first row to slope and intercept.
        mydf.to_csv('slider.csv',index=True) # Write to the csv the final values of the button.  
    
    # add a button widget and configure with the call back
    button = Button(label="Save slope and intercept to csv")
    button.on_click(callback)
    
    slider_slope.on_change('value', update)
    slider_intercept.on_change('value', update)
    
    layout = column(p, slider_slope, slider_intercept, button)
    curdoc().add_root(layout)
    show(layout, notebook_handle = True) # Launch the chart in the web browser.
    

    【讨论】:

      猜你喜欢
      • 2020-06-28
      • 2020-08-31
      • 2021-10-15
      • 2018-04-01
      • 2020-01-27
      • 1970-01-01
      • 1970-01-01
      • 2017-05-24
      • 1970-01-01
      相关资源
      最近更新 更多