Afin de comprendre le plus soigneusement possible l'intrigue interactive de bokeh, je vais essayer de le déchiffrer en utilisant [Échantillon abordable] 0 comme exemple. J'ai écrit les spécifications comme prévu à partir du code source, mais comme j'ai également vérifié la documentation, cela ne devrait faire aucun doute. Le code source complet est [ici] 1
Un tel mec ↓
# create three normal population samples with different parameters
x1 = np.random.normal(loc=5.0, size=400) * 100
y1 = np.random.normal(loc=10.0, size=400) * 10
x2 = np.random.normal(loc=5.0, size=800) * 50
y2 = np.random.normal(loc=5.0, size=800) * 10
x3 = np.random.normal(loc=55.0, size=200) * 10
y3 = np.random.normal(loc=4.0, size=200) * 10
x = np.concatenate((x1, x2, x3))
y = np.concatenate((y1, y2, y3))
L'explication est omise car elle n'est pas liée au bokeh.
TOOLS="pan,wheel_zoom,box_select,lasso_select,reset"
# create the scatter plot
p = figure(tools=TOOLS, plot_width=600, plot_height=600, min_border=10, min_border_left=50,
toolbar_location="above", x_axis_location=None, y_axis_location=None,
title="Linked Histograms")
p.background_fill_color = "#fafafa"
↑ Donnez à l'objet figure le nom de l'outil que vous souhaitez afficher et définissez la couleur d'arrière-plan. Jusqu'à ce point, le flux est le même qu'un graphique normal. figure est une fonction de bokeh.models.figure et sa valeur de retour est bokeh.models.Figure. Je vais y mettre divers paramètres.
p.select(BoxSelectTool).select_every_mousemove = False
p.select(LassoSelectTool).select_every_mousemove = False
↑ Faites attention d'ici. Premièrement, p.select () est une méthode de figure, et la principale est la méthode de source d'héritage bokeh.models.Model. Étant donné un objet de classe pour le sélecteur, renvoie le type de sélecteur approprié affecté à la figure. Par exemple, dans la première ligne, une instance de la classe BoxSelectTool est obtenue et en définissant select_every_mousemove sur False, la mise à jour ne se produit pas tant que la sélection de la souris n'est pas terminée.
Référence →
select(selector) Query this object and all of its references for objects that match the given selector.
r = p.scatter(x, y, size=3, color="#3A5785", alpha=0.6)
Voici un graphique des points! La valeur de retour r est utilisée en dernier. La valeur de retour lorsqu'elle est tracée est généralement renvoyée par une instance de la classe GlyphRenderer, qui peut être utilisée ultérieurement pour peaufiner le tracé.
# create the horizontal histogram
hhist, hedges = np.histogram(x, bins=20)
hzeros = np.zeros(len(hedges)-1)
hmax = max(hhist)*1.1
↑ La valeur de retour de numpy.histrogram () est (liste des hauteurs de chaque histogramme, liste des valeurs limites de l'histogramme). le bokeh n'a pas d'importance.
LINE_ARGS = dict(color="#3A5785", line_color=None)
ph = figure(toolbar_location=None, plot_width=p.plot_width, plot_height=200, x_range=p.x_range,
y_range=(-hmax, hmax), min_border=10, min_border_left=50, y_axis_location="right")
ph.xgrid.grid_line_color = None
ph.yaxis.major_label_orientation = np.pi/4
ph.background_fill_color = "#fafafa"
ph.quad(bottom=0, left=hedges[:-1], right=hedges[1:], top=hhist, color="white", line_color="#3A5785")
↑ Histogramme horizontal. Il existe de nombreux éléments de réglage et c'est compliqué, mais le flux de base reste le même. J'obtiens une figure avec figure () et dessine un quadrangle avec sa méthode quad (). (Il semble que ce soit une méthode qui trace un rectangle parallèle aux coordonnées, pas une méthode dédiée à l'histogramme). Les coordonnées des quatre côtés sont spécifiées par bas ~ haut. Chaque quadrangle semble être une instance de la classe bokeh.models.glyphs.Quad.
Les autres arguments qui semblent utiles sont les suivants.
toolbar_location=None #Masquer la barre de tâches
plot_width=p.plot_width #Partage de la largeur du tracé
x_range=p.x_range #x Partage de plage de coordonnées
min_border_left=50 #Marge minimale sur le côté gauche du tracé
ph.yaxis.major_label_orientation = np.pi/4 #Rotation des étiquettes de coordonnées
L'histogramme qui s'affiche lorsque vous sélectionnez un groupe de points,
hh1 = ph.quad(bottom=0, left=hedges[:-1], right=hedges[1:], top=hzeros, alpha=0.5, **LINE_ARGS)
hh2 = ph.quad(bottom=0, left=hedges[:-1], right=hedges[1:], top=hzeros, alpha=0.1, **LINE_ARGS)
Je l'ai dessiné en premier. À ce stade, top = hzeros, donc la hauteur est définie sur 0 et elle n'est pas visible. Il semble que la hauteur de ces valeurs de retour hh1 et hh2 soit mise à jour lorsqu'elle est sélectionnée (*).
# create the vertical histogram
vhist, vedges = np.histogram(y, bins=20)
vzeros = np.zeros(len(vedges)-1)
vmax = max(vhist)*1.1
pv = figure(toolbar_location=None, plot_width=200, plot_height=p.plot_height, x_range=(-vmax, vmax),
y_range=p.y_range, min_border=10, y_axis_location="right")
pv.ygrid.grid_line_color = None
pv.xaxis.major_label_orientation = np.pi/4
pv.background_fill_color = "#fafafa"
pv.quad(left=0, bottom=vedges[:-1], top=vedges[1:], right=vhist, color="white", line_color="#3A5785")
vh1 = pv.quad(left=0, bottom=vedges[:-1], top=vedges[1:], right=vzeros, alpha=0.5, **LINE_ARGS)
vh2 = pv.quad(left=0, bottom=vedges[:-1], top=vedges[1:], right=vzeros, alpha=0.1, **LINE_ARGS)
Il est réglé de la même manière que dans le sens horizontal.
Assemblez les anciens.
layout = gridplot([[p, pv], [ph, None]], merge_tools=False)
↑ Premièrement, les figures sont disposées en deux dimensions avec la fonction bokeh.layouts.girdplot (). À propos, si vous souhaitez simplement organiser verticalement ou horizontalement au lieu de deux dimensions, utilisez bokeh.layouts.column ou row.
curdoc().add_root(layout)
curdoc().title = "Selection Histogram"
↑ curdoc est une abréviation pour le document courant, qui récupère le Document par défaut (une classe qui résume la sortie de bokeh) et le lie à la grille avec la méthode add_root (). C'est le point, mais il semble que la spécification est que "lorsqu'une modification est apportée à la grille add_root, le rappel enregistré dans Document avec" on_change "est appelé." De la référence →
add_root(model, setter=None)
Add a model as a root of this Document. Any changes to this model (including to other models referred to by it) will trigger on_change callbacks registered on this document.
Donc, si vous regardez d'abord la dernière ligne,
r.data_source.selected.on_change('indices', update)
Il est vrai que le callback est enregistré dans on_change.
R ici est la valeur de retour de scatter plot () (une instance de la classe GlyphRenderer) r.data_source correspond à l'ensemble de données tracé et sa sélection correspond à la partie sélectionnée des données.
on_chage () est une méthode de bokeh.model.Model qui enregistre un rappel pour cet objet.
def on_change(self, attr, *callbacks):
''' Add a callback on this object to trigger when ``attr`` changes.
Args:
attr (str) : an attribute name on this object
*callbacks (callable) : callback functions to register
Le premier argument attr est difficile à comprendre, mais ici nous décrivons "quels changements pour appeler le callback". Cette fois, puisqu'il s'agit d'une plage de sélection (= index de données = selection.indices), "indices" est spécifié.
Si vous souhaitez associer un rappel à un événement spécifique (appui sur un bouton, mouvement du curseur, etc.) au lieu de le modifier
on_event(event, callback)
utilisation.
(Mis à part ce qui suit) Ici, les fonctions Python sont enregistrées, mais si vous souhaitez enregistrer des fonctions écrites en javascript (une instance de bokeh.models.CustomJS)
m.js_on_change(attr, callback)
Devrait être utilisé. Cette zone est décrite dans le [Chapitre 6 du tutoriel officiel] 1. Il semble que vous deviez l'utiliser si vous voulez une seule sortie html. Avertissement à l'exécution →
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
(À part d'ici)
Enfin, à propos du contenu de la fonction de rappel update ()
def update(attr, old, new):
inds = new
if len(inds) == 0 or len(inds) == len(x):
hhist1, hhist2 = hzeros, hzeros
vhist1, vhist2 = vzeros, vzeros
else:
neg_inds = np.ones_like(x, dtype=np.bool)
neg_inds[inds] = False
hhist1, _ = np.histogram(x[inds], bins=hedges)
vhist1, _ = np.histogram(y[inds], bins=vedges)
hhist2, _ = np.histogram(x[neg_inds], bins=hedges)
vhist2, _ = np.histogram(y[neg_inds], bins=vedges)
hh1.data_source.data["top"] = hhist1
hh2.data_source.data["top"] = -hhist2
vh1.data_source.data["right"] = vhist1
vh2.data_source.data["right"] = -vhist2
Il semble que attr spécifié par on_change et les valeurs anciennes et nouvelles avant et après le changement de l'attribut soient données en arguments. Ici, nouveau est l'index du groupe de points nouvellement sélectionné. Comme prédit par (*), la valeur supérieure de l'histogramme horizontal est mise à jour en fonction de l'index sélectionné. La relation de référence est un peu loin, mais
Jeu de données d'origine ↑ nuage de points (= partager la source de données avec un histogramme horizontal) ↑ selection ↑ rappel on_change
On peut voir que l'index donné à l'argument on_change est le même que l'index du premier ensemble de données car il est connecté comme ceci.
[Échantillon officiel] 0 [Code source] 1
Recommended Posts