diff --git a/CHANGELOG.md b/CHANGELOG.md index 78a9966375c..880774085a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fixed another compatibility issue with Pandas 2.0, just affecting `px.*(line_close=True)` [[#4190](https://github.com/plotly/plotly.py/pull/4190)] + - Added some rounding to the `make_subplots` function to handle situations where the user-input specs cause the domain to exceed 1 by small amounts https://github.com/plotly/plotly.py/pull/4153 ## [5.14.1] - 2023-04-05 diff --git a/packages/python/plotly/plotly/_subplots.py b/packages/python/plotly/plotly/_subplots.py index 24ec630e01d..f2606f49e45 100644 --- a/packages/python/plotly/plotly/_subplots.py +++ b/packages/python/plotly/plotly/_subplots.py @@ -671,7 +671,6 @@ def _check_hv_spacing(dimsize, spacing, name, dimvarname, dimname): # Loop through specs -- (r, c) <-> (row, col) for r, spec_row in enumerate(specs): for c, spec in enumerate(spec_row): - if spec is None: # skip over None cells continue @@ -702,6 +701,46 @@ def _check_hv_spacing(dimsize, spacing, name, dimvarname, dimname): else: y_s = grid[r_spanned][c][1] + spec["b"] y_e = grid[r][c][1] + heights[-1 - r] - spec["t"] + + if y_s < 0.0: + # round for values very close to one + # handles some floating point errors + if y_s > -0.01: + y_s = 0.0 + else: + raise Exception( + "A combination of the 'b' values, heights, and " + "number of subplots too large for this subplot grid." + ) + if y_s > 1.0: + # round for values very close to one + # handles some floating point errors + if y_s < 1.01: + y_s = 1.0 + else: + raise Exception( + "A combination of the 'b' values, heights, and " + "number of subplots too large for this subplot grid." + ) + + if y_e < 0.0: + if y_e > -0.01: + y_e = 0.0 + else: + raise Exception( + "A combination of the 't' values, heights, and " + "number of subplots too large for this subplot grid." + ) + + if y_e > 1.0: + if y_e < 1.01: + y_e = 1.0 + else: + raise Exception( + "A combination of the 't' values, heights, and " + "number of subplots too large for this subplot grid." + ) + y_domain = [y_s, y_e] list_of_domains.append(x_domain) @@ -726,7 +765,6 @@ def _check_hv_spacing(dimsize, spacing, name, dimvarname, dimname): insets_ref = [None for inset in range(len(insets))] if insets else None if insets: for i_inset, inset in enumerate(insets): - r = inset["cell"][0] - 1 c = inset["cell"][1] - 1 @@ -1052,7 +1090,6 @@ def _subplot_type_for_trace_type(trace_type): def _validate_coerce_subplot_type(subplot_type): - # Lowercase subplot_type orig_subplot_type = subplot_type subplot_type = subplot_type.lower() @@ -1200,7 +1237,6 @@ def _build_subplot_title_annotations( def _build_grid_str(specs, grid_ref, insets, insets_ref, row_seq): - # Compute rows and columns rows = len(specs) cols = len(specs[0]) @@ -1257,7 +1293,6 @@ def _pad(s, cell_len=cell_len): # Loop through specs, fill in _tmp for r, spec_row in enumerate(specs): for c, spec in enumerate(spec_row): - ref = grid_ref[r][c] if ref is None: if _tmp[r][c] == "": @@ -1339,7 +1374,6 @@ def _pad(s, cell_len=cell_len): def _set_trace_grid_reference(trace, layout, grid_ref, row, col, secondary_y=False): - if row <= 0: raise Exception( "Row value is out of range. " "Note: the starting cell is (1, 1)" @@ -1461,7 +1495,6 @@ def _get_grid_subplot(fig, row, col, secondary_y=False): def _get_subplot_ref_for_trace(trace): - if "domain" in trace: return SubplotRef( subplot_type="domain", diff --git a/packages/python/plotly/plotly/tests/test_core/test_subplots/test_make_subplots.py b/packages/python/plotly/plotly/tests/test_core/test_subplots/test_make_subplots.py index 7c7a1a9da8e..28ad18f19a6 100644 --- a/packages/python/plotly/plotly/tests/test_core/test_subplots/test_make_subplots.py +++ b/packages/python/plotly/plotly/tests/test_core/test_subplots/test_make_subplots.py @@ -660,6 +660,22 @@ def test_specs_padding(self): ) self.assertEqual(fig.to_plotly_json(), expected.to_plotly_json()) + def test_specs_rounding_rounds_down(self): + n_subplots = 8 + padding_size = 0.2 + + specs = [] + for _ in range(n_subplots): + specs.append([{"b": padding_size / 2.0, "t": padding_size / 2.0}]) + + fig = subplots.make_subplots(rows=n_subplots, specs=specs) + self.assertTrue( + all( + fig.layout[f"yaxis{i if i > 1 else ''}"]["domain"][0] <= 1.0 + for i in range(1, n_subplots + 1) + ) + ) + def test_specs_padding_bottom_left(self): expected = Figure( data=Data(), @@ -1592,7 +1608,6 @@ def test_large_columns_no_errors(self): ) def test_row_width_and_column_width(self): - expected = Figure( { "data": [], @@ -1680,7 +1695,6 @@ def test_row_width_and_column_width(self): self.assertEqual(fig.to_plotly_json(), expected.to_plotly_json()) def test_row_width_and_shared_yaxes(self): - expected = Figure( { "data": [],