From c613cccb529932a87dcd0a33cd735ba34d0b78c5 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 26 Oct 2018 11:50:29 -0400 Subject: [PATCH 1/5] first pass at subplot titles respecting row_width and column_width --- plotly/tools.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 87b789309b3..6e9edb9726a 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1001,7 +1001,6 @@ def _checks(item, defaults): ) for c in col_seq ] for r in row_seq ] - # [grid_ref] Initialize the grid and insets' axis-reference lists grid_ref = [[None for c in range(cols)] for r in range(rows)] insets_ref = [None for inset in range(len(insets))] if insets else None @@ -1080,6 +1079,7 @@ def _get_anchors(r, c, x_cnt, y_cnt, shared_xaxes, shared_yaxes): return x_anchor, y_anchor list_of_domains = [] # added for subplot titles + list_of_domains_complete = [] # hold onto every domain used, even if shared axes # Function pasting x/y domains in layout object (2d case) def _add_domain(layout, x_or_y, label, domain, anchor, position): @@ -1323,20 +1323,33 @@ def _pad(s, cell_len=cell_len): subtitle_pos_x.append(sum(x_domains) / 2) for y_domains in y_dom: subtitle_pos_y.append(y_domains[1]) + # If shared_axes is True the domin of each subplot is not returned so the # title position must be calculated for each subplot else: - subtitle_pos_x = [None] * cols - subtitle_pos_y = [None] * rows - delt_x = (x_e - x_s) + x_dom = [layout[k]['domain'] for k in layout if 'xaxis' in k] + y_dom = [layout[k]['domain'] for k in layout if 'yaxis' in k] for index in range(cols): - subtitle_pos_x[index] = ((delt_x / 2) + - ((delt_x + horizontal_spacing) * index)) - subtitle_pos_x *= rows - for index in range(rows): - subtitle_pos_y[index] = (1 - ((y_e + vertical_spacing) * index)) - subtitle_pos_y *= cols - subtitle_pos_y = sorted(subtitle_pos_y, reverse=True) + subtitle_pos_x = [] + for x_domains in x_dom: + subtitle_pos_x.append(sum(x_domains) / 2) + subtitle_pos_x *= rows + + if shared_yaxes: + for index in range(rows): + subtitle_pos_y = [] + for y_domain in y_dom: + subtitle_pos_y.append(y_domain[1]) + subtitle_pos_y *= cols + subtitle_pos_y = sorted(subtitle_pos_y, reverse=True) + + else: + for index in range(rows): + subtitle_pos_y = [] + for y_domain in y_dom: + subtitle_pos_y.append(y_domain[1]) + subtitle_pos_y = sorted(subtitle_pos_y, reverse=True) + subtitle_pos_y *= cols plot_titles = [] for index in range(len(subplot_titles)): From e83207a401916b3f526892d6df29d1bf1941f1e0 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 26 Oct 2018 12:24:49 -0400 Subject: [PATCH 2/5] add test for row_width and column_width in make_subplots --- .../test_tools/test_make_subplots.py | 63 +++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_make_subplots.py b/plotly/tests/test_core/test_tools/test_make_subplots.py index 9db24b3cd69..cb9e02ec9f7 100644 --- a/plotly/tests/test_core/test_tools/test_make_subplots.py +++ b/plotly/tests/test_core/test_tools/test_make_subplots.py @@ -1946,7 +1946,7 @@ def test_subplot_titles_shared_axes(self): layout=Layout( annotations=Annotations([ Annotation( - x=0.22499999999999998, + x=0.225, y=1.0, xref='paper', yref='paper', @@ -1957,7 +1957,7 @@ def test_subplot_titles_shared_axes(self): yanchor='bottom' ), Annotation( - x=0.7749999999999999, + x=0.775, y=1.0, xref='paper', yref='paper', @@ -1968,7 +1968,7 @@ def test_subplot_titles_shared_axes(self): yanchor='bottom' ), Annotation( - x=0.22499999999999998, + x=0.225, y=0.375, xref='paper', yref='paper', @@ -1979,7 +1979,7 @@ def test_subplot_titles_shared_axes(self): yanchor='bottom' ), Annotation( - x=0.7749999999999999, + x=0.775, y=0.375, xref='paper', yref='paper', @@ -2010,6 +2010,7 @@ def test_subplot_titles_shared_axes(self): ) ) ) + fig = tls.make_subplots(rows=2, cols=2, subplot_titles=('Title 1', 'Title 2', 'Title 3', 'Title 4'), @@ -2155,3 +2156,57 @@ def test_large_columns_no_errors(self): fig = tls.make_subplots(100, 1, vertical_spacing=v_space, specs=[[{'is_3d': True}] for _ in range(100)]) + + def test_row_width_and_column_width(self): + + expected = Figure({ + 'data': [], + 'layout': {'annotations': [{'font': {'size': 16}, + 'showarrow': False, + 'text': 'Title 1', + 'x': 0.405, + 'xanchor': 'center', + 'xref': 'paper', + 'y': 1.0, + 'yanchor': 'bottom', + 'yref': 'paper'}, + {'font': {'size': 16}, + 'showarrow': False, + 'text': 'Title 2', + 'x': 0.9550000000000001, + 'xanchor': 'center', + 'xref': 'paper', + 'y': 1.0, + 'yanchor': 'bottom', + 'yref': 'paper'}, + {'font': {'size': 16}, + 'showarrow': False, + 'text': 'Title 3', + 'x': 0.405, + 'xanchor': 'center', + 'xref': 'paper', + 'y': 0.1875, + 'yanchor': 'bottom', + 'yref': 'paper'}, + {'font': {'size': 16}, + 'showarrow': False, + 'text': 'Title 4', + 'x': 0.9550000000000001, + 'xanchor': 'center', + 'xref': 'paper', + 'y': 0.1875, + 'yanchor': 'bottom', + 'yref': 'paper'}], + 'xaxis': {'anchor': 'y', 'domain': [0.0, 0.81]}, + 'xaxis2': {'anchor': 'y2', 'domain': [0.91, 1.0]}, + 'xaxis3': {'anchor': 'y3', 'domain': [0.0, 0.81]}, + 'xaxis4': {'anchor': 'y4', 'domain': [0.91, 1.0]}, + 'yaxis': {'anchor': 'x', 'domain': [0.4375, 1.0]}, + 'yaxis2': {'anchor': 'x2', 'domain': [0.4375, 1.0]}, + 'yaxis3': {'anchor': 'x3', 'domain': [0.0, 0.1875]}, + 'yaxis4': {'anchor': 'x4', 'domain': [0.0, 0.1875]}} + }) + fig = tls.make_subplots(rows=2, cols=2, + subplot_titles=('Title 1', 'Title 2', 'Title 3', 'Title 4'), + row_width=[1, 3], column_width=[9, 1]) + self.assertEqual(fig.to_plotly_json(), expected.to_plotly_json()) From b9ec250aff8038a10a4c0800e5a9edb0e94e8af8 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Fri, 26 Oct 2018 14:14:57 -0400 Subject: [PATCH 3/5] fixes py2/3 compatibility (for tests) --- plotly/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 6e9edb9726a..d6430c2a11d 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1327,8 +1327,8 @@ def _pad(s, cell_len=cell_len): # If shared_axes is True the domin of each subplot is not returned so the # title position must be calculated for each subplot else: - x_dom = [layout[k]['domain'] for k in layout if 'xaxis' in k] - y_dom = [layout[k]['domain'] for k in layout if 'yaxis' in k] + x_dom = [layout[k]['domain'] for k in sorted(layout.to_plotly_json().keys()) if 'xaxis' in k] + y_dom = [layout[k]['domain'] for k in sorted(layout.to_plotly_json().keys()) if 'yaxis' in k] for index in range(cols): subtitle_pos_x = [] for x_domains in x_dom: From 13f491e944019f09b23c5967af6ec6fd0c53ae89 Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 29 Oct 2018 15:08:03 -0400 Subject: [PATCH 4/5] jon's comments --- .../test_tools/test_make_subplots.py | 56 ++++++++++++++++++- plotly/tools.py | 23 +++++++- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_make_subplots.py b/plotly/tests/test_core/test_tools/test_make_subplots.py index cb9e02ec9f7..5e78e18f247 100644 --- a/plotly/tests/test_core/test_tools/test_make_subplots.py +++ b/plotly/tests/test_core/test_tools/test_make_subplots.py @@ -2017,7 +2017,6 @@ def test_subplot_titles_shared_axes(self): shared_xaxes=True, shared_yaxes=True) self.assertEqual(fig.to_plotly_json(), expected.to_plotly_json()) - def test_subplot_titles_irregular_layout(self): # make a title for each subplot when the layout is irregular: expected = Figure( @@ -2210,3 +2209,58 @@ def test_row_width_and_column_width(self): subplot_titles=('Title 1', 'Title 2', 'Title 3', 'Title 4'), row_width=[1, 3], column_width=[9, 1]) self.assertEqual(fig.to_plotly_json(), expected.to_plotly_json()) + + def test_row_width_and_shared_yaxes(self): + + expected = Figure({ + 'data': [], + 'layout': {'annotations': [{'font': {'size': 16}, + 'showarrow': False, + 'text': 'Title 1', + 'x': 0.225, + 'xanchor': 'center', + 'xref': 'paper', + 'y': 1.0, + 'yanchor': 'bottom', + 'yref': 'paper'}, + {'font': {'size': 16}, + 'showarrow': False, + 'text': 'Title 2', + 'x': 0.775, + 'xanchor': 'center', + 'xref': 'paper', + 'y': 1.0, + 'yanchor': 'bottom', + 'yref': 'paper'}, + {'font': {'size': 16}, + 'showarrow': False, + 'text': 'Title 3', + 'x': 0.225, + 'xanchor': 'center', + 'xref': 'paper', + 'y': 0.1875, + 'yanchor': 'bottom', + 'yref': 'paper'}, + {'font': {'size': 16}, + 'showarrow': False, + 'text': 'Title 4', + 'x': 0.775, + 'xanchor': 'center', + 'xref': 'paper', + 'y': 0.1875, + 'yanchor': 'bottom', + 'yref': 'paper'}], + 'xaxis': {'anchor': 'y', 'domain': [0.0, 0.45]}, + 'xaxis2': {'anchor': 'free', 'domain': [0.55, 1.0], 'position': 0.4375}, + 'xaxis3': {'anchor': 'y2', 'domain': [0.0, 0.45]}, + 'xaxis4': {'anchor': 'free', 'domain': [0.55, 1.0], 'position': 0.0}, + 'yaxis': {'anchor': 'x', 'domain': [0.4375, 1.0]}, + 'yaxis2': {'anchor': 'x3', 'domain': [0.0, 0.1875]}} + }) + + fig = tls.make_subplots(rows=2, cols=2, row_width=[1, 3], shared_yaxes=True, + subplot_titles=('Title 1', 'Title 2', 'Title 3', 'Title 4')) + + self.assertEqual(fig.to_plotly_json(), expected.to_plotly_json()) + + # def test_row_width_and_shared_yaxes(self): \ No newline at end of file diff --git a/plotly/tools.py b/plotly/tools.py index d6430c2a11d..c0950c2d8b0 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -13,6 +13,7 @@ import six import copy +import re from plotly import exceptions, optional_imports, session, utils from plotly.files import (CONFIG_FILE, CREDENTIALS_FILE, FILE_CONTENT, @@ -1079,7 +1080,6 @@ def _get_anchors(r, c, x_cnt, y_cnt, shared_xaxes, shared_yaxes): return x_anchor, y_anchor list_of_domains = [] # added for subplot titles - list_of_domains_complete = [] # hold onto every domain used, even if shared axes # Function pasting x/y domains in layout object (2d case) def _add_domain(layout, x_or_y, label, domain, anchor, position): @@ -1327,8 +1327,24 @@ def _pad(s, cell_len=cell_len): # If shared_axes is True the domin of each subplot is not returned so the # title position must be calculated for each subplot else: - x_dom = [layout[k]['domain'] for k in sorted(layout.to_plotly_json().keys()) if 'xaxis' in k] - y_dom = [layout[k]['domain'] for k in sorted(layout.to_plotly_json().keys()) if 'yaxis' in k] + x_dom_vals = [k for k in layout.to_plotly_json().keys() if 'xaxis' in k] + y_dom_vals = [k for k in layout.to_plotly_json().keys() if 'yaxis' in k] + + # sort xaxis and yaxis layout keys + r = re.compile('\d+') + + def key_func(m): + try: + return int(r.search(m).group(0)) + except AttributeError: + return 0 + + xaxies_labels_sorted = sorted(x_dom_vals, key=key_func) + yaxies_labels_sorted = sorted(y_dom_vals, key=key_func) + + x_dom = [layout[k]['domain'] for k in xaxies_labels_sorted] + y_dom = [layout[k]['domain'] for k in yaxies_labels_sorted] + for index in range(cols): subtitle_pos_x = [] for x_domains in x_dom: @@ -1336,6 +1352,7 @@ def _pad(s, cell_len=cell_len): subtitle_pos_x *= rows if shared_yaxes: + print 'the place to be' for index in range(rows): subtitle_pos_y = [] for y_domain in y_dom: From 54d9065c38ea681b56fdf4cba5d9a16c5475d40c Mon Sep 17 00:00:00 2001 From: Adam Kulidjian Date: Mon, 29 Oct 2018 15:10:29 -0400 Subject: [PATCH 5/5] remove print statement --- plotly/tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plotly/tools.py b/plotly/tools.py index c0950c2d8b0..3f6fd488069 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -1352,7 +1352,6 @@ def key_func(m): subtitle_pos_x *= rows if shared_yaxes: - print 'the place to be' for index in range(rows): subtitle_pos_y = [] for y_domain in y_dom: