Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 45ddf75

Browse filesBrowse files
Merge pull request plotly#3014 from plotly/pre_distfuncs
Extra tests, bugfix and finish histnorm labelling
2 parents 234ec0d + 0501de6 commit 45ddf75
Copy full SHA for 45ddf75

File tree

Expand file treeCollapse file tree

4 files changed

+181
-23
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+181
-23
lines changed

‎packages/python/plotly/plotly/express/_core.py

Copy file name to clipboardExpand all lines: packages/python/plotly/plotly/express/_core.py
+29-11Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -147,19 +147,36 @@ def _is_continuous(df, col_name):
147147

148148

149149
def get_decorated_label(args, column, role):
150-
label = get_label(args, column)
150+
original_label = label = get_label(args, column)
151151
if "histfunc" in args and (
152152
(role == "z")
153153
or (role == "x" and "orientation" in args and args["orientation"] == "h")
154154
or (role == "y" and "orientation" in args and args["orientation"] == "v")
155155
):
156-
if label:
157-
label = "%s of %s" % (args["histfunc"] or "count", label)
156+
histfunc = args["histfunc"] or "count"
157+
if histfunc != "count":
158+
label = "%s of %s" % (histfunc, label)
158159
else:
159160
label = "count"
160161

161162
if "histnorm" in args and args["histnorm"] is not None:
162-
label = "%s of %s" % (args["histnorm"], label)
163+
if label == "count":
164+
label = args["histnorm"]
165+
else:
166+
histnorm = args["histnorm"]
167+
if histfunc == "sum":
168+
if histnorm == "probability":
169+
label = "%s of %s" % ("fraction", label)
170+
elif histnorm == "percent":
171+
label = "%s of %s" % (histnorm, label)
172+
else:
173+
label = "%s weighted by %s" % (histnorm, original_label)
174+
elif histnorm == "probability":
175+
label = "%s of sum of %s" % ("fraction", label)
176+
elif histnorm == "percent":
177+
label = "%s of sum of %s" % ("percent", label)
178+
else:
179+
label = "%s of %s" % (histnorm, label)
163180

164181
if "barnorm" in args and args["barnorm"] is not None:
165182
label = "%s (normalized as %s)" % (label, args["barnorm"])
@@ -924,13 +941,6 @@ def apply_default_cascade(args):
924941
"longdashdot",
925942
]
926943

927-
# If both marginals and faceting are specified, faceting wins
928-
if args.get("facet_col", None) is not None and args.get("marginal_y", None):
929-
args["marginal_y"] = None
930-
931-
if args.get("facet_row", None) is not None and args.get("marginal_x", None):
932-
args["marginal_x"] = None
933-
934944

935945
def _check_name_not_reserved(field_name, reserved_names):
936946
if field_name not in reserved_names:
@@ -1765,6 +1775,14 @@ def infer_config(args, constructor, trace_patch, layout_patch):
17651775
args[position] = args["marginal"]
17661776
args[other_position] = None
17671777

