1

How to center the bar plot to show the difference of a certain column?

I have the following bar plot, done with matplotlib : Bar plot

Note how the barplot is really bad. The difference between each bar cant really be seen properly. So what I want, is to use the red bar as the origin in the y-axis. That way, the other bars would show the difference (blue_bar(i) - redbar).

In other words, I want the value of the red bar in the y-axis to be the y-origin of the plot.

Again, in another words, the red bar is the accuracy obtained by my academic work. I want to plot the other article results compared/ IN RELATION to mine.

I made the following picture using paint.net to illustrate what I want. Any other ideas/suggestions are really appreciated.

enter image description here

Appendix :

I used the following code to produce the first graphic :

import numpy as np
import random
from matplotlib import pyplot as plt

accuracies = [0.9630, 0.9597, 0.9563, 0.9533, 0.9527, 0.9480, 0.9477, 0.9472, 0.9472, 0.9466, 0.9452, 0.9452, 0.9442,
              0.9440, 0.9434, 0.9420, 0.9407, 0.9407, 0.9391, 0.9377, 0.9185, 0.9268]

sensitividades = [0.7680, 0.7200, 0.8173, 0.7569, 0.7406, 0.7354, 0.7746, 0.7344, 0.7067, 0.7410, 0.7370, 0.7321,
                  0.7357]

especificidades = [0.9827, 0.9733, 0.9816, 0.9807, 0.9789, 0.9724, 0.9764, 0.9801, 0.9751, 0.9521, 0.9487, 0.9694]

accuracies = [x * 100 for x in accuracies]

y = accuracies

N = len(y)
x = range(N)
width = 1 / 1.1
fig, ax = plt.subplots(1, 1)
ax.grid(zorder=0)

# Plot other articles
ax.bar(x, y, width, color="blue", zorder=3)

# Plot my work
ax.bar(x[len(x) - 1] + 1, 95.30, width, color="red", zorder=3)

plt.title('Accuracy of each article')
plt.xlabel('Article')
plt.ylabel('Accuracy')

plt.savefig('foo.png')
plt.show()
KenobiBastila
  • 539
  • 4
  • 16
  • 52

2 Answers2

4

You could either set the y limits closer to the interesting values:

import numpy as np
import random
from matplotlib import pyplot as plt

accuracies = [0.9630, 0.9597, 0.9563, 0.9533, 0.9527, 0.9480, 0.9477, 0.9472, 0.9472, 0.9466, 0.9452, 0.9452, 0.9442,
              0.9440, 0.9434, 0.9420, 0.9407, 0.9407, 0.9391, 0.9377, 0.9185, 0.9268]

sensitividades = [0.7680, 0.7200, 0.8173, 0.7569, 0.7406, 0.7354, 0.7746, 0.7344, 0.7067, 0.7410, 0.7370, 0.7321,
                  0.7357]

especificidades = [0.9827, 0.9733, 0.9816, 0.9807, 0.9789, 0.9724, 0.9764, 0.9801, 0.9751, 0.9521, 0.9487, 0.9694]

accuracies = [x * 100 for x in accuracies]

my_acc = 95.30
y = accuracies

N = len(y)
x = range(N)
width = 1 / 1.1
fig, ax = plt.subplots(1, 1)
ax.grid(zorder=0)

# Plot other articles
ax.bar(x, y, width, color="blue", zorder=3)

# Plot my work
ax.bar(x[len(x) - 1] + 1, my_acc, width, color="red", zorder=3)

plt.title('Accuracy of each article')
plt.ylim(min(y) - 0.5, max(y) +0.5)
plt.xlabel('Article')
plt.ylabel('Accuracy')

plt.savefig('foo2.png')
plt.show()

reduced y range

Or you could plot it around zero, with your result being the new origin (but you will have to indicate by how much you shifted the origin somewhere in the legend or somewhere else):

import numpy as np
import random
from matplotlib import pyplot as plt

