question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

hv.save() yields increasing side margins in exported png when using datashader

See original GitHub issue

Hi, 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: plot0

plot8

plot when not using datashader, does not change during loop: plot

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
marcbernotcommented, Aug 20, 2020

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.

from bokeh.io.export import export_png
from bokeh.layouts import Spacer, Row
from PIL import Image

filename = 'plot.png'
sp = Spacer(margin=[5,5,5,5],sizing_mode='stretch_width')
row = Row(sp)

for k in range(3):
    export_png(row,filename=filename)
    print(Image.open(filename).size)

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 with hv.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:

import numpy as np
import pandas as pd
import holoviews as hv
hv.extension('bokeh')
from PIL import Image

n_samples = 100
filename = 'plot.png'
df = pd.DataFrame(dict(x = np.arange(n_samples),
                       y = np.random.rand(n_samples)))

renderer = hv.renderer('bokeh')
h = hv.DynamicMap(hv.Curve(df))
plot, fmt = renderer._validate(h,'png')
print(plot.layout)

Where should this bug be fixed without breaking legit use cases?

Maybe by changing this test

elif dynamic or (self._render_with_panel and fmt == 'html')

that results in toggling center=True for the holoviews pane.

I don’t see the logic behind why a hv.DynamicMap should result in a center=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.

import panel as pn
pn.pane.HoloViews(plot).save(filename)
0reactions
Tsarpfcommented, Nov 24, 2022

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?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Developers - hv.save() yields increasing side margins in exported ...
hv.save() yields increasing side margins in exported png when using datashader ... It seems the export feature using hv.save seems broken, though I'm...
Read more >
Exporting and Archiving — HoloViews v1.15.3
In this notebook, we show how HoloViews works together with the Jupyter ... The fig="png" part of the hv.save function call above specified...
Read more >
holoviews Changelog - pyup.io
Improve error message for `hv.opts` without a plotting backend ([5494](https://github.com/holoviz/holoviews/pull/5494)) - Fix warnings exposed in CI logs
Read more >
Strength of visualization-python visuals tutorial - Kaggle
Added pairplot for feature interaction using Holoviews in second ... you can use bokeh with other libraries like seaborn, data-shader or ...
Read more >
Why is my Datashader plot saved from HoloViews such a low ...
Good question about a subtle issue. What's happening is that hv.save exports the "initial" rendering of a HoloViews object, ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found