File size: 24,187 Bytes
b9a0f21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
# Plotting with Bokeh


```python
import numpy as np
import pandas as pd
import holoviews as hv
from holoviews import dim, opts

hv.extension('bokeh')
```

One of the major design principles of HoloViews is that the declaration of data is completely independent from the plotting implementation. Bokeh provides a powerful platform to generate interactive plots using HTML5 canvas and WebGL, and is ideally suited towards interactive exploration of data. By combining the ease of generating interactive, high-dimensional visualizations with the interactive widgets and fast rendering provided by Bokeh, HoloViews becomes even more powerful.

This user guide will cover various interactive features that bokeh provides which is not covered by the more general user guides, including interactive tools, linked axes and brushing and more. The general principles behind customizing plots and styling the visual elements of a plot are covered in the [Style Mapping](04-Style_Mapping.ipynb) and [Customizing Plots](Customizing_Plots.ipynb) user guides.

## Working with bokeh directly

When HoloViews outputs bokeh plots it creates and manipulates bokeh models in the background. If at any time you need access to the underlying Bokeh representation of an object you can use the ``hv.render`` function to convert it. For example let us convert a HoloViews ``Image`` to a bokeh Figure, which will let us access and modify every aspect of the plot:


```python
img = hv.Image(np.random.rand(10, 10))

fig = hv.render(img)

print('Figure: ', fig)
print('Renderers: ', fig.renderers[-1].glyph)
```

## Exporting static files

Bokeh supports static export to png's using ``Selenium``, ``PhantomJS`` and ``pillow``, to install the required dependencies run:

```
conda install selenium phantomjs pillow
```

alternatively install PhantomJS from npm using:

```
npm install -g phantomjs-prebuilt
```

To switch to png output persistently you can run:

```
hv.output(fig='png')
```


```python
violin = hv.Violin(np.random.randn(100))

hv.output(violin, fig='png')
```

The exported png can also be saved to disk using the ``save`` function by changing the file extension from ``.html``, which exports an interactive plot, ``.png``:

```python
hv.save(violin, 'violin.png')
```

## Element Style options

One of the major benefits of bokeh is that it was designed from the ground up with consistency in mind, therefore most style options are a combination of the fill, line, and text style options listed below:


```python
from holoviews.plotting.bokeh.styles import (line_properties, fill_properties, text_properties)
print("""
Line properties: %s\n
Fill properties: %s\n
Text properties: %s
""" % (line_properties, fill_properties, text_properties))
```

Note also that most of these options support vectorized style mapping as described in the [Style Mapping user guide](04-Style_Mapping.ipynb). Here's an example of HoloViews Elements using a Bokeh backend, with bokeh's style options:


```python
curve_opts = opts.Curve(line_width=10, line_color='indianred', line_dash='dotted', line_alpha=0.5)
point_opts = opts.Points(fill_color='#00AA00', fill_alpha=0.5, line_width=1, line_color='black', size=5)
text_opts  = opts.Text(text_align='center', text_baseline='middle', text_color='gray', text_font='Arial')

xs = np.linspace(0, np.pi*4, 100)
data = (xs, np.sin(xs))

(hv.Curve(data) + hv.Points(data) + hv.Text(6, 0, 'Here is some text')).opts(
    curve_opts, point_opts, text_opts)
```

Notice that because the first two plots use the same underlying data, they become linked, such that zooming or panning one of the plots makes the corresponding change on the other.

## Setting Backend Opts

HoloViews does not expose every single option from Bokeh.

