Is there any way to put the x-ticks above each stacked bar in a stacked bar chart, rather than below the x-axis? To be clear, I don't mean place the x-tick above each individual bar in a single stacked bar, I mean place the x-tick above the stacked bar itself. Here's how I'm creating my plot:
df = pd.DataFrame(np.random.randint(1, 5, size=(3200, 3)))
df.loc[np.random.choice(df.index, size=3190, replace=False), :] = 0
df_select = df[df.sum(axis=1)>1]
fig, ax = plt.subplots()
ax.bar(df_select.index, df_select.iloc[:,0], label = df_select.columns[0], wdith = 15)
if df_select.shape[1] > 1:
for i in range(1, df_select.shape[1]):
bottom = df_select.iloc[:,np.arange(0,i,1)].sum(axis=1)
ax.bar(df_select.index, df_select.iloc[:,i], bottom=bottom, label =
df_select.columns[i], width = 15)
ax.set_xticks(df_select.index)
plt.legend(loc='best', bbox_to_anchor=(1, 0.5))
plt.xticks(rotation=90, fontsize=8) #this puts the x ticks below the x axis
Additionally, I'd like to place some text at specific points along the x axis. I'm storing these sites in a list:
sites = [19, 173, 1002] # the number and elements of this list vary
So, for example, at x = 173, I'd like to place the text 'site (173)' along with a tick at position 173.
For your reference, I've posted images for what my current code produces, and what I would like to produce: current: https://i.sstatic.net/QDlEP.png goal: https://i.sstatic.net/IJJo4.png
This can be achieved by using the example that @Evan linked to in this comments, namely this one. The important bit is the function named autolabel
in the linked example. Changing the values displayed from the y axis values to the x axis values is easy, replace height
with rect.get_x()
. The trickier bit is putting the values at the top of the bar. The total height of your bars can be found by summing the values in your DataFrame.
heights = df_select.iloc[:,:].sum(axis=1)
This then needs to be passed to the autolabel function and used as the height of the bars. The x tick labels can be removed using
ax.get_xaxis().set_ticklabels([])
And you can add further text below your x axis for specific sites by simply using ax.text
using the contents of sites
as the x location, and setting the y location to be below the axis (-0.5 or something).
Putting this all together, we get the following working example:
def autolabel(rects, heights):
"""
Attach a text label above each bar displaying its height
Modified for use with a stacked bar chart
"""
for i, rect in enumerate(rects):
x = rect.get_x()
wid = rect.get_width()
height = heights.values[i]
ax.text(x + wid/2., 1.05*height,
'%d' % (int(x) + int((wid/2)+0.5)),
ha='center', va='bottom', rotation=90)
df = pd.DataFrame(np.random.randint(1, 5, size=(3200, 3)))
df.loc[np.random.choice(df.index, size=3190, replace=False), :] = 0
df_select = df[df.sum(axis=1)>1]
fig, ax = plt.subplots()
ax.bar(df_select.index, df_select.iloc[:,0], label = df_select.columns[0], width = 15)
if df_select.shape[1] > 1:
for i in range(1, df_select.shape[1]):
bottom = df_select.iloc[:,np.arange(0,i,1)].sum(axis=1)
rects1 = ax.bar(df_select.index, df_select.iloc[:,i], bottom=bottom, label =
df_select.columns[i], width = 15)
ax.set_xticks(df_select.index)
ax.get_xaxis().set_ticklabels([]) # turn off the x tick labels
plt.legend(loc='best', bbox_to_anchor=(1, 0.5))
heights = df_select.iloc[:, :].sum(axis=1)
autolabel(rects1, heights)
# Select 3 random value to add labels below the x axis. Make sure they are
# within the axis limits
sites = np.random.choice(df_select.index, 3)
for site in sites:
ax.text(site, -0.5,
'site(%s)' % site,
ha='center', va='bottom',fontsize=6)
plt.show()
Which gives the following graph:
Note: this can sometimes look messy because the bars are very thin and spread out, and can be placed close to each other, meaning values may start to overlap.