Skip to content

Commit

Permalink
Handle large collections of subplots without validation error (#1091)
Browse files Browse the repository at this point in the history
* Fix for GH1031, large subplot grids resulted in validation error.

* Removed internal usage of deprecated classes.
  • Loading branch information
jonmmease authored Aug 5, 2018
1 parent d670bb0 commit 7ab0c90
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 8 deletions.
6 changes: 3 additions & 3 deletions plotly/matplotlylib/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ def draw_text(self, **props):
if not align:
align = props['style']['halign'] # mpl default
if 'annotations' not in self.plotly_fig['layout']:
self.plotly_fig['layout']['annotations'] = go.Annotations()
self.plotly_fig['layout']['annotations'] = []
if props['text_type'] == 'xlabel':
self.msg += " Text object is an xlabel\n"
self.draw_xlabel(**props)
Expand Down Expand Up @@ -576,7 +576,7 @@ def draw_text(self, **props):
yref = 'paper'
xanchor = props['style']['halign'] # no difference here!
yanchor = mpltools.convert_va(props['style']['valign'])
annotation = go.Annotation(
annotation = go.layout.Annotation(
text=(str(props['text']) if
isinstance(props['text'], six.string_types) else
props['text']),
Expand Down Expand Up @@ -631,7 +631,7 @@ def draw_title(self, **props):
'position'])
x, y = mpltools.display_to_paper(x_px, y_px,
self.plotly_fig['layout'])
annotation = go.Annotation(
annotation = go.layout.Annotation(
text=props['text'],
font=go.layout.annotation.Font(
color=props['style']['color'],
Expand Down
27 changes: 27 additions & 0 deletions plotly/tests/test_core/test_tools/test_make_subplots.py
Original file line number Diff line number Diff line change
Expand Up @@ -2128,3 +2128,30 @@ def test_subplot_titles_insets(self):
fig = tls.make_subplots(insets=[{'cell': (1, 1), 'l': 0.7, 'b': 0.3}],
subplot_titles=("", 'Inset'))
self.assertEqual(fig, expected)

def test_large_columns_no_errors(self):
"""
Test that creating subplots with a large number of columns, and
zero vertical spacing doesn't result in domain values that are out
of range.
Here is the error that was reported in GH1031
ValueError:
Invalid value of type 'builtins.float' received for the
'domain[1]' property of layout.yaxis
Received value: 1.0000000000000007
The 'domain[1]' property is a number and may be specified as:
- An int or float in the interval [0, 1]
"""
v_space = 0.0

# 2D
fig = tls.make_subplots(100, 1, vertical_spacing=v_space)

# 3D
fig = tls.make_subplots(100, 1,
vertical_spacing=v_space,
specs=[[{'is_3d': True}] for _ in range(100)])
16 changes: 11 additions & 5 deletions plotly/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,9 +568,9 @@ def get_subplots(rows=1, columns=1, print_grid=False, **kwargs):
y_start = (plot_height + vertical_spacing) * rrr
y_end = y_start + plot_height

xaxis = graph_objs.XAxis(domain=[x_start, x_end], anchor=x_anchor)
xaxis = dict(domain=[x_start, x_end], anchor=x_anchor)
fig['layout'][xaxis_name] = xaxis
yaxis = graph_objs.YAxis(domain=[y_start, y_end], anchor=y_anchor)
yaxis = dict(domain=[y_start, y_end], anchor=y_anchor)
fig['layout'][yaxis_name] = yaxis
plot_num += 1

Expand Down Expand Up @@ -1080,7 +1080,11 @@ def _get_anchors(r, c, x_cnt, y_cnt, shared_xaxes, shared_yaxes):
# Function pasting x/y domains in layout object (2d case)
def _add_domain(layout, x_or_y, label, domain, anchor, position):
name = label[0] + 'axis' + label[1:]
axis = {'domain': domain}

# Clamp domain elements between [0, 1].
# This is only needed to combat numerical precision errors
# See GH1031
axis = {'domain': [max(0.0, domain[0]), min(1.0, domain[1])]}
if anchor:
axis['anchor'] = anchor
if isinstance(position, float):
Expand All @@ -1090,7 +1094,9 @@ def _add_domain(layout, x_or_y, label, domain, anchor, position):

# Function pasting x/y domains in layout object (3d case)
def _add_domain_is_3d(layout, s_label, x_domain, y_domain):
scene = graph_objs.Scene(domain={'x': x_domain, 'y': y_domain})
scene = dict(
domain={'x': [max(0.0, x_domain[0]), min(1.0, x_domain[1])],
'y': [max(0.0, y_domain[0]), min(1.0, y_domain[1])]})
layout[s_label] = scene

x_cnt = y_cnt = s_cnt = 1 # subplot axis/scene counters
Expand Down Expand Up @@ -1339,7 +1345,7 @@ def _pad(s, cell_len=cell_len):
'yref': 'paper',
'text': subplot_titles[index],
'showarrow': False,
'font': graph_objs.Font(size=16),
'font': dict(size=16),
'xanchor': 'center',
'yanchor': 'bottom'
})
Expand Down

0 comments on commit 7ab0c90

Please sign in to comment.