How to add difference in percentage above bars in plotly

Viewed 2595

Basically, I have the following code from the plotly tutorial in python:

import plotly.graph_objects as go
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')
fig.show()

This code produces a plain bar plot, separated by type (animals) and colors ("SF Zoo", "LA Zoo"). I want to add a percentage above the red bars to represent the percentual difference between the red bar value and the corresponding blue/purple one, like in the following figure:

Desired Image

How should I write the corresponding code for this?

Edit: I want to add the percentages only for the red bars and not on the red bars, but above them, like in the image

2 Answers

In order to accomplish this, you should have the lists for the y values along with a list where the percentages will go outside of the go.bar like this:

y1 = [20, 14, 23]
y2 = [12, 18, 29]
c = []

In order to calculate the percentages (rounded down, which you can change to rounded up by replacing math.floor() with math.ceil()), you can use this:

i = 0

while i < len(y1):
    i = i
    if y1[i] > y2[i]:
        q = y1[i]-y2[i]
        c.append( '-' + str(math.floor(q/y1[i]*100)) + '%')
        i=i+1
    if y1[i] < y2[i]:
        t = y2[i]-y1[i]
        c.append('+' + str(math.floor(t/y1[i]*100)) + '%')
        i=i+1

This just finds the difference between the y-values and then divides is by the original value which you are trying to find the percentage from, multiplying it by 100, to make it a percentage.

From there you should set your y-values to be equal to the lists you set previously, along with setting the text to be equal to the list of percentages, c. Another thing you must do is set the textposition to outside, because the default is none.

fig = go.Figure(data=[
    go.Bar(name='SF Zoo', x=animals, y=y1),
    go.Bar(name='LA Zoo', x=animals, y=y2, text=c, textposition='outside')
])

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

The full code looks like this:

import plotly.graph_objects as go
import math

y1 = [20, 14, 23]
y2 = [12, 18, 29]

animals=['giraffes', 'orangutans', 'monkeys']

c = []

i = 0

while i < len(y1):
    i = i
    if y1[i] > y2[i]:
        q = y1[i]-y2[i]
        c.append( '-' + str(math.floor(q/y1[i]*100)) + '%')
        i=i+1
    if y1[i] < y2[i]:
        t = y2[i]-y1[i]
        c.append('+' + str(math.floor(t/y1[i]*100)) + '%')
        i=i+1
print(c)

fig = go.Figure(data=[
    go.Bar(name='SF Zoo', x=animals, y=y1),
    go.Bar(name='LA Zoo', x=animals, y=y2, text=c, textposition='outside')
])

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

The outputted plot looks like this:

enter image description here

Since you are calculating a percent change between the SF and LA values, it makes sense to store the SF and LA values in separate lists, then calculate the percent change between each of these values and store them in another list.

Then you can pass this list to the text parameter of the second go.Bar object to ensure that the annotation only shows up above the second bar for each set of grouped bars, as well as set the text size and color. Another list comprehension with a conditional can be used add a + symbol to any positive percent changes.

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

SF_zoo_counts = [20, 14, 23]
LA_zoo_counts = [12, 18, 29]

## calculate percentage change of an animal from SF to LA
LA_SF_percent_change = [100*(LA_count - SF_count) / SF_count for SF_count, LA_count in zip(SF_zoo_counts, LA_zoo_counts)]

## pass the text to the second graph_object only
fig = go.Figure(data=[
    go.Bar(name='SF Zoo', x=animals, y=SF_zoo_counts),
    go.Bar(name='LA Zoo', x=animals, y=LA_zoo_counts, 
        text=[f"+{percent_change:.0f}%" if percent_change > 0 else f"{percent_change:.0f}%" 
            for percent_change in LA_SF_percent_change ],
        textposition='outside',
        textfont_size=18,
        textfont_color='red'
        )
])
# Change the bar mode
fig.update_layout(barmode='group')
fig.show()

enter image description here

Related