Skip to content

Commit

Permalink
Implement multi-yaxis support (#5621)
Browse files Browse the repository at this point in the history
Co-authored-by: Jean-Luc Stevens <[email protected]>
Co-authored-by: James A. Bednar <[email protected]>
  • Loading branch information
3 people committed Jul 21, 2023
1 parent d48950a commit 2d7e1cc
Show file tree
Hide file tree
Showing 18 changed files with 868 additions and 324 deletions.
61 changes: 59 additions & 2 deletions examples/user_guide/Customizing_Plots.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
" - [Inverting axes](#inverting-axes): Flipping the x-/y-axes and inverting an axis\n",
" - [Axis labels](#axis-labels): Setting axis labels using dimensions and options\n",
" - [Axis ranges](#axis-ranges): Controlling axes ranges using dimensions, padding and options\n",
" - [Axis ticks](#axis-ticks): Controlling axis tick locations, labels and formatting"
" - [Axis ticks](#axis-ticks): Controlling axis tick locations, labels and formatting\n",
" - [Twin axes](#twin-axes): Enabling twin axes"
]
},
{
Expand Down Expand Up @@ -689,6 +690,62 @@
"source": [
"bars.opts(xrotation=45)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Twin axes\n",
"*(Available in HoloViews > 1.17)*\n",
"\n",
"HoloViews now supports displaying overlays containing two different value dimensions as twin axes for chart elements. To maintain backwards compatibility, this feature is only enabled by setting the `multi_y=True` option on the overlay.\n",
"\n",
"To illustrate, here is an overlay containing three curves with two value dimensions ('A' and 'B'). Setting `multi_y=True` then maps these two value dimensions to twin-axes:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"overlay = hv.Curve([1, 2, 3], vdims=['A']) * hv.Curve([2, 3, 4], vdims=['A']) * hv.Curve([3, 2, 1], vdims=['B'])\n",
"overlay.opts(multi_y=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Additional value dimensions do map to additional axes but be aware that support of multi axes beyond twin axes is currently considered experimental.\n",
"\n",
"The first value dimension is mapped to the left-hand axis and the second value dimension maps to the right axis. Note that the two axes are individually zoomable by hovering over them and using the Bokeh wheelzoom tool.\n",
"\n",
"\n",
"#### Supported `multi_y` options\n",
"\n",
"When `multi_y` is enabled, you can set individual axis options on the elements of the overlay.\n",
"\n",
"In this example, the left axis uses the default options while the right axis is an inverted, autoranged, log axis with a set `ylim`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"(hv.Curve([1, 2, 3], vdims=['A']) * hv.Curve([2, 3, 4], vdims=['B']).opts(autorange='y', invert_yaxis=True, logy=True, ylim=(1,10))).opts(multi_y=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Supported options for customizing individual axes are `apply_ranges`, `autorange='y'`, `invert_yaxis`, `logy` and `ylim`.\n",
"\n",
"Note that as of HoloViews 1.17.0, `multi_y` does not have streaming plot support and that linked streams are not yet aware of additional y-axes."
]
}
],
"metadata": {
Expand All @@ -698,5 +755,5 @@
}
},
"nbformat": 4,
"nbformat_minor": 2
"nbformat_minor": 4
}
8 changes: 4 additions & 4 deletions holoviews/plotting/bokeh/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def get_batched_data(self, element, ranges=None):
data[k].extend(eld)
return data, elmapping, style

def get_extents(self, element, ranges=None, range_type='combined'):
def get_extents(self, element, ranges=None, range_type='combined', **kwargs):
return None, None, None, None


Expand Down Expand Up @@ -161,7 +161,7 @@ def _init_glyph(self, plot, mapping, properties):
plot.renderers.append(box)
return None, box

def get_extents(self, element, ranges=None, range_type='combined'):
def get_extents(self, element, ranges=None, range_type='combined', **kwargs):
loc = element.data
if isinstance(element, VLine):
dim = 'x'
Expand Down Expand Up @@ -241,7 +241,7 @@ def _init_glyph(self, plot, mapping, properties):
plot.add_layout(slope)
return None, slope

def get_extents(self, element, ranges=None, range_type='combined'):
def get_extents(self, element, ranges=None, range_type='combined', **kwargs):
return None, None, None, None


Expand Down Expand Up @@ -366,7 +366,7 @@ def _init_glyph(self, plot, mapping, properties, key):
plot.renderers.append(renderer)
return renderer, glyph

def get_extents(self, element, ranges=None, range_type='combined'):
def get_extents(self, element, ranges=None, range_type='combined', **kwargs):
return None, None, None, None


Expand Down
14 changes: 1 addition & 13 deletions holoviews/plotting/bokeh/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ def get_data(self, element, ranges, style):
self._get_hover_data(data, element)
return (data, mapping, style)

def get_extents(self, element, ranges, range_type='combined'):
def get_extents(self, element, ranges, range_type='combined', **kwargs):
ydim = element.get_dimension(1)
s0, s1 = ranges[ydim.name]['soft']
s0 = min(s0, 0) if isfinite(s0) else 0
Expand Down Expand Up @@ -693,11 +693,6 @@ class SpikesPlot(SpikesMixin, ColorbarPlot):
_nonvectorized_styles = base_properties + ['cmap']
_plot_methods = dict(single='segment')

def _get_axis_dims(self, element):
if 'spike_length' in self.lookup_options(element, 'plot').options:
return [element.dimensions()[0], None, None]
return super()._get_axis_dims(element)

def get_data(self, element, ranges, style):
dims = element.dimensions()

Expand Down Expand Up @@ -815,13 +810,6 @@ def _axis_properties(self, axis, key, plot, dimension=None,
props['group_text_baseline'] = 'middle'
return props

def _get_axis_dims(self, element):
if element.ndims > 1 and not (self.stacked or not self.multi_level):
xdims = element.kdims
else:
xdims = element.kdims[0]
return (xdims, element.vdims[0])

def _get_factors(self, element, ranges):
xvals, gvals = self._get_coords(element, ranges)
if gvals is not None:
Expand Down
Loading

0 comments on commit 2d7e1cc

Please sign in to comment.