Reputation: 197
Primarly to use holoviews' decimate operation I stick to holoviews, but I ran into limitations in adapting the bokeh output within holoviews:
To realize "linked panning" in pure bokeh, one needs to share the e.g. x_range parameter in bokehs bokeh.plotting.figure.Figure object like
# create a new plot
s1 = figure(plot_width=250, plot_height=250)
# create a new plot and share only one range
s2 = figure(plot_width=250, plot_height=250, x_range=s1.x_range)
Full example on: https://docs.bokeh.org/en/latest/docs/user_guide/interaction/linking.html#userguide-interaction-linking
I would like to implement linked panning directly within holoviews. The reason for that is, to preserve interactivity in plots with different ranges.
I know, the holoviews option axiswise=True perfectly adjusts different ranges automatically but it seems this cannot be defined for a single axis only (e.g. y-axis), while perserving linked panning interactivity on the x-axis.
How can I accomplish this? Thank you very much!
Upvotes: 1
Views: 761
Reputation: 4080
There are several different answer to this question. Let's start with the most immediate fix for what you are trying to do which is to link one axis but not another. This is most easily accomplished by giving the dimensions different names, e.g. simply name one of the dimensions something other than the default 'x' or 'y':
hv.Curve([1, 2, 3], 'x', 'y') + hv.Curve([1, 2, 3], 'x', 'y2')
To be very exact, the "identity" of an axis in this case is determined by the Dimension name, label and unit. This allows axes representing the same quantities to be easily shared. If you really need to you could even give them different names/labels/units and then override the displayed label with the xlabel/ylabel options.
Now to answer the more literal version of your answer, HoloViews generates bokeh models in the background and then renders them to screen. You can define hooks which can modify the bokeh plot representation before it is rendered. I certainly wouldn't recommend this approach but you can achieve the same thing as the previous solution like this:
ax_range = None
def hook(plot, element):
global ax_range
ax_range = plot.handles['x_range']
def hook2(plot, element):
plot.state.x_range = ax_range
hv.Curve([1, 2, 3], 'x', 'y').opts(hooks=[hook]) + hv.Curve([1, 2, 3], 'x2', 'y').opts(hooks=[hook2], axiswise=True)
Lastly we could make the linking more explicit and if you like this approach I'd recommend you file an issue to request it be included. In HoloViews 1.11 so called Links were introduced, we could easily add a RangeLink
like this:
import param
from holoviews.plotting.links import Link
class RangeLink(Link):
x_range = param.Boolean(default=True)
y_range = param.Boolean(default=True)
_requires_target = True
from holoviews.plotting.bokeh.callbacks import LinkCallback
class RangeLinkCallback(LinkCallback):
def __init__(self, root_model, link, source_plot, target_plot):
if link.x_range:
target_plot.handles['x_range'] = source_plot.handles['x_range']
target_plot.state.x_range = source_plot.state.x_range
if link.y_range:
target_plot.handles['y_range'] = source_plot.handles['y_range']
target_plot.state.y_range = source_plot.state.y_range
RangeLink.register_callback('bokeh', RangeLinkCallback)
# Now we can use it to link the axes
curve = hv.Curve([1, 2, 3])
curve2 = hv.Curve([1, 2, 3])
RangeLink(curve, curve2, y_range=False)
curve + curve2.opts(axiswise=True)
Upvotes: 2