Setting marker size gives a white outline in 3D Scatter

Hello!

I’m plotting a 3D Scatter plot (in Python), and I added a column to my dataframe named “marker_size” that I use to dynamically set my marker size for different types of points. The column contains integers only. For some reason switching from a static size of 3 to dynamic sizes makes the markers appear smaller and gives them a white outline. Including a very small portion of the plot so you can see what I mean.

02%20AM

Could someone please explain why that is the case and how I could change it to regular-looking markers?

This is how I create the plot:

'data':[go.Scatter3d(
        x=df['x'],
        y=df['y'],
        z=df['z'],
        text=df['i'],
        textposition="top center",
        mode='markers',
        marker=dict(size=df["marker_size"], symbol="circle", color=df["colour"]),
        name=str(df['i'])
        )
    ]

Thank you!
Anastasia

Hi @anastasia.m,

Welcome to plotly forum!! Could you be more precise, what do you mean by “switching from a static size of 3 to dynamic sizes”?

I’ve just experimented an animation that changes from frame to frame, the marker size and color, and it works fine. What is your working plotly version?

Here is my code based on your data:

import numpy as np
import pandas as pd
import plotly.graph_objects as go

colors = ['#531e7d', '#5e59b0', '#6d91bf', '#a7c0ca',  '#cfb19c', '#bf735d', '#9d3a4f',
          "rgb(255, 127, 14)",
          "rgb(44, 160, 44)",
          "rgb(214, 39, 40)"]

d = {'x': -2.3+np.random.rand(10),
     'y': 1-2*np.random.rand(10),
     'z': 1.5*np.random.rand(10),
     'i': ['a', 'b', 'ccc', 'dev', 'fyi', 'calc', 'comp', 'miao', 'yes', 'nono'],
     'marker_size': np.random.randint(15, 25, size=10),
     'colour': colors}
df = pd.DataFrame(d)

data =[go.Scatter3d(
        x=df['x'],
        y=df['y'],
        z=df['z'],
        text=df['i'],
        textposition="top center",
        mode='markers',
        marker=dict(size=df["marker_size"],  color=df["colour"]),
        name=str(df['i'])
        )
    ]

h = 0.25
xm, xM = df['x'].min()-h, df['x'].max()+h
ym, yM = df['y'].min()-h, df['y'].max()+h
zm, zM = df['z'].min()-h, df['z'].max()+h

frames = [ dict(data=[dict(type='scatter3d',
                           marker=dict(size = np.random.randint(15, 30, size=10).tolist(),
                                      color = np.random.permutation(colors)))],
           traces='0') for k in range(30)]

layout =go.Layout(width=600, height=600,
                  scene=dict(
                          xaxis =dict(range=[xm, xM]),
                          yaxis=dict(range=[ym, yM]),
                          zaxis=dict(range=[zm, zM]),
                  camera=dict(eye=dict(x=1.25, y=1.55, z= 1))),
                  
                 updatemenus=[dict(type='buttons', showactive=False,
                                y=0,
                                x=1.05,
                                xanchor='right',
                                yanchor='top',
                                pad=dict(t=0, r=10),
                                buttons=[dict(label='Play',
                                              method='animate',
                                              args=[None, 
                                                    dict(frame=dict(duration=50, 
                                                                    redraw=True),
                                                         transition=dict(duration=0),
                                                         fromcurrent=True,
                                                         mode='immediate')
                                                   ]
                                             )
                                        ]
                               )
                          ]     )

fig=go.Figure(data=data, layout=layout, frames=frames)
fig.show()

1 Like

Hi @empet,

Thank you for a prompt response!

Sorry for the lack of clarity. I meant that if I set my marker size statically (that is, each marker is of the same size) like this:

'data':[go.Scatter3d(
        x=df['x'],
        y=df['y'],
        z=df['z'],
        text=df['i'],
        textposition="top center",
        mode='markers',
        marker=dict(size=3, symbol="circle", color=df["colour"]),
        name=str(df['i'])
        )
    ]

The markers look like this (normal):
55%20PM

However, if I create a column in my dataframe called “marker_size”, fill it with integers (3 by default) and set it like this (and not change anything else):

'data':[go.Scatter3d(
        x=df['x'],
        y=df['y'],
        z=df['z'],
        text=df['i'],
        textposition="top center",
        mode='markers',
        marker=dict(size=df["marker_size"], symbol="circle", color=df["colour"]),
        name=str(df['i'])
        )
    ]

The markers look like this:
48%20AM

I’m using plotly version 4.0.0.

Thank you,
Anastasia

@anastasia.m

When two markers of distinct size are overlapping an encircling white line is plotted to distinguish them. It appears you have a dense set of markers, or the plotting window is too small or the camera.eye position is too close to the set of markers. Your posted images look like 2d scatter plots.

In my animation only two markers overlap, and they also have that white encircling line.

@empet

That makes sense, but why does it differ depending on how I set the marker size? It’s the same exact plot in the first image, except that I just use size 3 for all markers without an extra column in the dataframe, so it still has the same density and it’s viewed on the same screen.

Thank you!

@anastasia.m

When your markers have different size and color, they encode some data attributes. Hence if the size isn’t constant
the encircling white line around the overlapping markers helps users to compare the attributes encoded by size. It’s not the case when the markers have the same size, because the user isn’t interested in such a comparison.

Ah yeah, that’s not documented very well - if at all! We have different default behavior depending on whether marker.size is a constant or an array. If it’s an array, by default we decrease the opacity to 0.7 and add a 1px white outline. We added that logic a long time ago so you’d be able to see overlapping markers better in bubble charts, on the theory that bubble charts tend to have much larger markers - thus a much higher likelihood of overlap - than regular scatter charts. At this point it probably causes more confusion than it’s worth, but we don’t want to break backward compatibility.

You can explicitly set marker.opacity=1 and marker.line.width=0 to restore the constant-size behavior.

Thank you so much! It definitely is missing in the documentation, I thought I was doing some dark magic.