Compose same object with different opts?

I am making visualizations to illustrate a regridding problem. I’d like to show two plots next to one another, the left side zoomed out to show the whole grid extent, the right side zoomed in to show a smaller area.

I’m finding that I can do this in two separate notebook cells, but if I try to place the plots side by side they both get the same axis limits. From " Q: Why are my .options(), .relabel(), .redim(), and similar settings not having any effect?" in the FAQ, I expected to see two different versions of the grid1 object plotted.

This is a minimal working example; in the real one the grids are also overlaid over a bunch of polygons representing coastlines and adminstrative boundaries.

# In[1]:

import holoviews as hv
from holoviews import opts
import numpy as np
import bokeh.palettes as bp


def make_grid(n, color):
    grid = hv.QuadMesh((np.arange(n), np.arange(n), np.random.rand(n, n))).opts(
        line_width=0.2, line_alpha=1.0, fill_alpha=0.0, line_color=color
    return grid

grid1 = make_grid(20, "red")

# In[2]:

# these both plot with x limits (5, 10)
grid1.opts(xlim=(2, 18)) + grid1.opts(xlim=(5, 10))

# In[3]:

# these both plot with xlimits (2, 18)
grid1.options(xlim=(2, 18)) + grid1.options(xlim=(5, 10))

# In[4]:

# plotting in separate cells works
grid1.opts(xlim=(2, 18))

# In[5]:

grid1.opts(xlim=(5, 10))


This is one of the possible solutions to your problem:

(grid1.redim.range(x=(2, 18)) + grid1.redim.range(x=(5, 10))).opts(shared_axes=False)

The FAQ has some useful info on how shared_axes works. Another approach would have been to apply .opts(axiswise=True) to each QuadMesh element.

In the examples you tried, I can tell you that .opts actually modifies in place the element, so grid1.opts(xlim=(2, 18)) + grid1.opts(xlim=(5, 10)) applies .opts twice to grid1. So avoid having .opts modify the element in place, you can call it like .opts(..., clone=True). In the solution I gave I use redim as it doesn’t modify grid1, but a solution with .opts(xlim=...) would also have been possible.

That’s right. @Timothy-W-Hilton , we set that up as a trap for you; .options() clones automatically, while .opts() only clones when requested. We wrote .options first, but then found that many more people were surprised by their option settings disappearing than were surprised when the options were shared across uses of the same object. So we added .opts with the less-surprising behavior and mostly don’t mention .options any more, apart from that FAQ, but there are certainly cases where you do want clone=True.

Thanks @maximlt and @jbednar – that helped me a lot. For anyone who finds this in the future, these three calls all work for me:

(grid1.opts(xlim=(2, 18), clone=True) + grid1.opts(xlim=(5, 10), clone=True)).opts(

(grid1.options(xlim=(2, 18)) + grid1.options(xlim=(5, 10))).opts(shared_axes=False)

(grid1.redim.range(x=(2, 18)) + grid1.redim.range(x=(5, 10))).opts(shared_axes=False)

HoloViz really is a brilliant piece of work. Thanks!


Here’s one more alternative; you can clone your object explicitly if you prefer:

(grid1.opts(xlim=(2, 18)) + grid1.clone().opts(xlim=(5, 10))).opts(shared_axes=False)

And as Maxime noted, it’s not just about cloning, but also about sharing axes, so that bit still needs to be there to make them truly independent.