Open MoustHolmes opened 10 months ago
Can confirm.
Here's simple example to see difference:
from plotly import express as px
import pandas as pd
df = pd.DataFrame({"x": [1, 2], "y": [3,4]})
# works
px.density_heatmap(df)
# works
px.density_heatmap(df, color_continuous_scale="Viridis")
# works
px.density_heatmap(df, marginal_y="histogram", marginal_x="histogram")
# raises error
px.density_heatmap(df, marginal_y="histogram", marginal_x="histogram", color_continuous_scale="Viridis")
I can confirm the same error message.
from plotly import express as px
import pandas as pd
dff = px.data.tips()
fig = px.density_heatmap(dff, x="total_bill", y="tip", marginal_x="histogram")
fig.show()
error_fig = px.density_heatmap(dff, x="total_bill", y="tip", marginal_x="histogram", color_continuous_scale="Viridis")
error_fig.show()
It's as if it's trying to apply to the color scale to the marginal histogram as well...
I'll mark this as a bug for now, and we'll see what our engineers think once they have more time to take a deeper look.
To understand what happens we have to study the "anatomy" of this fig:
fig=px.density_heatmap(df, marginal_y="histogram", marginal_x="histogram")
Let us inspect:
fig.data:
(Histogram2d({
'coloraxis': 'coloraxis',
'hovertemplate': 'index=%{x}<br>value=%{y}<br>count=%{z}<extra></extra>',
'name': '',
'x': array([0, 1, 0, 1], dtype=int64),
'xaxis': 'x',
'xbingroup': 'x',
'y': array([1, 2, 3, 4], dtype=int64),
'yaxis': 'y',
'ybingroup': 'y'
}),
Histogram({
'alignmentgroup': 'True',
'bingroup': 'x',
'hovertemplate': 'index=%{x}<br>count=%{y}<extra></extra>',
'legendgroup': '',
'marker': {'color': '#0d0887'},
'name': '',
'offsetgroup': '',
'opacity': 0.5,
'showlegend': False,
'x': array([0, 1, 0, 1], dtype=int64),
'xaxis': 'x3',
'yaxis': 'y3'
}),
Histogram({
'alignmentgroup': 'True',
'bingroup': 'y',
'hovertemplate': 'value=%{y}<br>count=%{x}<extra></extra>',
'legendgroup': '',
'marker': {'color': '#0d0887'},
'name': '',
'offsetgroup': '',
'opacity': 0.5,
'showlegend': False,
'xaxis': 'x2',
'y': array([1, 2, 3, 4], dtype=int64),
'yaxis': 'y2'
}))
and
fig.layout.coloraxis
layout.Coloraxis({
'colorbar': {'title': {'text': 'count'}},
'colorscale': [[0.0, '#0d0887'], [0.1111111111111111, '#46039f'],
[0.2222222222222222, '#7201a8'], [0.3333333333333333,
'#9c179e'], [0.4444444444444444, '#bd3786'],
[0.5555555555555556, '#d8576b'], [0.6666666666666666,
'#ed7953'], [0.7777777777777778, '#fb9f3a'],
[0.8888888888888888, '#fdca26'], [1.0, '#f0f921']]
})
What we see? The values in the Histogram2d
are colormapped to the default colorscale, plasma, set via layout.coloraxis.colorscale
, while the two marginal histograms are colored with the first color, '#0d0887'
, in the plasma colorscale, and the opacity 0.5.
Hence if you want to change the colorscale to "Viridis", you may update it for the histogram2d as follows:
fig.update_layout(coloraxis_colorscale='Viridis')
, but you cannot access the color corresponding to 0.0 (the first color) in the viridis colorscale, to set is as a color for marginal histograms. As a conclusion, I think that there is no bug, and this type of plot was designed only for plasma colorscale, i.e. with the actual default settings, because setting the color for marginal distribution requires a non-standard operation . Looking at the https://github.com/plotly/plotly.py/blob/master/packages/python/plotly/plotly/express/colors/init.py we realize that using a particular colorscale would involve many searches to access the first color in that colorscale.
I don't think that the intention here was to make the marginal histogram of the colorscale you selected for the entire heatmap, but rather to just add a marginal histogram, in addition to main focus - the heatmap. Meaning, I would be fine if the marginal histogram remains in whatever colorscale as long as it is there and the heatmap follows the desired colormap. I was using heatmaps for a long time quite fine with custom colormap and didn't care that marginal histogram has a single color (aka colorscale was applied only to the heatmap, but not to marginal histogram) (btw, in plotly_dark they look terrible - hard to distinct). I hope the intention is clear: being able to use heatmpas with colorscales while also adding marginal heatmaps (with or without colorscale) - it's not to have both heatmap and histogram in single colorscale.
I was going to try a workaround with converting Viridis to 2d list, but i see that it might not happen so easily...
I explained how this kind of px. density_heatmap is designed. You can change the default settings performing trace updates ( for the color of the marginal histograms), respectively, layout update: fig.update_layout(coloraxis_colorscale='NewcolorscaleName')
Example that still works:
# custom color scale definition
cscale = [
[0, '#FFFF66'],
[0.05, '#FFFF66'],
[0.7, '#ff0000'],
[1, '#ff0000']
]
# no error
px.density_heatmap(df, marginal_y="histogram", marginal_x="histogram", color_continuous_scale=cscale)
What is strange to me is that I can set a custom colorscale, and it still works. And histogram won't try to be colorful in this case.
Judging by the fact that this issue is brought up only now (I have some vague memory of this same error from a few months ago) - people are not using these 2 features together.
I appreciate workaround you provided, but I don't see OP intention to apply colorscale to marginal histogram, nor do I want it - it's simply that using colorscale by name for heatmap with marginal histogram (regardless of colors of the marginal histogram) is not working is surprising and the resulting error is poorly communicated.
I suggest letting the marginal histogram ignore the color_continuous_scale
param from density_heatmap
and similr figure factories, because it is not like that histogram will benefit much from being colorful.
Maybe it would be nice to get a separate way of making that histogram obey some custom colorscale, but seems like it's not been requested yet.
This code snippet from ChatGPT3.5 is not raising error:
import numpy as np
import plotly.colors as pc
# Sample values (from 0 to 1)
values = np.linspace(0, 1, 10) # Replace with your values
# Get the Viridis colorscale
viridis_colorscale = pc.sequential.Viridis
# Map values to colors based on the Viridis colorscale
colors_for_values = [[value, viridis_colorscale[int(value * (len(viridis_colorscale) - 1))]] for value in values]
# does not raise error
px.density_heatmap(df, marginal_y="histogram", marginal_x="histogram", color_continuous_scale=colors_for_values)
The solution given by ChatGPT is a workaround that redefines an existing colorscale to give access to its first color. You can avoid this redefinition, as I said in the previous post, by updates:
fig = px.density_heatmap(df, marginal_y="histogram", marginal_x="histogram")
fig.update_layout(coloraxis_colorscale="Viridis")
fig.update_traces(marker_color=pc.sequential.Viridis[0], selector=dict(type="histogram"))
I see the issue doesn't progress...
Could my suggestion be implemented?
I suggest letting the marginal histogram ignore the color_continuous_scale param from density_heatmap and similar figure factories...
Hi - we are tidying up stale issues and PRs in Plotly's public repositories so that we can focus on things that are still important to our community. Since this one has been sitting for a while, I'm going to close it; if you'd like to submit a PR, we'd be happy to prioritize a review, and if it's a request for tech support, please post in our community forum. Thank you - @gvwilson
I was trying out the examples from https://plotly.com/python/2D-Histogram/ and wanted to use the density heat map with marginal distribution on x and y but wanted to change the colour map to align with some previous work. I found when combining the two subsequent examples from the 2D-Histogram page I got an error. If the marginal distribution and color_continuous_scale aren't meant to be used together there should at least be a better error message. I btw also test other kinds of distributions like box, violin and rug with the same result.
plotly Version: 5.17.0
My code:
Traceback: