Search code examples
pythonplotlyscatter3d

Background colour for text labels in Plotly?


I am displaying a section of a dim-reduced Word Embedding with Plotly in Python. I am using a 3D Scatter Plot, and everything is working fine so far, with this code (broken down):

fig = go.Figure()
trace = go.Scatter3d(
        x=df[0],
        y=df[1],
        z=df[2],
        text=df.index,
        mode="markers+text",
        textfont=dict(
            color=["crimson"]),
        marker=dict(
            size=3),
        hoverinfo="skip")

fig.add_trace(trace)

fig.show()

The only problem I am having is that some of the labels of the datapoints overlap, which results in them being unreadable at first glance.

Is there a possibility to specify the background colour of the text labels?

Edit:

The first 5 lines of my df look like this, and all the data is structured this way. The index is the word, and the three columns are its position in 3D-space.

                        0         1         2
chancellor       1.102989  0.416767  2.071260
abdicate         0.028073  1.156498  1.911484
overthrow        2.435294 -0.305266  0.998094
candidate        0.259697  0.648845  0.448700
elections        0.122355  0.815206  1.107913

However, my problem isn't really about the data, but about the possibilities one has to customize the visualization.

Edit2:

This is what my plot looks like.

enter image description here

As you can see with the cluster on the lower left, most of the labels overlap each other. I am now hoping that there is a hidden parameter I could use to make these labels better visible, and I instinctively looked for a way to add a white background to all of these labels, so that this would block the labels of the data points behind them.


Solution

  • I found a workaround, by not using the text-parameter in the trace definition, but by adding annotations for every datapoint. The code now looks like this:

    trace = go.Scatter3d(
                x=df[0],
                y=df[1],
                z=df[2],
                mode="markers",
                marker=dict(size=3),
                hoverinfo="skip")
        
    annos = []
    first = True
    for index, row in df.iterrows():
        if first:
            col = "black"
            first = False
        else:
            col = "black"
                
        anno = dict(x=row[0],
                    y=row[1],
                    z=row[2],
                    text=index,
                    showarrow=True,
                    arrowhead=0,
                    font=dict(color=col),
                    ax=0,
                    ay=-20,
                    bgcolor="white",
                    opacity=0.85)
    
        annos.append(anno)
            
    fig = go.Figure()
    fig.add_trace(trace)
    fig.update_layout(scene=dict(annotations=annos)
    

    The labels, implemented as annotations, now have a white background, that makes them able to be read even if there are more datapoints behind them. Sadly, the layering of these labels doesn't follow the perspective of the camera, but only the position on the y-axis. Therefore, the annotation with the lowest y-value is always behind all other annotations, even if the camera is located on the negative end of the y-axis.

    enter image description here