Instead, HoloViews allow users to attach [hooks](Customizing_Plots.ipynb#plot-hooks) to modify the plot object directly--but writing these hooks could be cumbersome, especially if it's only used for a single line of update.

Fortunately, HoloViews allows `backend_opts` for the Bokeh backend to configure options by declaring a dictionary with accessor specification for updating the plot components.

For example, here's how to make the toolbar auto-hide.


```python
hv.Curve(data).opts(
    backend_opts={"plot.toolbar.autohide": True}
)
```

The following is the equivalent, as a hook.


```python
def hook(hv_plot, element):
    toolbar = hv_plot.handles['plot'].toolbar
    toolbar.autohide = True

hv.Curve(data).opts(hooks=[hook])
```

Notice how much more concise it is with `backend_opts`!

With knowledge of the attributes of Bokeh, it's possible to configure many other plot components besides `toolbar`. Some examples include `legend`, `colorbar`, `xaxis`, `yaxis`, and much, much more.

If you're unsure, simply input your best guess and it'll try to provide a list of suggestions if there's an issue.

## Sizing Elements

In the bokeh backend the sizing of plots and specifically layouts of plots is determined in an inside-out or compositional manner. Each subplot can be sized independently and it will fill the allocated space. The sizing is determined by the combination of width, height, aspect and responsive options. In this section we will discover the different approaches to setting plot dimensions and aspects. 

### Width and height

The most straightforward approach to specifying the size of a plot is using a width and height specified in pixels. This is the default behavior and makes it easy to achieve precise alignment between plots but does not allow for keeping a constant aspect or responsive resizing. In particular, the specified size includes the axes and any legends or colorbars.


```python
points_a = hv.Points(data)
points_b = hv.Points(data)

points_a.opts(width=300, height=300) + points_b.opts(width=600, height=300)
```

### Frame width and height

The frame width on the other hand provides precise control over the inner dimensions of a plot, it ensures the actual plot frame matches the specified dimensions exactly. This makes it possible to achieve a precise aspect ratio between the axis scales, without worrying about the size of axes, colorbars, titles and legends, e.g. below we can see two plots defined using explicit ``frame_width`` and ``frame_height`` share the same dimensions despite the fact that one has a colorbar.


```python
xs = ys  = np.arange(10)
yy, xx = np.meshgrid(xs, ys)
zz = xx*yy

img = hv.Image(np.random.rand(100, 100))

points_a.opts(frame_width=200, frame_height=200) +\
img.opts(frame_width=200, frame_height=200, colorbar=True, axiswise=True)
```

### Aspect

The ``aspect`` and ``data_aspect`` options provide control over the scaling of the plot dimensions and the axis limits.

#### ``aspect``:

The ``aspect`` specifies the ratio between the width and height dimensions of the plot. If specified this options takes absolute precedence over the dimensions of the plot but has no effect on the plot's axis limits. It supports the following options:

* **``float``** : A numeric value will scale the ratio of plot width to plot height
* **``"equal"``** : Sets aspect of the axis scaling to be equal, equivalent to **``data_aspect=1``**
* **``"square"``** : Ensures the plot dimensions are square.

#### ``data_aspect``:

The ``data_aspect`` specifies the scaling between the x- and y-axis ranges. If specified this option will scale both the plot ranges and dimensions unless explicit ``aspect``, ``width`` or ``height`` value overrides the plot dimensions:

* **``float``** : Sets ratio between the units on the x-scale and the y-scale


```python
xs = np.linspace(0, 10)
ys = np.linspace(0, 5)

img = hv.Image((xs, ys, xs[:, np.newaxis]*np.sin(ys*4)))

(img.options(aspect='equal').relabel('aspect=\'equal\'') +
 img.options(aspect='square', colorbar=True, frame_width=300).relabel('aspect=\'square\'') +
 img.options(aspect=2).relabel('aspect=2') + 
 img.options(data_aspect=2, frame_width=300).relabel('data_aspect=2')).cols(2)
```

## Responsive

Since bokeh plots are rendered within a browser window which can be resized dynamically it supports responsive sizing modes allowing the plot to rescale when the window it is placed in is changed. If enabled, the behavior of ``responsive`` modes depends on whether an aspect or width/height option is set. Specifically responsive mode will only work if at least one dimension of the plot is left undefined, e.g. when width and height or width and aspect are set the plot is set to a fixed size, ignoring any ``responsive`` option. This leaves four different ``responsive`` modes:

* **``scale_both``**: If neither a width or a height are defined but a fixed aspect is defined both axes will be scaled up to the maximum size of the container. Scaling ensures that the aspect ratio of the plot is maintained.
* **``stretch_both``**: If neither a width, height or aspect are defined the plot will stretch to fill all available space.
* **``stretch_width``**: If a height but neither a width or aspect are defined the plot will stretch to fill all available horizontal space.
* **``stretch_height``**: If a width but neither a height or aspect are defined the plot will stretch to fill all available vertical space.

**Note**: In the notebook stretching and scaling the height does not increase the size of a cell.

As a simple example let us declare a plot that has a fixed height but stretches to fit all available horizontal space:


```python
hv.Points(data).opts(height=200, responsive=True, title='stretch width')
```

Similarly if we declare a fixed ``aspect`` or ``data_aspect`` responsive modes will try to fill all available space but avoid distorting the specified aspect:


```python
img.opts(data_aspect=0.5, responsive=True, title='scale both')
```

## Alignment

The alignment of a plot in a row or column can be controlled using the ``align`` option. It controls both the vertical alignment in a row and the horizontal alignment in a column and can be set to one of `'start'`, `'center'` or `'end'` (where `'start'` is the default).

#### Vertical


```python
points = hv.Points(data).opts(axiswise=True)
img = hv.Image((xs, ys, xs[:, np.newaxis]*np.sin(ys*4)))

img + points.opts(height=200, align='end')
```

#### Horizontal


```python
(img.opts(axiswise=True, width=200, align='center') + points).cols(1)
```

## Grid lines

Grid lines can be controlled through the combination of ``show_grid`` and ``gridstyle`` parameters. The ``gridstyle`` allows specifying a number of options including:

* ``grid_line_color``
* ``grid_line_alpha``
* ``grid_line_dash``
* ``grid_line_width``
* ``grid_bounds``
* ``grid_band``

These options may also be applied to minor grid lines by prepending the ``'minor_'`` prefix and may be applied to a specific axis by replacing ``'grid_`` with ``'xgrid_'`` or ``'ygrid_'``. Here we combine some of these options to generate a complex grid pattern:


```python
grid_style = {'grid_line_color': 'black', 'grid_line_width': 1.5, 'ygrid_bounds': (0.3, 0.7),
              'minor_xgrid_line_color': 'lightgray', 'xgrid_line_dash': [4, 4]}

hv.Points(np.random.rand(10, 2)).opts(gridstyle=grid_style, show_grid=True, size=5, width=600)
```

## Containers

The bokeh plotting extension also supports a number of additional features relating to container components.

### Tabs

Using bokeh, both ``(Nd)Overlay`` and ``(Nd)Layout`` types may be displayed inside a ``tabs`` widget. This may be enabled via a plot option ``tabs``, and may even be nested inside a Layout.


```python
x,y = np.mgrid[-50:51, -50:51] * 0.1

img = hv.Image(np.sin(x**2+y**2), bounds=(-1,-1,1,1))
(img.relabel('Image') * img.sample(x=0).relabel('Cross-section')).opts(tabs=True)
```

Another reason to use ``tabs`` is that some Layout combinations may not be able to be displayed directly using HoloViews. For example, it is not currently possible to display a ``GridSpace`` as part of a ``Layout`` in any backend, and this combination will automatically switch to a ``tab`` representation for the bokeh backend.

### Interactive Legends



When using ``NdOverlay`` and ``Overlay`` containers each element will get a legend entry, which can be used to interactively toggle the visibility of the element. In this example we will create a number of ``Histogram`` elements each with a different mean. By setting a ``muted_fill_alpha`` we can define the style of the element when it is de-selected using the legend, simply try tapping on each legend entry to see the effect:


```python
hv.NdOverlay({i: hv.Histogram(np.histogram(np.random.randn(100)+i*2)) for i in range(5)}).opts(
    'Histogram', width=600, alpha=0.8, muted_fill_alpha=0.1)
```

The other ``muted_`` options can be used to define other aspects of the Histogram style when it is unselected.

If you have multiple plots in a ``Layout`` with the same legend label, muting one of them will automatically mute all of them. 


```python
overlay1 = hv.Curve([0, 0], label="A") * hv.Curve([1, 1], label="B")
overlay2 = hv.Curve([2, 2], label="A") * hv.Curve([3, 3], label="B")
layout = overlay1 + overlay2
layout                                                           
```

If you want to turn off this behavior, use ``.opts(sync_legends=False)``


```python
layout.opts(sync_legends=False)
```

If you want to control the number of legend shown in the ``Layout`` or the position of them ``show_legends`` and ``legend_position`` can be used.


```python
layout.opts(sync_legends=True, show_legends=0, legend_position="top_left")
```

### Marginals


The Bokeh backend also supports marginal plots to generate adjoined plots. The most convenient way to build an AdjointLayout is with the ``.hist()`` method.


```python
points = hv.Points(np.random.randn(500,2))
points.hist(num_bins=51, dimension=['x','y'])
```

When the histogram represents a quantity that is mapped to a value dimension with a corresponding colormap, it will automatically share the colormap, making it useful as a colorbar for that dimension as well as a histogram.


```python
img.hist(num_bins=100, dimension=['x', 'y'], weight_dimension='z', mean_weighted=True) +\
img.hist(dimension='z')
```

## Tools

Bokeh provides a range of tools to interact with a plot and HoloViews adds a number of tools by default but also makes it easy to add additional tools. The ``default_tools`` define the list of tools that are added automatically and usually a user would override only the ``tools`` option to add additional tools. By default the ``default_tools`` include:

    ['save', 'pan', 'wheel_zoom', 'box_zoom', 'reset']

#### Toolbar

The bokeh toolbar is added automatically and will be placed to the right for a single plot and on the top for a layout. Additionally for layouts of plots it will automatically merge the toolbars to avoid crowding the plot. However both behaviors can be customized, the toolbar can be hidden or moved to a different location on a plot by setting the ``toolbar`` option to one of:

    ['above', 'below', 'left', 'right', 'disable', None]

Secondly a layout or grid plot supports the ``merge_tools`` option which can be used to maintain one toolbar per plot:


```python
(hv.Curve([1, 2, 3]).opts(toolbar='above') + hv.Curve([1, 2, 3]).opts(toolbar=None)).opts(merge_tools=False)
```

#### Hover tools

Some Elements allow revealing additional data by hovering over the data. To enable the hover tool, simply supply ``'hover'`` as a list to the ``tools`` plot option. By default the tool will display information for all the dimensions specified on the element:


```python
error = np.random.rand(100, 3)
heatmap_data = {(chr(65+i), chr(97+j)):i*j for i in range(5) for j in range(5) if i!=j}
data = [np.random.normal() for i in range(10000)]
hist = np.histogram(data, 20)

points = hv.Points(error)
heatmap = hv.HeatMap(heatmap_data).sort()
histogram = hv.Histogram(hist)
image = hv.Image(np.random.rand(50,50))

(points + heatmap + histogram + image).opts(
    opts.Points(tools=['hover'], size=5), opts.HeatMap(tools=['hover']),
    opts.Image(tools=['hover']), opts.Histogram(tools=['hover']),
    opts.Layout(shared_axes=False)).cols(2)
```

Additionally, you can provide `'vline'`, the equivalent of passing `HoverTool(mode='vline')`, or `'hline'` to set the hit-testing behavior


```python
error = np.random.rand(100, 3)
heatmap_data = {(chr(65+i), chr(97+j)):i*j for i in range(5) for j in range(5) if i!=j}
data = [np.random.normal() for i in range(10000)]
hist = np.histogram(data, 20)

points = hv.Points(error)
heatmap = hv.HeatMap(heatmap_data).sort()
histogram = hv.Histogram(hist)
image = hv.Image(np.random.rand(50,50))

(points + heatmap + histogram + image).opts(
    opts.Points(tools=['hline'], size=5), opts.HeatMap(tools=['hover']),
    opts.Image(tools=['vline']), opts.Histogram(tools=['hover']),
    opts.Layout(shared_axes=False)).cols(2)
```

It is also possible to explicitly declare the columns to display by manually constructing a `HoverTool` and declaring the tooltips as a list of tuples of the labels and a specification of the dimension name and how to display it (for a complete reference see the [bokeh user guide](https://bokeh.pydata.org/en/latest/docs/user_guide/tools.html#hovertool)).


```python
from bokeh.models import HoverTool
from bokeh.sampledata.periodic_table import elements

points = hv.Points(
    elements, ['electronegativity', 'density'],
    ['name', 'symbol', 'metal', 'CPK', 'atomic radius']
).sort('metal')

tooltips = [
    ('Name', '@name'),
    ('Symbol', '@symbol'),
    ('CPK', '$color[hex, swatch]:CPK')
]
hover = HoverTool(tooltips=tooltips)

points.opts(
    tools=[hover], color='metal', cmap='Category20',
    line_color='black', size=dim('atomic radius')/10,
    width=600, height=400, show_grid=True,
    title='Chemical Elements by Type (scaled by atomic radius)')
```

#### Selection tools

Bokeh provides a number of tools for selecting data points including ``tap``, ``box_select``, ``lasso_select`` and ``poly_select``. To distinguish between selected and unselected data points we can also control the color and alpha of the ``selection`` and ``nonselection`` points.  You can try out any of these selection tools and see how the plot is affected:


```python
hv.Points(error).opts(
    color='blue', nonselection_color='red', size=10, tools=['box_select', 'lasso_select', 'tap'])
```

#### Selection tool with shared axes and linked brushing

When dealing with complex multi-variate data it is often useful to explore interactions between variables across plots. HoloViews will automatically link the data sources of plots in a Layout if they draw from the same data, allowing for both linked axes and brushing.

We'll see what this looks like in practice using a small dataset of macro-economic data:


```python
macro_df = pd.read_csv('http://assets.holoviews.org/macro.csv', sep='\t')
```

By creating two ``Points`` Elements, which both draw their data from the same pandas DataFrame, the two plots become automatically linked. Note that the [Linking Plots user guide](Linking_Plots.ipynb) provides a more explicit way to declare two elements as being linked without having to share the same underlying datastructure. The automated linking behavior can be toggled with the ``shared_datasource`` plot option on a ``Layout`` or ``GridSpace``. You can try selecting data in one plot, and see how the corresponding data (those on the same rows of the DataFrame, even if the plots show different data, will be highlighted in each. 


```python
(hv.Scatter(macro_df, 'year', 'gdp') + hv.Scatter(macro_df, 'gdp',  'unem')).opts(
    opts.Scatter(tools=['box_select', 'lasso_select']), opts.Layout(shared_axes=True, shared_datasource=True))
```

A gridmatrix is a clear use case for linked plotting.  This operation plots any combination of numeric variables against each other, in a grid, and selecting datapoints in any plot will highlight them in all of them.  Such linking can thus reveal how values in a particular range (e.g. very large outliers along one dimension) relate to each of the other dimensions.


```python
table = hv.Dataset(macro_df, kdims=['year', 'country'])
matrix = hv.operation.gridmatrix(table.groupby('country'))

matrix.select(country=['West Germany', 'United Kingdom', 'United States']).opts(
    opts.Scatter(tools=['box_select', 'lasso_select', 'hover'], border=0))
```

#### Drawing Tools

Another commonly useful set of tools are the drawing tools which are integrated with the linked streams introduced in the [Custom Interactivity guide](13-Custom_Interactivity.ipynb). These tools allow drawing and annotating a plot and accessing the annotation back in Python. The available drawing tools include:

* [PointDraw](../reference/streams/bokeh/PointDraw.ipynb): The ``PointDraw`` stream adds a bokeh tool to the source plot, which allows drawing, dragging and deleting points.
* [BoxEdit](../reference/streams/bokeh/BoxEdit.ipynb): The ``BoxEdit`` stream adds a bokeh tool to the source plot, which allows drawing, dragging and deleting boxes.
* [FreehandDraw](../reference/streams/bokeh/FreehandDraw.ipynb): The ``FreehandDraw`` stream adds a bokeh tool to the source plot, which allows freehand drawing on the plot canvas
* [PolyDraw](../reference/streams/bokeh/PolyDraw.ipynb): The ``PolyDraw`` stream adds a bokeh tool to the source plot, which allows drawing, dragging and deleting polygons and paths.
* [PolyEdit](../reference/streams/bokeh/PolyEdit.ipynb): The ``PolyEdit`` stream adds a bokeh tool to the source plot, which allows drawing, dragging and deleting vertices on polygons and paths.

Each of the reference notebooks explains the tools in more detail but to get an of how these tools work see the ``FreehandDraw`` example below, which allows drawing on the canvas and accessing the drawn data from Python:


```python
path = hv.Path([])
freehand = hv.streams.FreehandDraw(source=path, num_objects=3)

path.opts(
    opts.Path(active_tools=['freehand_draw'], height=400, line_width=10, width=400))
```

To access the data from Python, you can access the ``element`` property on the stream, which lets us access each drawn line drawn as a separate ``Path`` element:


```python
freehand.element.split()
```

The [Reference Gallery](http://holoviews.org/reference/index.html) shows examples of all the Elements supported for Bokeh, in a format that can be compared with the corresponding matplotlib versions.

## Theming

Bokeh supports theming via the [Theme object](https://bokeh.pydata.org/en/latest/docs/reference/themes.html) object which can also be using in HoloViews. Applying a Bokeh theme is useful when you need to set detailed aesthetic options not directly exposed via the HoloViews style options.

To apply a Bokeh theme, you will need to create a ``Theme`` object:


```python
from bokeh.themes.theme import Theme

theme = Theme(
    json={
    'attrs' : {
        'Figure' : {
            'background_fill_color': '#2F2F2F',
            'border_fill_color': '#2F2F2F',
            'outline_line_color': '#444444',
        },
        'Grid': {
            'grid_line_dash': [6, 4],
            'grid_line_alpha': .3,
        },

        'Axis': {
            'major_label_text_color': 'white',
            'axis_label_text_color': 'white',
            'major_tick_line_color': 'white',
            'minor_tick_line_color': 'white',
            'axis_line_color': "white"
        }
    }
})
```

Instead of supplying a JSON object, you can also create a Bokeh ``Theme`` object from a [YAML file](https://bokeh.pydata.org/en/latest/docs/reference/themes.html). Once the ``Theme`` object is created, you can apply it by setting it on the ``theme`` parameter of the current Bokeh renderer:


```python
hv.renderer('bokeh').theme = theme
```

The theme will then be applied to subsequent plots:


```python
xs = np.linspace(0, np.pi*4, 100)
hv.Curve((xs, np.sin(xs)), label='foo').opts(bgcolor='grey')
```

You may also supply a name from Bokeh's built-in-themes:


```python
hv.renderer('bokeh').theme = 'light_minimal'
xs = np.linspace(0, np.pi*4, 100)
hv.Curve((xs, np.sin(xs)), label='foo')
```

To disable theming, you can set the ``theme`` parameter on the Bokeh renderer to ``None``.