Skip to content

Commit 9a7e3e0

Browse files
committed
Adding notebook and code for bullet graph article
1 parent 3c2ca76 commit 9a7e3e0

File tree

2 files changed

+555
-0
lines changed

2 files changed

+555
-0
lines changed

code/bullet_graph.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import matplotlib.pyplot as plt
2+
import seaborn as sns
3+
4+
5+
def bulletgraph(data=None, limits=None, labels=None, axis_label=None, title=None,
6+
size=(5, 3), palette=None, target_color="gray", bar_color="black",
7+
label_color="gray", formatter=None):
8+
""" Build out a bullet graph image
9+
Args:
10+
data = List of labels, measures and targets
11+
limits = list of range valules
12+
labels = list of descriptions of the limit ranges
13+
axis_label = string describing x axis
14+
title = string title of plot
15+
size = tuple for plot size
16+
palette = a seaborn palette
17+
target_color = color string for the target line
18+
bar_color = color string for the small bar
19+
label_color = color string for the limit label text
20+
formatter = matplotlib formatter object for x axis
21+
Returns:
22+
a matplotlib figure
23+
"""
24+
# Determine the max value for adjusting the bar height
25+
# Dividing by 10 seems to work pretty well
26+
h = limits[-1] / 10
27+
28+
# Use the green palette as a sensible default
29+
if palette is None:
30+
palette = sns.light_palette("green", len(limits), reverse=False)
31+
32+
# Must be able to handle one or many data sets via multiple subplots
33+
if len(data) == 1:
34+
fig, ax = plt.subplots(figsize=size, sharex=True)
35+
else:
36+
fig, axarr = plt.subplots(len(data), figsize=size, sharex=True)
37+
38+
# Add each bullet graph bar to a subplot
39+
for idx, item in enumerate(data):
40+
41+
# Get the axis from the array of axes returned when the plot is created
42+
if len(data) > 1:
43+
ax = axarr[idx]
44+
45+
# Formatting to get rid of extra marking clutter
46+
ax.set_aspect('equal')
47+
ax.set_yticklabels([item[0]])
48+
ax.set_yticks([1])
49+
ax.spines['bottom'].set_visible(False)
50+
ax.spines['top'].set_visible(False)
51+
ax.spines['right'].set_visible(False)
52+
ax.spines['left'].set_visible(False)
53+
54+
prev_limit = 0
55+
for idx2, lim in enumerate(limits):
56+
# Draw the bar
57+
ax.barh([1], lim - prev_limit, left=prev_limit, height=h,
58+
color=palette[idx2])
59+
prev_limit = lim
60+
rects = ax.patches
61+
# The last item in the list is the value we're measuring
62+
# Draw the value we're measuring
63+
ax.barh([1], item[1], height=(h / 3), color=bar_color)
64+
65+
# Need the ymin and max in order to make sure the target marker
66+
# fits
67+
ymin, ymax = ax.get_ylim()
68+
ax.vlines(
69+
item[2], ymin * .9, ymax * .9, linewidth=1.5, color=target_color)
70+
71+
# Now make some labels
72+
if labels is not None:
73+
for rect, label in zip(rects, labels):
74+
height = rect.get_height()
75+
ax.text(
76+
rect.get_x() + rect.get_width() / 2,
77+
-height * .4,
78+
label,
79+
ha='center',
80+
va='bottom',
81+
color=label_color)
82+
if axis_label:
83+
ax.set_xlabel(axis_label)
84+
if formatter:
85+
ax.xaxis.set_major_formatter(formatter)
86+
if title:
87+
fig.suptitle(title, fontsize=14)
88+
fig.subplots_adjust(hspace=0)
89+
return fig
90+
91+
92+
if __name__ == "__main__":
93+
94+
data_to_plot = [("John Smith", 105, 120), ("Jane Jones", 99, 110),
95+
("Fred Flintstone", 109, 125), ("Barney Rubble", 135, 123),
96+
("Mr T", 45, 105)]
97+
98+
my_fig = bulletgraph(
99+
data_to_plot,
100+
limits=[20, 60, 100, 160],
101+
labels=["Bad", "OK", "Good", "Excellent"],
102+
size=(8, 5),
103+
axis_label="Performance Measure",
104+
label_color="black",
105+
bar_color="#252525",
106+
target_color='#f7f7f7',
107+
title='Sales Rep Performance')
108+
plt.show()

0 commit comments

Comments
 (0)