From 6eeebc4250fe7b2cc4a454e0b6d1de630bde0b05 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Sun, 5 Feb 2017 19:24:23 +0000 Subject: [PATCH 1/3] Add plot option to transpose layouts --- holoviews/plotting/bokeh/plot.py | 2 +- holoviews/plotting/mpl/plot.py | 2 +- holoviews/plotting/plot.py | 5 ++++- holoviews/plotting/plotly/plot.py | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/holoviews/plotting/bokeh/plot.py b/holoviews/plotting/bokeh/plot.py index 283fa1fec5..ddfc896ec8 100644 --- a/holoviews/plotting/bokeh/plot.py +++ b/holoviews/plotting/bokeh/plot.py @@ -361,7 +361,7 @@ def _init_layout(self, layout): layout_subplots, layouts, paths = {}, {}, {} for r, c in self.coords: # Get view at layout position and wrap in AdjointLayout - key, view = layout_items.get((r, c), (None, None)) + key, view = layout_items.get((c, r) if self.transpose else (r, c), (None, None)) view = view if isinstance(view, AdjointLayout) else AdjointLayout([view]) layouts[(r, c)] = view paths[r, c] = key diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py index 9b300421c5..9c8560ef48 100644 --- a/holoviews/plotting/mpl/plot.py +++ b/holoviews/plotting/mpl/plot.py @@ -730,7 +730,7 @@ def _compute_gridspec(self, layout): col_widthratios, row_heightratios = {}, {} for (r, c) in self.coords: # Get view at layout position and wrap in AdjointLayout - _, view = layout_items.get((r, c), (None, None)) + _, view = layout_items.get((c, r) if self.transpose else (r, c), (None, None)) layout_view = view if isinstance(view, AdjointLayout) else AdjointLayout([view]) layouts[(r, c)] = layout_view diff --git a/holoviews/plotting/plot.py b/holoviews/plotting/plot.py index 338683de1a..27a5d44c2e 100644 --- a/holoviews/plotting/plot.py +++ b/holoviews/plotting/plot.py @@ -1030,6 +1030,9 @@ class GenericLayoutPlot(GenericCompositePlot): displays the elements in a cartesian grid in scanline order. """ + transpose = param.Boolean(default=False, doc=""" + Whether to transpose the layout when plotting""") + def __init__(self, layout, **params): if not isinstance(layout, (NdLayout, Layout)): raise ValueError("GenericLayoutPlot only accepts Layout objects.") @@ -1038,6 +1041,6 @@ def __init__(self, layout, **params): super(GenericLayoutPlot, self).__init__(layout, **params) self.subplots = {} - self.rows, self.cols = layout.shape + self.rows, self.cols = layout.shape[::-1] if self.transpose else layout.shape self.coords = list(product(range(self.rows), range(self.cols))) diff --git a/holoviews/plotting/plotly/plot.py b/holoviews/plotting/plotly/plot.py index 8c09bd4113..40d4f3e32e 100644 --- a/holoviews/plotting/plotly/plot.py +++ b/holoviews/plotting/plotly/plot.py @@ -66,7 +66,7 @@ def _init_layout(self, layout): inserts_cols = [] for r, c in self.coords: # Get view at layout position and wrap in AdjointLayout - key, view = layout_items.get((r, c), (None, None)) + key, view = layout_items.get((c, r) if self.transpose else (r, c), (None, None)) view = view if isinstance(view, AdjointLayout) else AdjointLayout([view]) layouts[(r, c)] = view paths[r, c] = key From e7d2c3dd1545113558e547d7520b2fbe90dfe523 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 6 Feb 2017 17:50:28 +0000 Subject: [PATCH 2/3] Small fixes for LayoutPlot transpose option --- holoviews/plotting/mpl/plot.py | 2 ++ holoviews/plotting/plot.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py index 9c8560ef48..6396fd95d5 100644 --- a/holoviews/plotting/mpl/plot.py +++ b/holoviews/plotting/mpl/plot.py @@ -876,6 +876,8 @@ def _compute_gridspec(self, layout): empty = isinstance(obj.main, Empty) if empty: obj = AdjointLayout([]) + elif self.transpose: + layout_count = (c*self.rows+(r+1)) else: layout_count += 1 subaxes = [plt.subplot(self.gs[ind], projection=proj) diff --git a/holoviews/plotting/plot.py b/holoviews/plotting/plot.py index 27a5d44c2e..262ea88834 100644 --- a/holoviews/plotting/plot.py +++ b/holoviews/plotting/plot.py @@ -1031,7 +1031,9 @@ class GenericLayoutPlot(GenericCompositePlot): """ transpose = param.Boolean(default=False, doc=""" - Whether to transpose the layout when plotting""") + Whether to transpose the layout when plotting. Switches + from row-based left-to-right and top-to-bottom scanline order + to column-based top-to-bottom and left-to-right order.""") def __init__(self, layout, **params): if not isinstance(layout, (NdLayout, Layout)): From d5705a4424f5393c152e1aaa71c0a3ba824dee6e Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 6 Feb 2017 18:03:50 +0000 Subject: [PATCH 3/3] Added unit tests for plotting (transposed) Layout --- tests/testplotinstantiation.py | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/testplotinstantiation.py b/tests/testplotinstantiation.py index fe2a403f85..08d792a265 100644 --- a/tests/testplotinstantiation.py +++ b/tests/testplotinstantiation.py @@ -183,6 +183,28 @@ def test_curve_heterogeneous_datetime_types_with_pd_overlay(self): plot = mpl_renderer.get_plot(curve_dt*curve_dt64*curve_pd) self.assertEqual(plot.handles['axis'].get_xlim(), (735964.0, 735976.0)) + def test_layout_instantiate_subplots(self): + layout = (Curve(range(10)) + Curve(range(10)) + Image(np.random.rand(10,10)) + + Curve(range(10)) + Curve(range(10))) + plot = mpl_renderer.get_plot(layout) + positions = [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3)] + self.assertEqual(sorted(plot.subplots.keys()), positions) + for i, pos in enumerate(positions): + adjoint = plot.subplots[pos] + if 'main' in adjoint.subplots: + self.assertEqual(adjoint.subplots['main'].layout_num, i+1) + + def test_layout_instantiate_subplots_transposed(self): + layout = (Curve(range(10)) + Curve(range(10)) + Image(np.random.rand(10,10)) + + Curve(range(10)) + Curve(range(10))) + plot = mpl_renderer.get_plot(layout(plot=dict(transpose=True))) + positions = [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1), (3, 0), (3, 1)] + self.assertEqual(sorted(plot.subplots.keys()), positions) + nums = [1, 5, 2, 6, 3, 7, 4, 8] + for pos, num in zip(positions, nums): + adjoint = plot.subplots[pos] + if 'main' in adjoint.subplots: + self.assertEqual(adjoint.subplots['main'].layout_num, num) class TestBokehPlotInstantiation(ComparisonTestCase): @@ -643,6 +665,21 @@ def test_curve_heterogeneous_datetime_types_with_pd_overlay(self): self.assertEqual(plot.handles['x_range'].start, np.datetime64(dt.datetime(2016, 1, 1))) self.assertEqual(plot.handles['x_range'].end, np.datetime64(dt.datetime(2016, 1, 13))) + def test_layout_instantiate_subplots(self): + layout = (Curve(range(10)) + Curve(range(10)) + Image(np.random.rand(10,10)) + + Curve(range(10)) + Curve(range(10))) + plot = bokeh_renderer.get_plot(layout) + positions = [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3)] + self.assertEqual(sorted(plot.subplots.keys()), positions) + + def test_layout_instantiate_subplots_transposed(self): + layout = (Curve(range(10)) + Curve(range(10)) + Image(np.random.rand(10,10)) + + Curve(range(10)) + Curve(range(10))) + plot = bokeh_renderer.get_plot(layout(plot=dict(transpose=True))) + positions = [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1), (3, 0), (3, 1)] + self.assertEqual(sorted(plot.subplots.keys()), positions) + + class TestPlotlyPlotInstantiation(ComparisonTestCase): @@ -725,3 +762,17 @@ def history_callback(x, history=deque(maxlen=10)): state = plot.state self.assertEqual(state['data'][0]['x'], np.arange(10)) self.assertEqual(state['data'][0]['y'], np.arange(10, 20)) + + def test_layout_instantiate_subplots(self): + layout = (Curve(range(10)) + Curve(range(10)) + Image(np.random.rand(10,10)) + + Curve(range(10)) + Curve(range(10))) + plot = plotly_renderer.get_plot(layout) + positions = [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3)] + self.assertEqual(sorted(plot.subplots.keys()), positions) + + def test_layout_instantiate_subplots_transposed(self): + layout = (Curve(range(10)) + Curve(range(10)) + Image(np.random.rand(10,10)) + + Curve(range(10)) + Curve(range(10))) + plot = plotly_renderer.get_plot(layout(plot=dict(transpose=True))) + positions = [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1), (3, 0), (3, 1)] + self.assertEqual(sorted(plot.subplots.keys()), positions)