From 90f56c8a9a55a7c28762ee43d5a5a377c2078e59 Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Wed, 25 Sep 2024 12:46:04 +0200 Subject: [PATCH] Add `sublabel_index` and `sublabel_skip` option for matplotlib backend (#6375) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Simon Høxbro Hansen --- holoviews/plotting/mpl/plot.py | 27 ++++++++++++++----- .../plotting/matplotlib/test_layoutplot.py | 22 +++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py index f166544d83..d8208709cd 100644 --- a/holoviews/plotting/mpl/plot.py +++ b/holoviews/plotting/mpl/plot.py @@ -120,12 +120,18 @@ class MPLPlot(DimensionedPlot): Allows labeling the subaxes in each plot with various formatters including {Alpha}, {alpha}, {numeric} and {roman}.""") + sublabel_offset = param.Number(default=0, bounds=(0, None), doc=""" + Allows offsetting the sublabel index.""") + sublabel_position = param.NumericTuple(default=(-0.35, 0.85), doc=""" Position relative to the plot for placing the optional subfigure label.""") sublabel_size = param.Number(default=18, doc=""" Size of optional subfigure label.""") + sublabel_skip = param.List(default=None, item_type=int, doc=""" + List of elements to skip when labeling subplots. Numbering starts at 1.""") + projection = param.Parameter(default=None, doc=""" The projection of the plot axis, default of None is equivalent to 2D plot, '3d' and 'polar' are also supported by matplotlib by default. @@ -207,20 +213,29 @@ def _get_fontsize_defaults(self): def _subplot_label(self, axis): + if self.sublabel_skip and self.layout_num in self.sublabel_skip: + return layout_num = self.layout_num if self.subplot else 1 - if self.sublabel_format and not self.adjoined and layout_num > 0: + if self.sublabel_skip: + if any(n < 1 for n in self.sublabel_skip): + raise ValueError('sublabel_skip values must be greater than 0') + sublabel_num = len(set(range(layout_num)) - set(self.sublabel_skip)) + else: + sublabel_num = layout_num + sublabel_num += self.sublabel_offset + if self.sublabel_format and not self.adjoined and sublabel_num > 0: from matplotlib.offsetbox import AnchoredText labels = {} if '{Alpha}' in self.sublabel_format: - labels['Alpha'] = int_to_alpha(layout_num-1) + labels['Alpha'] = int_to_alpha(sublabel_num-1) elif '{alpha}' in self.sublabel_format: - labels['alpha'] = int_to_alpha(layout_num-1, upper=False) + labels['alpha'] = int_to_alpha(sublabel_num-1, upper=False) elif '{numeric}' in self.sublabel_format: - labels['numeric'] = self.layout_num + labels['numeric'] = sublabel_num elif '{Roman}' in self.sublabel_format: - labels['Roman'] = int_to_roman(layout_num) + labels['Roman'] = int_to_roman(sublabel_num) elif '{roman}' in self.sublabel_format: - labels['roman'] = int_to_roman(layout_num).lower() + labels['roman'] = int_to_roman(sublabel_num).lower() at = AnchoredText(self.sublabel_format.format(**labels), loc=3, bbox_to_anchor=self.sublabel_position, frameon=False, prop=dict(size=self.sublabel_size, weight='bold'), diff --git a/holoviews/tests/plotting/matplotlib/test_layoutplot.py b/holoviews/tests/plotting/matplotlib/test_layoutplot.py index 97ed2ab410..5c32d73952 100644 --- a/holoviews/tests/plotting/matplotlib/test_layoutplot.py +++ b/holoviews/tests/plotting/matplotlib/test_layoutplot.py @@ -58,3 +58,25 @@ def test_layout_shared_axes_disabled(self): cp1, cp2 = plot.traverse(lambda x: x, [CurvePlot]) self.assertTrue(cp1.handles['axis'].get_ylim(), (1, 3)) self.assertTrue(cp2.handles['axis'].get_ylim(), (10, 30)) + + def test_layout_sublabel_offset(self): + from holoviews.plotting.mpl import CurvePlot + layout = Curve([]) + Curve([]) + Curve([]) + Curve([]) + layout.opts(sublabel_offset=1) + plot = mpl_renderer.get_plot(layout) + cps = plot.traverse(lambda x: x, [CurvePlot]) + assert cps[0].handles["sublabel"].get_text() == "B" + assert cps[1].handles["sublabel"].get_text() == "C" + assert cps[2].handles["sublabel"].get_text() == "D" + assert cps[3].handles["sublabel"].get_text() == "E" + + def test_layout_sublabel_skip(self): + from holoviews.plotting.mpl import CurvePlot + layout = Curve([]) + Curve([]) + Curve([]) + Curve([]) + layout.opts(sublabel_skip=[1, 3]) + plot = mpl_renderer.get_plot(layout) + cps = plot.traverse(lambda x: x, [CurvePlot]) + assert "sublabel" not in cps[0].handles + assert cps[1].handles["sublabel"].get_text() == "A" + assert "sublabel" not in cps[2].handles + assert cps[3].handles["sublabel"].get_text() == "B"