python-3.x3dplotlyscatter

Plotly ignores my custom colors in a 3d Scatter plot


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):

enter image description here

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 enter image description here

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.

enter image description here

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(...)

Light orange ????

enter image description here

Any idea of what is going on?


Solution

  • 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")
    

    enter image description here