Skip to content

Commit 92d13cb

Browse files
px raises error when horizontal or vertical spacing is impossible
describes that facet_{row,col}_spacing should be adjusted.
1 parent 85fa3af commit 92d13cb

File tree

4 files changed

+112
-34
lines changed

4 files changed

+112
-34
lines changed

Diff for: packages/python/plotly/plotly/express/_core.py

+34-15
Original file line numberDiff line numberDiff line change
@@ -2120,22 +2120,41 @@ def init_figure(args, subplot_type, frame_list, nrows, ncols, col_labels, row_la
21202120
for j in range(ncols):
21212121
subplot_labels[i * ncols + j] = col_labels[(nrows - 1 - i) * ncols + j]
21222122

2123+
def _spacing_error_translator(e, direction, facet_arg):
2124+
"""
2125+
Translates the spacing errors thrown by the underlying make_subplots
2126+
routine into one that describes an argument adjustable through px.
2127+
"""
2128+
if ("%s spacing" % (direction,)) in e.args[0]:
2129+
e.args = (
2130+
e.args[0]
2131+
+ """
2132+
Use the {facet_arg} argument to adjust this spacing.""".format(
2133+
facet_arg=facet_arg
2134+
),
2135+
)
2136+
raise e
2137+
21232138
# Create figure with subplots
2124-
fig = make_subplots(
2125-
rows=nrows,
2126-
cols=ncols,
2127-
specs=specs,
2128-
shared_xaxes="all",
2129-
shared_yaxes="all",
2130-
row_titles=[] if facet_col_wrap else list(reversed(row_labels)),
2131-
column_titles=[] if facet_col_wrap else col_labels,
2132-
subplot_titles=subplot_labels if facet_col_wrap else [],
2133-
horizontal_spacing=horizontal_spacing,
2134-
vertical_spacing=vertical_spacing,
2135-
row_heights=row_heights,
2136-
column_widths=column_widths,
2137-
start_cell="bottom-left",
2138-
)
2139+
try:
2140+
fig = make_subplots(
2141+
rows=nrows,
2142+
cols=ncols,
2143+
specs=specs,
2144+
shared_xaxes="all",
2145+
shared_yaxes="all",
2146+
row_titles=[] if facet_col_wrap else list(reversed(row_labels)),
2147+
column_titles=[] if facet_col_wrap else col_labels,
2148+
subplot_titles=subplot_labels if facet_col_wrap else [],
2149+
horizontal_spacing=horizontal_spacing,
2150+
vertical_spacing=vertical_spacing,
2151+
row_heights=row_heights,
2152+
column_widths=column_widths,
2153+
start_cell="bottom-left",
2154+
)
2155+
except ValueError as e:
2156+
_spacing_error_translator(e, "Horizontal", "facet_col_spacing")
2157+
_spacing_error_translator(e, "Vertical", "facet_row_spacing")
21392158

21402159
# Remove explicit font size of row/col titles so template can take over
21412160
for annot in fig.layout.annotations:

Diff for: packages/python/plotly/plotly/subplots.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -530,16 +530,22 @@ def _checks(item, defaults):
530530
)
531531
)
532532

533-
def _check_hv_spacing(dimsize, spacing, name, dimname):
533+
def _check_hv_spacing(dimsize, spacing, name, dimvarname, dimname):
534534
if spacing < 0 or spacing > 1:
535535
raise ValueError("%s spacing must be between 0 and 1." % (name,))
536536
if dimsize <= 1:
537537
return
538538
max_spacing = 1.0 / float(dimsize - 1)
539539
if spacing > max_spacing:
540540
raise ValueError(
541-
"%s spacing cannot be greater than (1 / (%s - 1)) = %f."
542-
% (name, dimname, max_spacing)
541+
"""{name} spacing cannot be greater than (1 / ({dimvarname} - 1)) = {max_spacing:f}.
542+
The resulting plot would have {dimsize} {dimname} ({dimvarname}={dimsize}).""".format(
543+
dimvarname=dimvarname,
544+
name=name,
545+
dimname=dimname,
546+
max_spacing=max_spacing,
547+
dimsize=dimsize,
548+
)
543549
)
544550

545551
# ### horizontal_spacing ###
@@ -549,7 +555,7 @@ def _check_hv_spacing(dimsize, spacing, name, dimname):
549555
else:
550556
horizontal_spacing = 0.2 / cols
551557
# check horizontal_spacing can be satisfied:
552-
_check_hv_spacing(cols, horizontal_spacing, "Horizontal", "cols")
558+
_check_hv_spacing(cols, horizontal_spacing, "Horizontal", "cols", "columns")
553559

