How to color points by a "string" column in a dataframe using graph_objects.Scatter3D?

I have a Pandas DataFrame with data of many points:

I cast the column “ntr” as “string” so that I can use it as a “categorical” variable to color the points:

I can use plotly.express to create the 3d scatter plot, with points colored by their “ntr” string attribute:

# plot using plotly.express
fig = px.scatter_3d(
    pts_df, 
    x = 'x', 
    y = 'y',
    z = 'z',
    color = 'ntr',
    color_continuous_scale='Plasma',
    template="plotly_dark", 
)

fig.update_layout(
    height=1300, 
    scene=dict(aspectmode='data'), 
    legend= {'itemsizing': 'constant'},
)

fig.update_traces(marker_size=2)

fig.update_scenes(
    xaxis_autorange="reversed", 
    yaxis_autorange="reversed", 
)

fig.update_scenes(camera_projection_type='orthographic')

fig.show()
# fig.write_html('./fig.html')

However, I’m unable to do the same using the graph_objects:

def plot3Dpts(_pts: pd.DataFrame):
    x = _pts['x']
    y = _pts['y']
    z = _pts['z']


    fig = go.Figure(
        data = [go.Scatter3d(
            x=x,
            y=y,
            z=z, 
            mode='markers', 
            marker=dict(
                color = _pts['ntr'], 
                # color=[f"rgb{tuple(pt_rgb)}" 
                #        for pt_rgb in _pts[['r', 'g', 'b']].values
                # ],
                # color=[f'rgb({np.random.randint(0,256)}, \
                #             {np.random.randint(0,256)}, \
                #             {np.random.randint(0,256)})' 
                #         for i in range(len(_pts))
                # ],
                size=2, 
                opacity=1.0, 
            ) 
        )], 
    )

    fig.update_layout(
        height=1300, 
        scene=dict(aspectmode='data'), 
        legend= {'itemsizing': 'constant'},
        template="plotly_dark", 
    )

    fig.update_scenes(
        xaxis_autorange="reversed", 
        yaxis_autorange="reversed", 
    )

    fig.update_scenes(camera_projection_type='orthographic')

    return fig

fig = plot3Dpts(pts_df) 
fig.write_html('./fig.html')

I got the following error message:

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

    The 'color' property is a color and may be specified as:
      - A hex string (e.g. '#ff0000')
      - An rgb/rgba string (e.g. 'rgb(255,0,0)')
      - An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
      - An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
      - A named CSS color:
            aliceblue, antiquewhite, aqua, aquamarine, azure,... 
      - A number that will be interpreted as a color
        according to scatter3d.marker.colorscale
      - A list or array of any of the above

May I ask how to adjust the code above so that we can color points by a categorial variable using graph_objects?

Thanks.

Under the hood what Plotly express does (with a categorical color column) is create a trace for each category. If you want to use graph_objects, you’ll need to do the same, looping through your categorical values and adding traces to the figure.

However now what is your reason for not using Plotly express? If your issue is that you want more customisation, express produces a figure object that can be further customised with the same syntax as graph_objects. Therefore I would recommend bootstrapping the figure with express then adding things on top/updating the figure as required.

1 Like