Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to align the bars in a bar chart between ticks (matplotlib)?

I need to align the bars in my plots to be centered between the x axis tick lines in matplotlib. I have tried using the option align='edge' which will move the bar towards the edge of the tick, but wont align it between ticks, I'm also not willing to widen the bars so that there is no white space between bars.

I have data with the form:

import matplotlib.pyplot as plt    
import numpy as np    
data = np.asarray([['7 Jan.',  60000],
                   ['14 Jan.', 37000],
                   ['21 Jan.', 32000]])

And I want to make a bar plot with it:

x = data[:, 0]
y = data[:, 1].astype(np.int)
plt.bar(x, y, width=0.7, color='#A90000')
plt.show()

Which yields:

enter image description here

Also, there's this tutorial which seems to approach the problem, but I haven't been able to my code. Any help is appreciated.

like image 672
corcholatacolormarengo Avatar asked Mar 04 '23 12:03

corcholatacolormarengo


1 Answers

You're plotting categorical data. That is some strings like ["Apple", "Banana", "Cherry"]. Now if you want to have bars in between categories, it's not really clear what unit that would be. What's half way between "Apple" and "Banana"? Maybe "Aubergine"? But one cannot know.

So it's probably best to give up the categorical plot. Now you can of course map categories to numbers. So first category corresponds to 0, second to 1 etc. Then it's easy to place the bars half way in between, at 0.5, 1.5, ....

import matplotlib.pyplot as plt    
import numpy as np    
data = np.asarray([['7 Jan.',  60000],
                   ['14 Jan.', 37000],
                   ['21 Jan.', 32000]])

cats = data[:, 0]
x = np.arange(len(cats)) + 0.5
y = data[:, 1].astype(np.int)

plt.bar(x, y, width=0.7, color='#A90000')
plt.xticks(x-0.5, cats)
plt.show()

enter image description here

Somehow the plot looks like it's missing the last label though, which is simply not present in your list. You could of course add it manually, if you want.

An alternative is to work with actual dates. Since your categories actually correspond to dates that should be possible.

from datetime import datetime
import matplotlib.pyplot as plt    
import numpy as np    
data = np.asarray([['7 Jan.',  60000],
                   ['14 Jan.', 37000],
                   ['21 Jan.', 32000]])

# convert categories to dates
xt = [datetime.strptime(d, "%d %b.") for d in data[:, 0]]
# assume all dates are equally spaced
dt = (xt[1] - xt[0])/2
# get bar positions by adding half the interval to each date
x = [tp + dt for tp in xt]

y = data[:, 1].astype(np.int)

plt.bar(x, y, width=(0.7*2*dt).days, color='#A90000')
plt.xticks(xt, data[:, 0])
plt.show()

The resulting plot is visually exactly the same as above, however, the xaxis is now in units of datetime. This allows to use tickers and formatters instead of manually placing the ticks and labels.

import numpy as np
from datetime import datetime
import matplotlib.pyplot as plt  
import matplotlib.dates as mdates


data = np.asarray([['7 Jan.',  60000],
                   ['14 Jan.', 37000],
                   ['21 Jan.', 32000]])

# convert categories to dates
xt = [datetime.strptime(d, "%d %b.") for d in data[:, 0]]
# assume all dates are equally spaced
dt = (xt[1] - xt[0])/2
# get bar positions by adding half the interval to each date
x = [tp + dt for tp in xt]

y = data[:, 1].astype(np.int)

plt.bar(x, y, width=(0.7*2*dt).days, color='#A90000')
plt.gca().xaxis.set_major_locator(mdates.WeekdayLocator(mdates.SU, interval=1))
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%d %b %Y"))
plt.show()

enter image description here

Now the labels correspond to actual dates which can be formatted in a desired way. You'll notice that the dates here are from 1900 such that we needed to show the labels on every sunday (As the 7th of january 1900 was a sunday). You may want to add the real year to your data, such that this plot becomes correct.

like image 141
ImportanceOfBeingErnest Avatar answered Mar 08 '23 12:03

ImportanceOfBeingErnest