554560
# ### vertical_spacing ###
555561
if vertical_spacing is None:
@@ -558,7 +564,7 @@ def _check_hv_spacing(dimsize, spacing, name, dimname):
558564
else:
559565
vertical_spacing = 0.3 / rows
560566
# check vertical_spacing can be satisfied:
561-
_check_hv_spacing(rows, vertical_spacing, "Vertical", "rows")
567+
_check_hv_spacing(rows, vertical_spacing, "Vertical", "rows", "rows")
562568

563569
# ### subplot titles ###
564570
if subplot_titles is None:

Diff for: packages/python/plotly/plotly/tests/test_core/test_px/test_facets.py

+46
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
import plotly
2+
import pandas as pd
13
import plotly.express as px
24
from pytest import approx
5+
import pytest
6+
import random
37

48

59
def test_facets():
@@ -41,3 +45,45 @@ def test_facets():
4145
)
4246
assert fig.layout.xaxis4.domain[0] - fig.layout.xaxis.domain[1] == approx(0.09)
4347
assert fig.layout.yaxis4.domain[0] - fig.layout.yaxis.domain[1] == approx(0.08)
48+
49+
50+
@pytest.fixture
51+
def bad_facet_spacing_df():
52+
NROWS = 101
53+
NDATA = 1000
54+
categories = [n % NROWS for n in range(NDATA)]
55+
df = pd.DataFrame(
56+
{
57+
"x": [random.random() for _ in range(NDATA)],
58+
"y": [random.random() for _ in range(NDATA)],
59+
"category": categories,
60+
}
61+
)
62+
return df
63+
64+
65+
def test_bad_facet_spacing_eror(bad_facet_spacing_df):
66+
df = bad_facet_spacing_df
67+
with pytest.raises(
68+
ValueError, match="Use the facet_row_spacing argument to adjust this spacing\."
69+
):
70+
fig = px.scatter(
71+
df, x="x", y="y", facet_row="category", facet_row_spacing=0.01001
72+
)
73+
with pytest.raises(
74+
ValueError, match="Use the facet_col_spacing argument to adjust this spacing\."
75+
):
76+
fig = px.scatter(
77+
df, x="x", y="y", facet_col="category", facet_col_spacing=0.01001
78+
)
79+
# Check error is not raised when the spacing is OK
80+
try:
81+
fig = px.scatter(df, x="x", y="y", facet_row="category", facet_row_spacing=0.01)
82+
except ValueError:
83+
# Error shouldn't be raised, so fail if it is
84+
assert False
85+
try:
86+
fig = px.scatter(df, x="x", y="y", facet_col="category", facet_col_spacing=0.01)
87+
except ValueError:
88+
# Error shouldn't be raised, so fail if it is
89+
assert False

Diff for: packages/python/plotly/plotly/tests/test_core/test_subplots/test_make_subplots.py

+21-14
Original file line numberDiff line numberDiff line change
@@ -1940,22 +1940,29 @@ def test_if_passed_figure(self):
19401940
def test_make_subplots_spacing_error():
19411941
# check exception describing maximum value for horizontal_spacing or
19421942
# vertical_spacing is raised when spacing exceeds that value
1943-
with pytest.raises(
1944-
ValueError,
1945-
match=(
1946-
"^%s spacing cannot be greater than \(1 / \(%s - 1\)\) = %f.$"
1947-
% ("Vertical", "rows", 0.02)
1943+
for match in [
1944+
(
1945+
"^%s spacing cannot be greater than \(1 / \(%s - 1\)\) = %f."
1946+
% ("Vertical", "rows", 1.0 / 50.0)
19481947
).replace(".", "\."),
1949-
):
1950-
fig = subplots.make_subplots(51, 1, vertical_spacing=0.0201)
1951-
with pytest.raises(
1952-
ValueError,
1953-
match=(
1954-
"^%s spacing cannot be greater than \(1 / \(%s - 1\)\) = %f.$"
1955-
% ("Horizontal", "cols", 0.02)
1948+
"The resulting plot would have 51 rows \(rows=51\)\.$",
1949+
]:
1950+
with pytest.raises(
1951+
ValueError, match=match,
1952+
):
1953+
fig = subplots.make_subplots(51, 1, vertical_spacing=0.0201)
1954+
for match in [
1955+
(
1956+
"^%s spacing cannot be greater than \(1 / \(%s - 1\)\) = %f."
1957+
% ("Horizontal", "cols", 1.0 / 50.0)
19561958
).replace(".", "\."),
1957-
):
1958-
fig = subplots.make_subplots(1, 51, horizontal_spacing=0.0201)
1959+
"The resulting plot would have 51 columns \(cols=51\)\.$",
1960+
]:
1961+
with pytest.raises(
1962+
ValueError, match=match,
1963+
):
1964+
fig = subplots.make_subplots(1, 51, horizontal_spacing=0.0201)
1965+
# Check it's not raised when it's not beyond the maximum
19591966
try:
19601967
fig = subplots.make_subplots(51, 1, vertical_spacing=0.0200)
19611968
except ValueError:

0 commit comments

Comments
 (0)