How to prevent text overlapping between traces?

Hey all! I’m working with a bar chart and a scatter. I must show the values as text instead of using hover text.I didn’t find an answer reading the documentation. Maybe you could help me!

It would be great if I could find a way to set the bars’ text at the bottom of the bars.

See my code and the plot below, thanks a lot!

from plotly import graph_objs as go
from plotly.subplots import make_subplots

meses = ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']  
rlzd = [335101.3, 693658.5, 335101.3, 800019, 514379, 594379, 581279, 310029, 485022, 692091, 227918]
meta = [660756.5, 658980.9, 527478.1, 800019, 603574.5, 584198.7, 590098, 571476.2, 616965.7, 676205.1, 617237.94, 613634.7]

fig = make_subplots(rows=1, cols=2, column_widths=[0.85, 0.15], print_grid=True, horizontal_spacing=0.05)
fig.add_trace(go.Bar(
                x=meses,y = rlzd,
                text=rlzd,
                textfont=dict(size=15,color='white'),
                textposition='auto',
                name = 'Realizado',
                showlegend=True),
                row=1, col=1)
fig.add_trace(go.Scatter(
                x=meses,y=meta,
                name='Meta',
                mode='markers+lines+text',
                text=meta,
                textfont=dict(size=13,color='black'),
                textposition="top right",
                marker_size=8,
                showlegend=True,
                line = go.scatter.Line(color="DarkBlue", dash='dot')),
                row=1, col=1)
fig.show()

Hi @rber, welcome to the forum! Unfortunately the text attribute of bar plots cannot be positioned at the bottom of the bars. The best thing to do is probably to use annotations positioned at the bottom of the bars, https://plotly.com/python/text-and-annotations/#simple-annotation. Another solution, which is very hacky, is to create a second bar trace which is transparent and has lower values, either a constant value or here I took 1/4 th of the value

from plotly import graph_objs as go
from plotly.subplots import make_subplots
import numpy as np

meses = ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']  
rlzd = [335101.3, 693658.5, 335101.3, 800019, 514379, 594379, 581279, 310029, 485022, 692091, 227918]
meta = [660756.5, 658980.9, 527478.1, 800019, 603574.5, 584198.7, 590098, 571476.2, 616965.7, 676205.1, 617237.94, 613634.7]

fig = make_subplots(rows=1, cols=1, print_grid=True, horizontal_spacing=0.05)
fig.add_trace(go.Bar(
                x=meses,y = rlzd,
                text='',
                textfont=dict(size=15,color='white'),
                textposition='auto',
                name = 'Realizado',
                marker_opacity=1
                ),
                row=1, col=1)
fig.add_trace(go.Bar(
                x=meses,y = np.array(rlzd)/4.,
                text=rlzd,
                textfont=dict(size=15,color='white'),
                textposition='auto',
                name = 'Realizado',
                marker_color='rgba(0, 0, 0, 0)',
                marker_line_color='rgba(0, 0, 0, 0)',
                opacity=1,
                hoverinfo='skip',
                ),
                row=1, col=1)
fig.add_trace(go.Scatter(
                x=meses,y=meta,
                name='Meta',
                mode='markers+lines+text',
                text=meta,
                textfont=dict(size=13,color='black'),
                textposition="top right",
                marker_size=8,
                line = go.scatter.Line(color="DarkBlue", dash='dot')),
                row=1, col=1)
fig.update_layout(showlegend=False, barmode='overlay')
fig.show()

1 Like

Thank you Emanuelle. Indeed, very hacky! Your solution does the job.

One solution is to alternate the text position like in this live demo:

    import pandas as pd
    from plotly import express as px, graph_objects as go
    
    df = pd.DataFrame()
    df['x'] = [0, 1, 1, 2, 3, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12]
    df['y'] = [57, 55, 75, 23, 80, 66, 66, 23, 79, 79, 20, 71, 59, 74, 82, 77]
    df['explainer_name'] = ['tree_shap_approximation', 'saabas', 'tree_shap', 'baseline_random', 'archipelago',
                            'shapley_taylor_interaction', 'partition', 'anova', 'permutation_partition', 'permutation',
                            'shap_interaction', 'sage', 'maple', 'lime', 'kernel_shap', 'exact_shapley_values']
    
    fig = px.scatter(df,
                         x='x',
                         y='y',
                         # size='dot_size',
                         text='explainer_name',
                         # log_x=True,
                         labels={
                             "x": "Time",
                             "y": "Score",
                             # 'dot_size': 'Portability',
                             'explainer_name': 'Explainer '
                         },
                         title='No overlapping annotations',  # take some vertical space
                         )
    def improve_text_position(x):
        """ it is more efficient if the x values are sorted """
    positions = ['top center', 'bottom center']  # you can add more: left center ...
    return [positions[i % len(positions)] for i in range(len(x))]
    
    fig.update_traces(textposition=improve_text_position(df['x']))
    fig.show()

Before:
enter image description here

After:
enter image description here

It might not work with a lot of points like in #925 but it should help many users :slight_smile:

3 Likes