holoviews
holoviews copied to clipboard
SVG Support for Bokeh Backend
I wrote this a while back for HoloExt; was wondering if there's any interest for me to make a PR for this?
import os
from bokeh import models
from bokeh.io import export_svgs
from holoext.utils import DEFAULT_N, get_cmap, flatten, tidy_fn
def _get_figures_core(objs):
if isinstance(objs, list):
objs = [_get_figures_core(plot) for plot in objs]
elif isinstance(objs, (models.Column, models.Row)):
objs = [_get_figures_core(child) for child in objs.children
if not isinstance(child, (models.ToolbarBox,
models.WidgetBox))]
return objs
def _get_figures(objs):
try:
return list(flatten(_get_figures_core(objs)))
except TypeError:
return [_get_figures_core(objs)]
def _save_to_svg(hv_obj, save):
bokeh_obj = hv.renderer('bokeh').get_plot(hv_obj).state
figures = _get_figures(bokeh_obj)
for i, figure in enumerate(figures):
figure.output_backend = 'svg'
if len(figures) != 1:
if not os.path.exists(save):
os.mkdir(save)
tidied_title = tidy_fn(figure.title.text)
save_fp = os.path.join(
save, '{0}_{1}'.format(tidied_title, i))
else:
save_fp = save
if not save_fp.endswith('svg'):
save_fp = '{0}.{1}'.format(save_fp, 'svg')
export_svgs(figure, save_fp)
import numpy as np
import holoviews as hv
hv.extension('bokeh')
frequencies = [0.5, 0.75, 1.0, 1.25]
def sine_curve(phase, freq):
xvals = [0.1* i for i in range(100)]
return hv.Curve((xvals, [np.sin(phase+freq*x) for x in xvals]))
curve_dict = {f:sine_curve(0,f) for f in frequencies}
NdLayout = hv.NdLayout(curve_dict, kdims='frequency')
_save_to_svg(NdLayout, 'ndlayout_svgs')
There is definitely interest, I initially didn't implement this because each subplot is exported individually but we can start with that and maybe work out how to combine the individual svgs later.
Okay, I thought it would be a straightforward implementation, but the renderer pipeline is confusing to me.
My attempt https://github.com/ioam/holoviews/compare/export_svg?expand=1
It works on a single element, but with multiple elements, it now complains about RuntimeError: Models must be owned by only a single document, Range1d(id='7308857a-9088-49fb-9be8-993d2d514c40', ...) is already in a doc
even though I just copied the code straight from holoext, change a couple variable names and add self_or_cls to the functions themselves, which I don't fully understand either (why it's self_or_cls instead of self).
Looks like a good start, thanks! Can't immediately see the issue but it may be as simple as adding a simple function that removes the existing document from the models.
Hi guys, I think I'm missing something. I generated from Holoviews some graphs (Scatterplot, Violinplot) and managed to save all of them in svg format, using the following code:
#set the backend and the renderer
hv.extension('bokeh')
br = hv.renderer('bokeh')
#set the data
np.random.seed(37)
groups = [chr(65+g) for g in np.random.randint(0, 3, 200)]
#plot
violin = hv.Violin((groups, np.random.randint(0, 5, 200), np.random.randn(200)),
['Group', 'Category'], 'Value')
#export the plot
plot = br.get_plot(violin )
plot = plot.state
plot.output_backend='svg'
export_svgs(plot, filename="Violin.svg")
It seems to work properly, so I can't understand why Bokeh's guide (https://bokeh.pydata.org/en/latest/docs/user_guide/compat.html) and the code behind Bokeh's renderer in Holoviews state it is not possible doing this (i.e. exporting to svg format using Bokeh backend, while it is possible from matplotlib). Is there any limitation I can't see that is preventing this function to be included in Holoviews?
The problematic part is when you try to export a layout with multiple plots. Currently bokeh will export each subplot as a separate SVG. There is of course still some value in that and it might be worth adding but it's not as simple as other exporters which can render everything as one single file.
I understand the issue, but couldn't it be useful to add this note somewhere in the docs? I don't think it's necessary to remove the functionality altogether, because it's common to use a SVG export on a single graph. In my use case, I can use the workaround i posted, but it's not necessary. In my opinion this limitation of the SVG export is not enough to prevent the entire use of the function, but your opinion may be different. Anyhow, a simple note in the documentation can help other users with a use case similar to ours. Thank you anyway for the quick response :-)
As of Bokeh 0.12.6 it is also possible to export to SVG directly from python. I found this solution pretty straight forward:
from bokeh.io import export_svgs
p = hv.render(heatmap, backend='bokeh')
p.output_backend = "svg"
export_svgs(p, filename="heatmap.svg")
Interestingly, neither of the solutions posted by @RColetto and @Sieboldianus seem to be working on a simple hv.Scatter example using HoloViews 1.12.7:
Both...
br = hv.renderer('bokeh')
x = hv.Scatter(np.random.rand(100,2))
plot = br.get_plot(x)
plot = plot.state
plot.output_backend='svg'
export_svgs(plot, filename="out.svg")
and...
x = hv.Scatter(np.random.rand(100,2))
p = hv.render(x, backend='bokeh')
p.output_backend = "svg"
export_svgs(p, filename="out.svg")
yield an svg which contains the axes but no points...
I'll be excited to see the hv.Layout svg rendering with bokeh backend, since I'm hoping to make plots of the type:

...essentially a collection of bivariate plots for QC of automated intensity thresholding.
@eburling I tested your example and it works for me:
Still, the way all of this needs to be setup deserves more attention in the Bokeh/Holoviews docs.
I added a gist that shows how to install Chrome, chromedriver (webdriver) and example code for svg_export
as of Bokeh 2.4.3 / Holoviews 1.14.8.
[btw. I think this issue can be closed.]
Unfortunately, the Bokeh svg export does not respect themes as reported in this bug. Apparently, this has since been fixed for Bokeh >3. So as far as I can see there is currently no way to get a vector graphics export from holoviews using bokeh with non-default or customised themes, right? Is there a roadmap when holoviews might be compatible with Bokeh >3? Thanks
Is there a roadmap when holoviews might be compatible with Bokeh >3?
It is planned to be released next month. If absolutely needed, you can play around with the pre-release version using pip install holoviews --pre
or conda install holoviews=1.16 -c pyviz/label/dev
.