Reputation: 68366
I have stumbled accross the following snippet, for creating horizontal bar chart using matplotlib:
import matplotlib
from pylab import *
val = 3+10*rand(5) # the bar lengths
pos = arange(5)+.5 # the bar centers on the y axis
print pos
figure(1)
barh(pos,val, align='center')
yticks(pos, ('Tom', 'Dick', 'Harry', 'Slim', 'Jim'))
xlabel('Performance')
title('horizontal bar chart using matplotlib')
grid(True)
show()
I want to modify the above script as follows:
any help (code snippet or links) to help me make the above modifications would be very helpful.
as an aside, if I wanted to make stacked horizontal bars (say each label had 3 stacked horizontal bars), how would I modify the code above to do plot a 3 stacked horizontal bar plot?
[[Edit]]
Could someone post two short code snippet that shows how to:
Print labels on the opposite side of the horizontal bars (so that for example, the label for 'negative' bars appears in the 1st quarant, and the labels for 'positive' bars appears in the 2nd quadrant
Plot multiple (say 2 or 3) horizontal bars (instead of just one). Good examples are the first two images shown here
Upvotes: 18
Views: 40013
Reputation: 16076
The following codes snippet is an example of using the text function to annotate text label on the left hand side for negative values and the right hand side for positive values as mentioned by both gcalmettes and Zhenya.
from pylab import setp
import numpy as np
import matplotlib.pyplot as plt
import math
# creation of the data
name_list = ['day1', 'day2', 'day3', 'day4']
data = {name: 3+10*np.random.rand(5) for name in name_list}
for name in name_list:
data[name][0] = data[name][0]*-1
data[name][2] = data[name][2]*-1
colors_list = ['0.5', 'r', 'b', 'g'] #optional
def customize_barh(data, width_bar=1, width_space=0.5, colors=None):
n_measure = len(data) #number of measure per people
n_people = data[data.keys()[0]].size # number of people
#some calculation to determine the position of Y ticks labels
total_space = n_people*(n_measure*width_bar)+(n_people-1)*width_space
ind_space = n_measure*width_bar
step = ind_space/2.
pos = np.arange(step, total_space+width_space, ind_space+width_space)
# create the figure and the axes to plot the data
fig = plt.figure(figsize=(8,6))
ax = fig.add_axes([0.15, 0.15, 0.65, 0.7])
# remove top and right spines and turn ticks off if no spine
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('default') # ticks position on the right
# postition of tick out
ax.tick_params(axis='both', direction='out', width=3, length=6,
labelsize=24, pad=8)
ax.spines['left'].set_linewidth(3)
ax.spines['bottom'].set_linewidth(3)
# plot the data
for i,day in enumerate(data.keys()):
if colors == None:
ax.barh(pos-step+i*width_bar, data[day], width_bar, #facecolor='0.4',
edgecolor='k', linewidth=3)
else:
ax.barh(pos-step+i*width_bar, data[day], width_bar, facecolor=colors[i],
edgecolor='k', linewidth=3)
ax.set_yticks(pos)
# you may want to use the list of name as argument of the function to be more
# flexible (if you have to add a people)
setp(ax.get_yticklabels(), visible=False)
ax.set_ylim((-width_space, total_space+width_space))
ax.set_xlabel('Performance', size=26, labelpad=10)
labels_list = ['Tom', 'Dick', 'Harry', 'Slim','Jim']
# creation of an array of positive/negative values (based on the values
# of the data) that will be used as x values for adding text as side labels
side_list = []
for index in range(len(labels_list)):
sum = 0
for name in name_list:
sum+= data[name][index]
if math.copysign(1,sum) > 0:
side_list.append(16)
else:
side_list.append(-21)
for label in labels_list:
plt.text(side_list[labels_list.index(label)], pos[labels_list.index(label)]-0.5, label,fontsize=26)
customize_barh(data, colors=colors_list)
plt.savefig('perf.png')
plt.show()
It works on the basis that the all the bars for a given person need to be negative or positive for the text to be annotated on the correct side. To change this behaviour just change the generation of side_list.
E.g If you want a certain bar threshold to determine the position of the label, then count the data values over that threshold instead of summing the values for a given name.
E.g For threshold of 3 bars out of however many, the for loop becomes
for index in range(len(labels_list)):
count = 0
for name in name_list:
if data[name][index] > 0:
count+= 1
if count > 3:
side_list.append(16)
else:
side_list.append(-21)
The generation of side_list will also need to be changed to adjust to the range of your data, as the examples given use random data in a specified range.
E.g You will need to adjust the label offsets of side_list.append(16)
and side_list.append(-21)
to suit your data.
Upvotes: 1
Reputation: 8704
As said by Zhenya, you will have to tweek your plot.
As an example, below is a function which produces a customize horizontal bar plot:
By default, it will plot the name of the categories (people) to the right, but you can of course change that.
import numpy as np
import matplotlib.pyplot as plt
# creation of the data
name_list = ['day1', 'day2', 'day3', 'day4']
data = {name: 3+10*np.random.rand(5) for name in name_list}
colors_list = ['0.5', 'r', 'b', 'g'] #optional
def customize_barh(data, width_bar=1, width_space=0.5, colors=None):
n_measure = len(data) #number of measure per people
n_people = data[data.keys()[0]].size # number of people
#some calculation to determine the position of Y ticks labels
total_space = n_people*(n_measure*width_bar)+(n_people-1)*width_space
ind_space = n_measure*width_bar
step = ind_space/2.
pos = np.arange(step, total_space+width_space, ind_space+width_space)
# create the figure and the axes to plot the data
fig = plt.figure(figsize=(8,6))
ax = fig.add_axes([0.15, 0.15, 0.65, 0.7])
# remove top and right spines and turn ticks off if no spine
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('right') # ticks position on the right
# postition of tick out
ax.tick_params(axis='both', direction='out', width=3, length=6,
labelsize=24, pad=8)
ax.spines['left'].set_linewidth(3)
ax.spines['bottom'].set_linewidth(3)
# plot the data
for i,day in enumerate(data.keys()):
if colors == None:
ax.barh(pos-step+i*width_bar, data[day], width_bar, #facecolor='0.4',
edgecolor='k', linewidth=3)
else:
ax.barh(pos-step+i*width_bar, data[day], width_bar, facecolor=colors[i],
edgecolor='k', linewidth=3)
ax.set_yticks(pos)
# you may want to use the list of name as argument of the function to be more
# flexible (if you have to add a people)
ax.set_yticklabels(('Tom', 'Dick', 'Harry', 'Slim', 'Jim'))
ax.set_ylim((-width_space, total_space+width_space))
ax.set_xlabel('Performance', size=26, labelpad=10)
customize_barh(data, colors=colors_list)
plt.savefig('perf.png')
plt.show()
which produces:
Upvotes: 11
Reputation: 26030
import matplotlib
from pylab import *
val = 3-6*rand(5) # the bar lengths # changed your data slightly
pos = arange(5)+.5 # the bar centers on the y axis
print pos
figure(1)
barh(pos,val, align='center',height=0.1) # notice the 'height' argument
yticks(pos, ('Tom', 'Dick', 'Harry', 'Slim', 'Jim'))
gca().axvline(0,color='k',lw=3) # poor man's zero level
xlabel('Performance')
title('horizontal bar chart using matplotlib')
grid(True)
show()
In general, I'd suggest not using from pyplot import *
. Unless you're in the interactive mode, use the object-oriented approach:
import matplotlib.pyplot as plt
from numpy.random import rand
from numpy import arange
val = 3-6*rand(5) # the bar lengths
pos = arange(5)+.5 # the bar centers on the y axis
print pos
fig = plt.figure()
ax = fig.add_subplot(111)
ax.barh(pos,val, align='center',height=0.1)
ax.set_yticks(pos, ('Tom', 'Dick', 'Harry', 'Slim', 'Jim'))
ax.axvline(0,color='k',lw=3) # poor man's zero level
ax.set_xlabel('Performance')
ax.set_title('horizontal bar chart using matplotlib')
ax.grid(True)
plt.show()
A good starting point for various sorts of plots is the matplotlib
gallery
Upvotes: 25