accuracies = [0.9630, 0.9597, 0.9563, 0.9533, 0.9527, 0.9480, 0.9477, 0.9472, 0.9472, 0.9466, 0.9452, 0.9452, 0.9442,
              0.9440, 0.9434, 0.9420, 0.9407, 0.9407, 0.9391, 0.9377, 0.9185, 0.9268]

sensitividades = [0.7680, 0.7200, 0.8173, 0.7569, 0.7406, 0.7354, 0.7746, 0.7344, 0.7067, 0.7410, 0.7370, 0.7321,
                  0.7357]

especificidades = [0.9827, 0.9733, 0.9816, 0.9807, 0.9789, 0.9724, 0.9764, 0.9801, 0.9751, 0.9521, 0.9487, 0.9694]

accuracies = [x * 100 for x in accuracies]

my_acc = 95.30
y = np.asarray(accuracies) - my_acc

N = len(y)
x = range(N)
width = 1 / 1.1
fig, ax = plt.subplots(1, 1)
ax.grid(zorder=0)

# Plot other articles
bars = ax.bar(x, y, width, color="blue", zorder=3)

# Plot my work
# ax.bar(x[len(x) - 1] + 1, my_acc, width, color="red", zorder=3)

plt.title('Accuracy of each article')
plt.yticks([0, -0.3, -1.3, -2.3, -3.3, 0.7, 1.7], [95.30, 95, 94, 93, 92, 96, 97])
plt.xlabel('Article')
plt.ylabel('Accuracy')
plt.ylim(min(y) - 0.5, max(y) + 0.7)


def autolabel(rects):
    for i in range(len(rects)):
        rect = rects[i]
        height = rect.get_height()
        if (height >= 0):
            ax.text(rect.get_x() + rect.get_width()/2.,
            0.3 + height,'[{}]'.format( i), ha='center', va='bottom', 
            fontsize=7.5)
        if (height < 0):
            ax.text(rect.get_x() + rect.get_width()/2.,
            height - 0.3,'[{}]'.format( i), ha='center', va='bottom', 
            fontsize=7.5)

autolabel(bars)
plt.savefig('foo.png')
plt.show()

new y origin

Of course, your own result would not appear in the second plot, since it would have height zero.

ml4294
  • 2,559
  • 5
  • 24
  • 24
  • Awesome, is there anyway to fix the y-axis labels to properly indicate the accuracy diff ? Also, if possible, how do i add to each bar a label "Mr et al., Bla bla et al, ...." <- author name to each bar – KenobiBastila Aug 26 '17 at 22:37
  • I have modified the answer to show the correct y labels. Concerning your second question: do you want a the other authors to appear as text above each bar? In this case, there will probably quite a mess above the bars, as this will be a lot of text. You could write '[1]', '[2]' above the bars and indicate their origin in the text. I have also included this in the updated answer. I used a modification of this: https://stackoverflow.com/questions/30228069/how-to-display-the-value-of-the-bar-on-each-bar-with-pyplot-barh – ml4294 Aug 27 '17 at 05:53
3

I actually think the way you have it represented now is actually best -- meaning there isn't a huge difference in accuracy on a cursory level.

However, if you want to set the value of the red bar as the origin, try this:

...
plt.title('Accuracy of each article')
plt.xlabel('Article')
plt.ylabel('Accuracy')

plt.ylim(95.30) # Sets the value of the red bar as the origin.

plt.savefig('foo.png')
plt.show()

enter image description here

Perhaps setting the minimum value of lowest accuracy of the article might make this graph more digestible.

...
plt.title('Accuracy of each article')
plt.xlabel('Article')
plt.ylabel('Accuracy')

plt.ylim(min(accuracies), 100) # Sets the value of minimum accuracy as the origin and the max value as 100.

plt.savefig('foo.png')
plt.show()

enter image description here

DoDoSmarts
  • 306
  • 1
  • 6