Search code examples
pythoncolorsplotlyplotly-pythonscatter3d

Plotly: How to define marker color based on category string value for a 3d scatter plot?


I am using plotly.graph_object for 3D scatter plot. I'd like to define marker color based on category string value. The category values are A2, A3, A4. How to modify below code? Thanks

Here is what I did:


import plotly.graph_objects as go

x=df_merged_pc['PC1']
y=df_merged_pc['PC2']
z=df_merged_pc['PC3']

color=df_merged_pc['AREA']

fig=go.Figure(data=[go.Scatter3d(x=x,y=y,z=z,mode='markers',
                                 marker=dict(size=12,
                                             color=df_merged_pc['AREA'],
                                             colorscale ='Viridis'))])
fig.show()

The error I got is:

ValueError: 
    Invalid element(s) received for the 'color' property of scatter3d.marker
        Invalid elements include: ['A3', 'A3', 'A3', 'A3', 'A3', 'A3', 'A3', 'A2', 'A2', 'A2']

Solution

  • I might be wrong here, but it sounds to me like you're actually asking for a widely used built-in feature of plotly.express where you can assign a color to subgroups of labeled data. Take the dataset px.data.iris as an example with:

    fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_width',
                  color='species')
    

    Here, the colors are assigned to different species of which you have three unique values ['setosa', 'versicolor', 'virginica']:

       sepal_length  sepal_width  petal_length  petal_width species  species_id
    0           5.1          3.5           1.4          0.2  setosa           1
    1           4.9          3.0           1.4          0.2  setosa           1
    2           4.7          3.2           1.3          0.2  setosa           1
    3           4.6          3.1           1.5          0.2  setosa           1
    4           5.0          3.6           1.4          0.2  setosa           1
    

    enter image description here

    This example can be expanded upon by changing the color scheme like above, in which case your color scheme can be defined by a dictionary:

    colors = {"flower": 'green', 'not a flower': 'rgba(50,50,50,0.6)'} 
    

    Or you can specify a discrete color sequence with:

    color_discrete_sequence = plotly.colors.sequential.Viridis
    

    You can also add a new column like random.choice(['flower', 'not a flower']) to change the category you would like your colors associated with.

    enter image description here

    Plotly.graph_objects

    If you would like to use go.Scatter3d instead I would build one trace per unique subgroup, and set up the colors using itertools.cycle like this:

    colors = cycle(plotly.colors.sequential.Viridis)
    
    fig = go.Figure()
    for s in dfi.species.unique():
        df = dfi[dfi.species == s]
        fig.add_trace(go.Scatter3d(x=df['sepal_length'], y = df['sepal_width'], z = df['petal_width'],
                                   mode = 'markers',
                                   name = s,
                                   marker_color = next(colors)))
    

    Complete code for plotly express

    import plotly.express as px
    import random
    df = px.data.iris()
    colors = {"flower": 'green', 'not a flower': 'rgba(50,50,50,0.6)'}
    
    df['plant'] = [random.choice(['flower', 'not a flower']) for obs in range(0, len(df))]
    fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_width',
                    color = 'plant',
                    color_discrete_map=colors
                       )
    fig.show()
    

    Complete code for plotly graph objects

    import plotly.graph_objects as go
    import plotly
    from itertools import cycle
    
    dfi = px.data.iris()
    colors = cycle(plotly.colors.sequential.Viridis)
    
    fig = go.Figure()
    for s in dfi.species.unique():
        df = dfi[dfi.species == s]
        fig.add_trace(go.Scatter3d(x=df['sepal_length'], y = df['sepal_width'], z = df['petal_width'],
                                   mode = 'markers',
                                   name = s,
                                   marker_color = next(colors)))
    fig.show()