Search code examples
plotlybar-chartpositioning

Shape positioning in categorical grouped bar chart


How can I put the shape in the categorical bar chart below in the middle of the bar specifying the number of giraffes in the SF Zoo (as shown in magenta in the attached picture)?

import plotly.graph_objects as go
import plotly.express as px
animals=['giraffes', 'orangutans', 'monkeys']

fig = go.Figure(data=[
    go.Bar(name='SF Zoo', x=animals, y=[20, 14, 23]),
    go.Bar(name='LA Zoo', x=animals, y=[12, 18, 29])
])

# Change the bar mode
fig.update_layout(barmode='group')

# Shape
fig.add_trace(go.Scatter(x=['giraffes', 'giraffes', 'orangutans', 'orangutans'],
                         y=[21, 25, 25, 16],
                         fill=None, mode="lines", line=dict(color='rgba(0,0,0,1)',width=2),
                         showlegend=False
                        )
             )

    
fig.show()

picture of output


Solution

  • As @Maximilian Peters pointed out, there isn't a good way of referencing a point an arbitrary amount between categorical coordinates.

    The best you can do is set the xref to paper meaning that the x-axis is scaled to be between 0 and 1. Then you have to guess and check where the x-coordinates should be to line up where you want them. Since your y-axis is numerical, you can reference the y-values you like directly. After some trial and error, drawing lines between (0.10, 20), (0,10, 25), (0.44, 25), (0.44, 14) gets you pretty close to your desired annotation.

    This is definitely hacky, and not generalizable if you add more bars, or change the width of the bars in any way.

    import plotly.graph_objects as go
    import plotly.express as px
    animals=['giraffes', 'orangutans', 'monkeys']
    
    fig = go.Figure(data=[
        go.Bar(name='SF Zoo', x=animals, y=[20, 14, 23]),
        go.Bar(name='LA Zoo', x=animals, y=[12, 18, 29])
    ])
    
    # Change the bar mode
    fig.update_layout(barmode='group')
    
    # Shape composed of three lines, looping through coordinates
    x_coords = [0.10, 0.10, 0.44, 0.44]
    y_coords = [20, 25, 25, 14]
    for i in range(1,len(x_coords)):
        fig.add_shape(
            type="line",
            xref="paper",
            x0=x_coords[i-1], 
            y0=y_coords[i-1], 
            x1=x_coords[i], 
            y1=y_coords[i],
            line=dict(color="magenta",width=3)
        )
        
    fig.show()
    

    enter image description here