web-dev-qa-db-fra.com

Comment obtenir un bokeh interactif dans le cahier Jupyter

Je me prépare à utiliser le bokeh pour une implémentation interactive en ligne de certains modèles python que j'ai écrits).

L'étape 1 consiste à comprendre quelques exemples interactifs de base, mais je ne peux pas obtenir les exemples d'introduction exécutés de manière INTERACTIVE dans un cahier Jupyter. J'espère que quelqu'un pourra corriger ma mauvaise compréhension de ce qui est un copier-coller du propre code d'exemple de bokeh.

Je suis conscient que la documentation de Bokeh n'est pas parfaite (j'ai corrigé une référence obsolète à bokeh.plotting.show plutôt que io.show), mais je pense que la structure de base que j'utilise devrait être proche de la corriger.

le code est basé sur: https://github.com/bokeh/bokeh/blob/master/examples/app/sliders.py

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

############ START BOILERPLATE ############
#### Interactivity -- BOKEH
import bokeh.plotting.figure as bk_figure
from bokeh.io import curdoc, show
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Slider, TextInput
from bokeh.io import output_notebook # enables plot interface in J notebook
# init bokeh
output_notebook()
############ END BOILERPLATE ############

# Set up data
N = 200
x = np.linspace(0, 4*np.pi, N)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))

# Set up plot
plot = bk_figure(plot_height=400, plot_width=400, title="my sine wave",
              tools="crosshair,pan,reset,save,wheel_zoom",
              x_range=[0, 4*np.pi], y_range=[-2.5, 2.5])

plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)

# Set up widgets
text = TextInput(title="title", value='my sine wave')
offset = Slider(title="offset", value=0.0, start=-5.0, end=5.0, step=0.1)
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0, step=0.1)
phase = Slider(title="phase", value=0.0, start=0.0, end=2*np.pi)
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1, step=0.1)

# Set up callbacks
def update_title(attrname, old, new):
    plot.title.text = text.value

text.on_change('value', update_title)

def update_data(attrname, old, new):
    # Get the current slider values
    a = amplitude.value
    b = offset.value
    w = phase.value
    k = freq.value

    # Generate the new curve
    x = np.linspace(0, 4*np.pi, N)
    y = a*np.sin(k*x + w) + b

    source.data = dict(x=x, y=y)
    ### I thought I might need a show() here, but it doesn't make a difference if I add one
    # show(layout)

for w in [offset, amplitude, phase, freq]:
    w.on_change('value', update_data)


# Set up layouts and add to document
inputs = widgetbox(text, offset, amplitude, phase, freq)
layout = row(plot,
             widgetbox(text, offset, amplitude, phase, freq))
curdoc().add_root(row(inputs, layout, width=800))
curdoc().title = "Sliders"

show(layout)

Je génère un tracé comme ci-dessous, mais la figure ne se met pas à jour lorsque les curseurs sont déplacés (ni lorsque le texte du titre est mis à jour) The static plot, teasing us with its sliders

Merci beaucoup pour toutes suggestions.

PS. J'essaie de garder ce code aussi près que possible de quelque chose que je peux implémenter avec des fichiers .py sur un serveur, évitant ainsi les solutions de contournement spécifiques à jupyter comme Push_notebook.

5
Mark_Anderson

Je suis d'accord (en tant qu'utilisateur) que les documents pourraient être meilleurs à ce sujet. J'ai dû chercher beaucoup pour trouver la procédure, mais quand vous la trouvez, ce n'est pas si difficile! J'ai modifié votre code, vous pouvez l'exécuter dans le cahier Jupyter.

L'astuce est:

from bokeh.application import Application
from bokeh.application.handlers import FunctionHandler
.
.
<your code here>
.
.
#add server-related code inside this modify_doc function
def modify_doc(doc): #use doc as you use curdoc() in bokeh server
    doc.add_root(<your_layout>)
    doc.on_change(...)
    doc.add_periodic_callback(...) 


handler = FunctionHandler(modify_doc)
app = Application(handler)
show(app)

et la version modifiée de votre code:

############ START BOILERPLATE ############
#### Interactivity -- BOKEH
import bokeh.plotting.figure as bk_figure
from bokeh.io import curdoc, show
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Slider, TextInput
from bokeh.io import output_notebook # enables plot interface in J notebook
import numpy as np
# init bokeh

from bokeh.application import Application
from bokeh.application.handlers import FunctionHandler


output_notebook()
############ END BOILERPLATE ############

# Set up data
N = 200
x = np.linspace(0, 4*np.pi, N)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))

# Set up plot
plot = bk_figure(plot_height=400, plot_width=400, title="my sine wave",
              tools="crosshair,pan,reset,save,wheel_zoom",
              x_range=[0, 4*np.pi], y_range=[-2.5, 2.5])

plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)

# Set up widgets
text = TextInput(title="title", value='my sine wave')
offset = Slider(title="offset", value=0.0, start=-5.0, end=5.0, step=0.1)
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0, step=0.1)
phase = Slider(title="phase", value=0.0, start=0.0, end=2*np.pi)
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1, step=0.1)

# Set up callbacks
def update_title(attrname, old, new):
    plot.title.text = text.value



def update_data(attrname, old, new):
    # Get the current slider values
    a = amplitude.value
    b = offset.value
    w = phase.value
    k = freq.value

    # Generate the new curve
    x = np.linspace(0, 4*np.pi, N)
    y = a*np.sin(k*x + w) + b

    source.data = dict(x=x, y=y)
    ### I thought I might need a show() here, but it doesn't make a difference if I add one
    # show(layout)

for w in [offset, amplitude, phase, freq]:
    w.on_change('value', update_data)


# Set up layouts and add to document
inputs = widgetbox(text, offset, amplitude, phase, freq)
layout = row(plot,
             widgetbox(text, offset, amplitude, phase, freq))



def modify_doc(doc):
    doc.add_root(row(layout, width=800))
    doc.title = "Sliders"
    text.on_change('value', update_title)


handler = FunctionHandler(modify_doc)
app = Application(handler)
show(app)
5
Hossein