Skip to content

Commit bb9ca36

Browse files
committed
Fix px marginal color with named continuous color scales
When color is continuous, Plotly Express colors marginal traces with the first entry of color_continuous_scale. For a named scale such as "Viridis" that entry was the first character of the string ("V"), and for a list of [position, color] pairs it was the pair itself, both of which are invalid marker colors. Normalize the scale with ColorscaleValidator before taking its first color. Closes #2059
1 parent 0de8a38 commit bb9ca36

3 files changed

Lines changed: 34 additions & 1 deletion

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
2121
- Fix `convert_dash` mapping dotted lines (`linestyle=':'`) to invalid `'circle'` in Plotly, correctly converting them to `'dot'` and preserving custom dash patterns as pixel lists, [[#5719](https://github.com/plotly/plotly.py/pull/5719)] with thanks to @robertoffmoura for the contribution!
2222
- Fix `px.sunburst`, `px.treemap` and `px.icicle` listing sectors in a different order on every run when `path` is used with a Polars DataFrame; sectors now follow their order of first appearance for all dataframe backends [[#5766](https://github.com/plotly/plotly.py/pull/5766)], with thanks to @Irahan2 for the contribution!
2323
- Fix `mpl_to_plotly` conversion of matplotlib contour lines and line collections: close contour rings ending with `Z` codes, support dash styles, convert date x-axes, separate disjoint subpaths with `None` separators, hide line collection traces from the legend by default, and group consecutive same-style lines into single traces [[#5770](https://github.com/plotly/plotly.py/pull/5770)], with thanks to @robertoffmoura for the contribution!
24+
- Fix Plotly Express marginal plots (`marginal_x`/`marginal_y`) failing with an invalid `marker.color` (such as `'V'` for `"Viridis"`) when `color` is continuous and `color_continuous_scale` is given as a named color scale or as a list of `[position, color]` pairs; the marginal traces now use the first color of the normalized scale [[#5839](https://github.com/plotly/plotly.py/pull/5839)]
2425

2526
## [7.1.0] - 2026-09-15
2627

‎plotly/express/_core.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,13 @@ def make_trace_spec(args, constructor, attrs, trace_patch):
995995
):
996996
if "marker" not in trace_spec.trace_patch:
997997
trace_spec.trace_patch["marker"] = dict()
998-
first_default_color = args["color_continuous_scale"][0]
998+
# color_continuous_scale may be a named scale (e.g. "Viridis") or a
999+
# list of [position, color] pairs, so normalize it before picking
1000+
# the first color
1001+
colorscale_validator = ColorscaleValidator("colorscale", "make_figure")
1002+
first_default_color = colorscale_validator.validate_coerce(
1003+
args["color_continuous_scale"]
1004+
)[0][1]
9991005
trace_spec.trace_patch["marker"]["color"] = first_default_color
10001006
result.append(trace_spec)
10011007

‎tests/test_optional/test_px/test_marginals.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,29 @@ def test_unsupported_marginal_raises_clear_error(): # issue 4654
204204
px.scatter(x=[1, 2, 3], y=[2, 3, 4], marginal_y="density")
205205
with pytest.raises(ValueError, match="Supported marginal plot types"):
206206
px.histogram(x=[1, 2, 3], marginal="density")
207+
208+
209+
@pytest.mark.parametrize(
210+
"color_continuous_scale, expected_color",
211+
[
212+
("Viridis", px.colors.sequential.Viridis[0]),
213+
("magma", px.colors.sequential.Magma[0]),
214+
("Viridis_r", px.colors.sequential.Viridis[-1]),
215+
(["red", "blue"], "red"),
216+
([[0, "red"], [1, "blue"]], "red"),
217+
],
218+
)
219+
@pytest.mark.parametrize("marginal", ["histogram", "box", "violin", "rug"])
220+
def test_marginal_color_with_continuous_color_scale(
221+
color_continuous_scale, expected_color, marginal
222+
): # issue 2059
223+
fig = px.scatter(
224+
x=[1, 2, 3],
225+
y=[2, 3, 4],
226+
color=[1, 2, 3],
227+
marginal_x=marginal,
228+
marginal_y=marginal,
229+
color_continuous_scale=color_continuous_scale,
230+
)
231+
assert fig.data[1].marker.color == expected_color
232+
assert fig.data[2].marker.color == expected_color

0 commit comments

Comments
 (0)