hv.save() yields increasing side margins in exported png when using datashader
See original GitHub issueHi,
first, thanks for providing an awesome library.
It seems the export feature using hv.save
seems broken, though I’m not sure whether this is a holoviews or bokeh issue.
ALL software version info
bokeh 2.1.0 chromedriver 2.24.1 jupyterlab 2.1.4 numpy 1.18.5 pandas 1.0.4 panel 0.9.5 Pillow 7.1.2 selenium 3.141.0
python 3.7.4 macos 10.15.5 chrome 83
Description of expected behavior and the observed behavior
When plotting data from a number of dataframes and exporting them to .png files, I noticed that the margin at the sides of the plot is increasing every time a new plot is exported. See a simplified example below.
Observations, some might not be directly related:
- This seems only to happen with datashader=True
- The last time I remember this working was beginning of this year. I do not know which libraries I updated since then, sorry.
- Currently, hv.save() quietly saves as .html even with extension .png both in the path and in
fmt='png'
defined; this might be related to PR #4304 though I’m not sure - Also noticed that the version using the
hv.Store.renderers
was quietly saving as html when chromedriver was not installed - not sure if I should file this as an issue? - When trying to figure out which option will size the plot when exported to png (something I still don’t fully understand), I noticed a mention (here) that the canvas for gridplots changed sometime around boke 2.0 - these threads are on the bokeh export_png function
- I did try inserting
hv.output(size=200)
in the loop to reset the plot sizing, but that does not affect the outcome - So far the only way I found to reset the margins/sizing was to restart the notebook kernel
Complete, minimal, self-contained example code that reproduces the issue
import numpy as np
import pandas as pd
from time import time
import holoviews as hv
import datashader as ds
import hvplot.pandas
from holoviews.operation.datashader import datashade, dynspread
df = pd.DataFrame()
df['x'] = np.arange(100)
df['y'] = np.random.rand(len(df.x))
df['c1'] = df.x // 10
df['c1'] = df.x // 50
for i in range(9):
plot = df.hvplot(x='x', y='y', row='c1', datashade=True, dynspread=True).opts(toolbar=None)
renderer = hv.Store.renderers['bokeh'].instance(fig='png', holomap='widgets')
renderer.save(plot, f'plot{i}.png')
# hv.save(plot, f'plot_save_{i}.png', fmt='png') # currently ouputs .html w/o warning
Screenshots or screencasts of the bug in action
First and last plot result from that loop:
plot when not using datashader, does not change during loop:
Issue Analytics
- State:
- Created 3 years ago
- Comments:7 (3 by maintainers)
Top GitHub Comments
It took some time to understand what was going on! Here are the elements I got.
Ultimately this behaviour comes from the fact that bokeh does not handle well a spacer with stretch_width sizing mode. This can’t really be considered to be a bokeh bug though. Indeed, there is a warning in the get_screenshot_as_png bokeh function that says Responsive sizing_modes may generate layouts with unexpected size and aspect ratios. It is recommended to use the default
fixed
sizing mode.Here is a minimal bokeh code to reproduce the strange increasing size of the pngs.
So where do these spacers come from in the first place? This is not related to datashader per se but rather related to hv.DynamicMap. Indeed png exports are correct with
hv.Curve(df[['x','y']])
but incorrect withhv.DynamicMap(hv.Curve(df[['x','y']]))
.By following the code trail, it appears that : hv.DynamicMap => center = True in holoviews/plotting/renderer.py/_validate code => layout = [HSpacer(), self, HSpacer()] in panel/pane/holoviews.py/_update_layout => bokeh spacer “bug” since the default sizing mode for HSpacer is stretch_width
Here is the code to check this:
Where should this bug be fixed without breaking legit use cases?
Maybe by changing this test
that results in toggling
center=True
for the holoviews pane.I don’t see the logic behind why a
hv.DynamicMap
should result in acenter=True
holoviews pane but I don’t have a clear view of the current use cases that could be broken by changing this line.Waiting for a fix, a quick workaround is simply to create yourself the panel holoviews pane and save it.
Thanks for the workaround! Helped as I’m exporting a ton of graphs as PNG, and the size kept growing.
However, the save function in
pn.pane.HoloViews(plot).save(filename)
does not support setting DPI?