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)