Interactions

[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, plot_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 Tools

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(plot_width=300, plot_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=.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 TapTool, CustomJS, ColumnDataSource

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

p = figure(plot_width=600, plot_height=300, tools=[tap])

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

show(p)

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 CustomJS, ColumnDataSource, Slider

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

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

plot = figure(plot_width=400, plot_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=.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))

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

x = [random() for x in range(500)]
y = [random() for y in range(500)]
color = ["navy"] * len(x)

s1 = ColumnDataSource(data=dict(x=x, y=y, color=color))
p = figure(plot_width=400, plot_height=400, tools="lasso_select", title="Select Here")
p.circle('x', 'y', color='color', size=8, alpha=0.4, source=s1,
         selection_color="firebrick", selection_alpha=0.4)

s2 = ColumnDataSource(data=dict(xm=[0,1],ym=[0.5, 0.5]))
p.line(x='xm', y='ym', color="orange", line_width=5, alpha=0.6, source=s2)

s1.callback = CustomJS(args=dict(s1=s1, s2=s2), code="""
    var inds = s1.selected.indices;
    if (inds.length == 0)
        return;

    var ym = 0
    for (var i = 0; i < inds.length; i++) {
        ym += s1.data.y[inds[i]]
    }

    ym /= inds.length
    s2.data.ym = [ym, ym]

    // necessary becasue we mutated source.data in-place
    s2.change.emit();
""")

show(p)

CustomJS für UI-Events

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

[9]:
from bokeh.plotting import figure
from bokeh import events
from bokeh.models import CustomJS, Div, Button
from bokeh.layouts import column, row

import numpy as np
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 Interactions.