Interaktionen#

[1]:
from bokeh.io import output_notebook, show
from bokeh.plotting import figure


output_notebook()
Loading BokehJS ...

Verknüpfte Interaktionen#

Es ist möglich, verschiedenen Bokeh-Plots interaktiv zu verknüpfen. Z.B. können zwei oder mehr Plots verknüpft werden, wobei in einem der Plots verschoben, gezoomt o.ä. wird und die anderen Plots gleichzeitig aktualisiert werden. Es ist auch möglich, Auswahlen zwischen zwei Plots zu verknüpfen, sodass bei der Auswahl von Elementen in einem Plot auch die entsprechenden Elemente im zweiten Plot ausgewählt werden.

Verknüpftes Verschieben#

Verknüpftes Verschieben (wenn mehrere Darstellungen Bereiche haben, die synchron bleiben) ist mit Bokeh einfach zu realisieren. Ihr teilt einfach die entsprechenden Bereichsobjekte zwischen zwei (oder mehr) Plots auf. Das folgende Beispiel zeigt, wie ihr dies erreicht, indem ihr die Bereiche der drei Diagramme auf verschiedene Weise verknüpft:

[2]:
from bokeh.layouts import gridplot


x = list(range(11))
y0, y1, y2 = x, [10 - i for i in x], [abs(i - 5) for i in x]

plot_options = dict(width=250, height=250, tools="pan,wheel_zoom")

# create a new plot
s1 = figure(**plot_options)
s1.circle(x, y0, size=10, color="navy")

# create a new plot and share both ranges
s2 = figure(x_range=s1.x_range, y_range=s1.y_range, **plot_options)
s2.triangle(x, y1, size=10, color="firebrick")

# create a new plot and share only one range
s3 = figure(x_range=s1.x_range, **plot_options)
s3.square(x, y2, size=10, color="olive")

p = gridplot([[s1, s2, s3]])

# show the results
show(p)

Verknüpfte Auswahl#

Das Verknüpfen von Auswahlen erfolgt auf ähnliche Weise, indem Datenquellen zwischen Plots gemeinsam genutzt werden. Beachtet, dass normalerweise mit bokeh.plotting und bokeh.charts die Erstellung einer Standarddatenquelle für einfache Darstellungen automatisch erfolgt. Um jedoch eine Datenquelle gemeinsam zu nutzen, müssen wir sie manuell erstellen und explizit übergeben. Dies wird im folgenden Beispiel veranschaulicht:

[3]:
from bokeh.models import ColumnDataSource


x = list(range(-20, 21))
y0, y1 = [abs(xx) for xx in x], [xx**2 for xx in x]

# create a column data source for the plots to share
source = ColumnDataSource(data=dict(x=x, y0=y0, y1=y1))

TOOLS = "box_select,lasso_select,help"

# create a new plot and add a renderer
left = figure(tools=TOOLS, width=300, height=300)
left.circle("x", "y0", source=source)

# create another new plot and add a renderer
right = figure(tools=TOOLS, width=300, height=300)
right.circle("x", "y1", source=source)

p = gridplot([[left, right]])

show(p)

Hover Tool#

Bokeh verfügt über ein Hover-Tool, mit dem zusätzliche Informationen in einem Popup angezeigt werden können, wenn der Mauszeiger über einer bestimmten Glyphe schwebt. Die grundlegende Konfiguration des Hover-Tools bedeutet, dass eine Liste von (name, format)-Tupeln bereitgestellt wird. Die vollständigen Details findet ihr im User’s Guide.

Das folgende Beispiel zeigt einige grundlegende Anwendungen des Hover-Werkzeugs mit einer Kreis-Glyphe, wobei die in utils.py definierten Hover-Informationen verwendet werden:

[4]:
from bokeh.models import HoverTool


source = ColumnDataSource(
    data=dict(
        x=[1, 2, 3, 4, 5],
        y=[2, 5, 8, 2, 7],
        desc=["A", "b", "C", "d", "E"],
    )
)

hover = HoverTool(
    tooltips=[
        ("index", "$index"),
        ("(x,y)", "($x, $y)"),
        ("desc", "@desc"),
    ]
)

p = figure(
    width=300, height=300, tools=[hover], title="Mouse over the dots"
)

p.circle("x", "y", size=20, source=source)

show(p)

Widgets#

Bokeh unterstützt die direkte Integration mit einem kleinen grundlegenden Widget-Set. Dies kann in Verbindung mit einem Bokeh-Server oder mit CustomJS-Modellen verwendet werden, um eure Dokumente interaktiver zu gestalten. Eine vollständige Liste mit Beispielcode findet ihr im Abschnitt Adding Widgets des Benutzerhandbuchs.

Um die Widgets zu verwenden, fügt sie wie ein Plotobjekt in ein Layout ein:

[5]:
from bokeh.models import Column
from bokeh.models.widgets import Slider


slider = Slider(start=0, end=10, value=1, step=0.1, title="foo")

show(Column(slider))

CustomJS Callbacks#

Damit ein Widget nützlich wird, muss es Aktionen ausführen können. Mit Hilfe des Bokeh-Servers können Widgets echten Python-Code ausführen. Diese Möglichkeit wird unter Bokeh-Server erläutert. Hier betrachten wir, wie Widgets mit CustomJS-Callbacks konfiguriert werden können, die JavaScript-Code-Snippets ausführen.

[6]:
from bokeh.models import ColumnDataSource, CustomJS, TapTool


callback = CustomJS(code="alert('you tapped a circle!')")
tap = TapTool(callback=callback)

p = figure(width=600, height=300, tools=[tap])

p.circle(x=[1, 2, 3, 4, 5], y=[2, 5, 8, 2, 7], size=20)

show(p)

Beispiel für ein Slider-Widget#

Das folgende Beispiel zeigt eine an einen Slider angehängte Aktion, mit der eine Datenquelle aktualisiert wird.

[7]:
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, CustomJS, Slider


x = [x * 0.005 for x in range(0, 201)]

source = ColumnDataSource(data=dict(x=x, y=x))

plot = figure(width=400, height=400)
plot.line("x", "y", source=source, line_width=3, line_alpha=0.6)

slider = Slider(start=0.1, end=6, value=1, step=0.1, title="power")

update_curve = CustomJS(
    args=dict(source=source, slider=slider),
    code="""
    var data = source.data;
    var f = slider.value;
    x = data['x']
    y = data['y']
    for (i = 0; i < x.length; i++) {
        y[i] = Math.pow(x[i], f)
    }

    // necessary becasue we mutated source.data in-place
    source.change.emit();
""",
)
slider.js_on_change("value", update_curve)


show(column(slider, plot))

Beispiel für eine Datenauswahl#

Es ist auch möglich, JavaScript-Aktionen auszuführen, wenn sich eine Benutzerauswahl (z.B. Box, Punkt, Lasso) ändert. Dies geschieht durch Anhängen derselben Art von CustomJS-Objekt an die Datenquelle, für die die Auswahl getroffen wird.

Das folgende Beispiel ist etwas komplexer und zeigt die Aktualisierung einer Datenquelle einer Glyphe als Reaktion auf die Auswahl einer anderen Glyphe:

[8]:
from random import random

from bokeh.layouts import row
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.plotting import figure, output_file, show


x = [random() for x in range(500)]
y = [random() for y in range(500)]

s1 = ColumnDataSource(data=dict(x=x, y=y))
p1 = figure(width=400, height=400, tools="lasso_select", title="Select Here")
p1.circle("x", "y", source=s1, alpha=0.6)

s2 = ColumnDataSource(data=dict(x=[], y=[]))
p2 = figure(
    width=400,
    height=400,
    x_range=(0, 1),
    y_range=(0, 1),
    tools="",
    title="Watch Here",
)
p2.circle("x", "y", source=s2, alpha=0.6)

s1.selected.js_on_change(
    "indices",
    CustomJS(
        args=dict(s1=s1, s2=s2),
        code="""
        const inds = cb_obj.indices;
        const d1 = s1.data;
        const d2 = s2.data;
        d2['x'] = []
        d2['y'] = []
        for (let i = 0; i < inds.length; i++) {
            d2['x'].push(d1['x'][inds[i]])
            d2['y'].push(d1['y'][inds[i]])
        }
        s2.change.emit();
    """,
    ),
)

layout = row(p1, p2)

show(layout)

CustomJS für UI-Events#

Alle verfügbaren UI-Ereignisse und ihre Eigenschaften sind im Abschnitt bokeh.events aufgeführt.

[9]:
import numpy as np

from bokeh import events
from bokeh.layouts import column, row
from bokeh.models import Button, CustomJS, Div
from bokeh.plotting import figure


x = np.random.random(size=2000) * 100
y = np.random.random(size=2000) * 100

p = figure(tools="box_select")
p.scatter(x, y, radius=1, fill_alpha=0.6, line_color=None)

div = Div(width=400)
button = Button(label="Button")
layout = column(button, row(p, div))

# Events with no attributes
button.js_on_event(
    events.ButtonClick,
    CustomJS(
        args=dict(div=div),
        code="""
div.text = "Button!";
""",
    ),
)

p.js_on_event(
    events.SelectionGeometry,
    CustomJS(
        args=dict(div=div),
        code="""
div.text = "Selection! <p> <p>" + JSON.stringify(cb_obj.geometry, undefined, 2);
""",
    ),
)

show(layout)

Zusätzliche Informationen#

Es gibt viele Arten von Interaktionen und Ereignissen, die mit CustomJS-Rückrufen verbunden werden können:

  • Widgets: - Button, Toggle, Dropdown, TextInput, AutocompleteInput, Select, Multiselect, Slider, (DateRangeSlider), DatePicker,

  • Tools: - TapTool, BoxSelectTool, HoverTool,

  • Selection: - ColumnDataSource, AjaxDataSource, BlazeDataSource, ServerDataSource

  • Ranges: - Range1d, DataRange1d, FactorRange

Vollständigere Beispiele findet ihr in JavaScript callbacks.