1778+
# If both marginals and faceting are specified, faceting wins
1779+
if args.get("facet_col", None) is not None and args.get("marginal_y", None):
1780+
args["marginal_y"] = None
1781+
1782+
if args.get("facet_row", None) is not None and args.get("marginal_x", None):
1783+
args["marginal_x"] = None
1784+
1785+
# facet_col_wrap only works if no marginals or row faceting is used
17681786
if (
17691787
args.get("marginal_x", None) is not None
17701788
or args.get("marginal_y", None) is not None

‎packages/python/plotly/plotly/tests/test_core/test_px/test_facets.py

Copy file name to clipboardExpand all lines: packages/python/plotly/plotly/tests/test_core/test_px/test_facets.py
+47Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,53 @@ def test_facets():
4747
assert fig.layout.yaxis4.domain[0] - fig.layout.yaxis.domain[1] == approx(0.08)
4848

4949

50+
def test_facets_with_marginals():
51+
df = px.data.tips()
52+
53+
fig = px.histogram(df, x="total_bill", facet_col="sex", marginal="rug")
54+
assert len(fig.data) == 4
55+
fig = px.histogram(df, x="total_bill", facet_row="sex", marginal="rug")
56+
assert len(fig.data) == 2
57+
58+
fig = px.histogram(df, y="total_bill", facet_col="sex", marginal="rug")
59+
assert len(fig.data) == 2
60+
fig = px.histogram(df, y="total_bill", facet_row="sex", marginal="rug")
61+
assert len(fig.data) == 4
62+
63+
fig = px.scatter(df, x="total_bill", y="tip", facet_col="sex", marginal_x="rug")
64+
assert len(fig.data) == 4
65+
fig = px.scatter(
66+
df, x="total_bill", y="tip", facet_col="day", facet_col_wrap=2, marginal_x="rug"
67+
)
68+
assert len(fig.data) == 8 # ignore the wrap when marginal is used
69+
fig = px.scatter(df, x="total_bill", y="tip", facet_col="sex", marginal_y="rug")
70+
assert len(fig.data) == 2 # ignore the marginal in the facet direction
71+
72+
fig = px.scatter(df, x="total_bill", y="tip", facet_row="sex", marginal_x="rug")
73+
assert len(fig.data) == 2 # ignore the marginal in the facet direction
74+
fig = px.scatter(df, x="total_bill", y="tip", facet_row="sex", marginal_y="rug")
75+
assert len(fig.data) == 4
76+
77+
fig = px.scatter(
78+
df, x="total_bill", y="tip", facet_row="sex", marginal_y="rug", marginal_x="rug"
79+
)
80+
assert len(fig.data) == 4 # ignore the marginal in the facet direction
81+
fig = px.scatter(
82+
df, x="total_bill", y="tip", facet_col="sex", marginal_y="rug", marginal_x="rug"
83+
)
84+
assert len(fig.data) == 4 # ignore the marginal in the facet direction
85+
fig = px.scatter(
86+
df,
87+
x="total_bill",
88+
y="tip",
89+
facet_row="sex",
90+
facet_col="sex",
91+
marginal_y="rug",
92+
marginal_x="rug",
93+
)
94+
assert len(fig.data) == 2 # ignore all marginals
95+
96+
5097
@pytest.fixture
5198
def bad_facet_spacing_df():
5299
NROWS = 101
+26Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import plotly.express as px
2+
import pytest
3+
4+
5+
@pytest.mark.parametrize("px_fn", [px.scatter, px.density_heatmap, px.density_contour])
6+
@pytest.mark.parametrize("marginal_x", [None, "histogram", "box", "violin"])
7+
@pytest.mark.parametrize("marginal_y", [None, "rug"])
8+
def test_xy_marginals(px_fn, marginal_x, marginal_y):
9+
df = px.data.tips()
10+
11+
fig = px_fn(
12+
df, x="total_bill", y="tip", marginal_x=marginal_x, marginal_y=marginal_y
13+
)
14+
assert len(fig.data) == 1 + (marginal_x is not None) + (marginal_y is not None)
15+
16+
17+
@pytest.mark.parametrize("px_fn", [px.histogram])
18+
@pytest.mark.parametrize("marginal", [None, "rug", "histogram", "box", "violin"])
19+
@pytest.mark.parametrize("orientation", ["h", "v"])
20+
def test_single_marginals(px_fn, marginal, orientation):
21+
df = px.data.tips()
22+
23+
fig = px_fn(
24+
df, x="total_bill", y="total_bill", marginal=marginal, orientation=orientation
25+
)
26+
assert len(fig.data) == 1 + (marginal is not None)

‎packages/python/plotly/plotly/tests/test_core/test_px/test_px_functions.py

Copy file name to clipboardExpand all lines: packages/python/plotly/plotly/tests/test_core/test_px/test_px_functions.py
+79-12Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -379,18 +379,73 @@ def test_parcats_dimensions_max():
379379
assert [d.label for d in fig.data[0].dimensions] == ["sex", "smoker", "day", "size"]
380380

381381

382-
def test_histfunc_hoverlabels():
382+
@pytest.mark.parametrize("histfunc,y", [(None, None), ("count", "tip")])
383+
def test_histfunc_hoverlabels_univariate(histfunc, y):
384+
def check_label(label, fig):
385+
assert fig.layout.yaxis.title.text == label
386+
assert label + "=" in fig.data[0].hovertemplate
387+
383388
df = px.data.tips()
384-
fig = px.histogram(df, x="total_bill")
385-
label = "count"
386-
assert fig.layout.yaxis.title.text == label
387-
assert label + "=" in fig.data[0].hovertemplate
388389

389-
fig = px.histogram(df, x="total_bill", y="tip")
390-
label = "sum of tip"
391-
assert fig.layout.yaxis.title.text == label
392-
assert label + "=" in fig.data[0].hovertemplate
390+
# base case, just "count" (note count(tip) is same as count())
391+
fig = px.histogram(df, x="total_bill", y=y, histfunc=histfunc)
392+
check_label("count", fig)
393+
394+
# without y, label is just histnorm
395+
for histnorm in ["probability", "percent", "density", "probability density"]:
396+
fig = px.histogram(
397+
df, x="total_bill", y=y, histfunc=histfunc, histnorm=histnorm
398+
)
399+
check_label(histnorm, fig)
400+
401+
for histnorm in ["probability", "percent", "density", "probability density"]:
402+
for barnorm in ["percent", "fraction"]:
403+
fig = px.histogram(
404+
df,
405+
x="total_bill",
406+
y=y,
407+
histfunc=histfunc,
408+
histnorm=histnorm,
409+
barnorm=barnorm,
410+
)
411+
check_label("%s (normalized as %s)" % (histnorm, barnorm), fig)
412+
413+
414+
def test_histfunc_hoverlabels_bivariate():
415+
def check_label(label, fig):
416+
assert fig.layout.yaxis.title.text == label
417+
assert label + "=" in fig.data[0].hovertemplate
393418

419+
df = px.data.tips()
420+
421+
# with y, should be same as forcing histfunc to sum
422+
fig = px.histogram(df, x="total_bill", y="tip")
423+
check_label("sum of tip", fig)
424+
425+
# change probability to fraction when histfunc is sum
426+
fig = px.histogram(df, x="total_bill", y="tip", histnorm="probability")
427+
check_label("fraction of sum of tip", fig)
428+
429+
# percent is percent
430+
fig = px.histogram(df, x="total_bill", y="tip", histnorm="percent")
431+
check_label("percent of sum of tip", fig)
432+
433+
# the other two are "weighted by"
434+
for histnorm in ["density", "probability density"]:
435+
fig = px.histogram(df, x="total_bill", y="tip", histnorm=histnorm)
436+
check_label("%s weighted by tip" % histnorm, fig)
437+
438+
# check a few "normalized by"
439+
for histnorm in ["density", "probability density"]:
440+
for barnorm in ["fraction", "percent"]:
441+
fig = px.histogram(
442+
df, x="total_bill", y="tip", histnorm=histnorm, barnorm=barnorm
443+
)
444+
check_label(
445+
"%s weighted by tip (normalized as %s)" % (histnorm, barnorm), fig
446+
)
447+
448+
# these next two are weird but OK...
394449
fig = px.histogram(
395450
df,
396451
x="total_bill",
@@ -399,9 +454,21 @@ def test_histfunc_hoverlabels():
399454
histnorm="probability",
400455
barnorm="percent",
401456
)
402-
label = "probability of min of tip (normalized as percent)"
403-
assert fig.layout.yaxis.title.text == label
404-
assert label + "=" in fig.data[0].hovertemplate
457+
check_label("fraction of sum of min of tip (normalized as percent)", fig)
458+
459+
fig = px.histogram(
460+
df,
461+
x="total_bill",
462+
y="tip",
463+
histfunc="avg",
464+
histnorm="percent",
465+
barnorm="fraction",
466+
)
467+
check_label("percent of sum of avg of tip (normalized as fraction)", fig)
468+
469+
# this next one is basically "never do this" but needs a defined behaviour
470+
fig = px.histogram(df, x="total_bill", y="tip", histfunc="max", histnorm="density")
471+
check_label("density of max of tip", fig)
405472

406473

407474
def test_timeline():

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.