I'm rendering a 3D space that looks like half a sphere using a 3D Scatter plot in Plotly. I want to change the colour of each point according to the values in a Pandas DataFrame.
The rows of my DataFrame look like this:
{
"x": 1,
"y": -31,
"z": -63,
"M": -1,
"E": -1,
"F": -1,
"CE": -1,
"MA": -1,
"R": -1
}
This way, in order to build my diagram I use:
fig = px.scatter_3d(json_df, x="x", y="y", z="z", custom_data=["M", "E", "F", "CE", "MA", "R"])
fig.update_traces(
hovertemplate="<br>".join([
"M: %{customdata[0]}",
"E: %{customdata[1]}",
"F: %{customdata[2]}",
"CE: %{customdata[3]}",
"MA: %{customdata[4]}",
"R: %{customdata[5]}"
])
)
fig.show()
Which produces this image (if you also hover of each point, you get the info especified in hovertemplate
):
Now I want to change the colour of each point according to those values M, E, F, etc., having a "default colour" for non-valid points (those where M, E, F, etc. is -1
). Right now, I want to use a colour for non-valid points and a random-colour for valid ones.
grey_colour = 'rgb(84, 84, 84)'
def row_to_color(df_row):
if df_row["M"] == -1:
# non-valid colour
return grey_colour
else:
#randomly selected colour
return 'rgb(' + str(0) + ', ' + str(np.random.randint(0,255)) + ', ' + str(np.random.randint(0,255)) + ')'
# Almost 2000-elements-long array, with a large part of it being that grey default color.
custom_color = json_df.apply(row_to_color, axis=1)
fig = px.scatter_3d(json_df, x="x", y="y", z="z", custom_data=["M", "E", "F", "CE", "MA", "R"], color=custom_color)
However, Plotly ignores that non-valid colour (no matter what it is) and goes back to the default one, only respecting the colours that are different from the default one.
I've done some testing and if I use another condition (so there are far less "non-valid colour" items in the custom_color array), the plot seems to render correctly, but the colour is still incorrect. For instance:
def row_to_color(df_row):
if df_row["x"] > 10: #less strict condition
return 'rgb(84, 84, 84)' #grey
else:
return 'rgb(' + str(0) + ', ' + str(np.random.randint(0,255)) + ', ' + str(np.random.randint(0,255)) + ')'
...
fig = px.scatter_3d(...)
Any idea of what is going on?
According to the main maintainer of Plotly, Nicolas Kruchten, (and the official documentation of Plotly, that unfortunately I had misread), if you want to use a specific colour for every item that is going to be plotted in the final image, you just need to use the color_discrete_map="identity"
argument when calling your plot function.
If you don't use it, Plotly tries to use the colours specified in the color
array as colors to be assigned to categories of data. If you don't have categories, Plotly just goes nuts during the plotting.
By adding that parameter to the call, we tell Plotly to treat every item in the color
array separately and set it as the colour for the corresponding item in the data provided.
fig = px.scatter_3d(json_df, x="x", y="y", z="z",
custom_data=["M", "E", "F", "CE", "MA", "R"],
color=custom_color,
color_discrete_map="identity")