|
| 1 | +""" |
| 2 | +Thanks Josh Hemann for the example |
| 3 | +
|
| 4 | +This examples comes from an application in which grade school gym |
| 5 | +teachers wanted to be able to show parents how their child did across |
| 6 | +a handful of fitness tests, and importantly, relative to how other |
| 7 | +children did. To extract the plotting code for demo purposes, we'll |
| 8 | +just make up some data for little Johnny Doe... |
| 9 | +
|
| 10 | +""" |
| 11 | +import numpy as np |
| 12 | +import matplotlib.pyplot as plt |
| 13 | +import pylab |
| 14 | +from matplotlib.patches import Polygon |
| 15 | +from matplotlib.ticker import MaxNLocator |
| 16 | + |
| 17 | + |
| 18 | + |
| 19 | +student = 'Johnny Doe' |
| 20 | +grade = 2 |
| 21 | +gender = 'boy' |
| 22 | +cohortSize = 62 #The number of other 2nd grade boys |
| 23 | + |
| 24 | +numTests = 5 |
| 25 | +testNames = ['Pacer Test', 'Flexed Arm\n Hang', 'Mile Run', 'Agility', |
| 26 | + 'Push Ups'] |
| 27 | +testMeta = ['laps', 'sec', 'min:sec', 'sec', ''] |
| 28 | +scores = ['7', '48', '12:52', '17', '14'] |
| 29 | +rankings = np.round(np.random.uniform(0, 1, numTests)*100, 0) |
| 30 | + |
| 31 | +fig = plt.figure(figsize=(9,7)) |
| 32 | +ax1 = fig.add_subplot(111) |
| 33 | +plt.subplots_adjust(left=0.115, right=0.88) |
| 34 | +fig.canvas.set_window_title('Eldorado K-8 Fitness Chart') |
| 35 | +pos = np.arange(numTests)+0.5 #Center bars on the Y-axis ticks |
| 36 | +rects = ax1.barh(pos, rankings, align='center', height=0.5, color='m') |
| 37 | + |
| 38 | +ax1.axis([0,100,0,5]) |
| 39 | +pylab.yticks(pos, testNames) |
| 40 | +ax1.set_title('Johnny Doe') |
| 41 | +plt.text(50, -0.5, 'Cohort Size: ' + str(cohortSize), |
| 42 | + horizontalalignment='center', size='small') |
| 43 | + |
| 44 | +# Set the right-hand Y-axis ticks and labels and set X-axis tick marks at the |
| 45 | +# deciles |
| 46 | +ax2 = ax1.twinx() |
| 47 | +ax2.plot([100,100], [0, 5], 'white', alpha=0.1) |
| 48 | +ax2.xaxis.set_major_locator(MaxNLocator(11)) |
| 49 | +xticks = pylab.setp(ax2, xticklabels=['0','10','20','30','40','50','60', |
| 50 | +'70', |
| 51 | + '80','90','100']) |
| 52 | +ax2.xaxis.grid(True, linestyle='--', which='major', color='grey', |
| 53 | +alpha=0.25) |
| 54 | +#Plot a solid vertical gridline to highlight the median position |
| 55 | +plt.plot([50,50], [0, 5], 'grey', alpha=0.25) |
| 56 | + |
| 57 | +# Build up the score labels for the right Y-axis by first appending a carriage |
| 58 | +# return to each string and then tacking on the appropriate meta information |
| 59 | +# (i.e., 'laps' vs 'seconds'). We want the labels centered on the ticks, so if |
| 60 | +# there is no meta info (like for pushups) then don't add the carriage return to |
| 61 | +# the string |
| 62 | + |
| 63 | +def withnew(i, scr): |
| 64 | + if testMeta[i] != '' : return '%s\n'%scr |
| 65 | + else: return scr |
| 66 | +scoreLabels = [withnew(i, scr) for i,scr in enumerate(scores)] |
| 67 | +scoreLabels = [i+j for i,j in zip(scoreLabels, testMeta)] |
| 68 | +pylab.yticks(pos, scoreLabels) |
| 69 | +ax2.set_ylabel('Test Scores') |
| 70 | +#Make list of numerical suffixes corresponding to position in a list |
| 71 | +# 0 1 2 3 4 5 6 7 8 9 |
| 72 | +suffixes =['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th'] |
| 73 | +ax2.set_xlabel('Percentile Ranking Across ' + str(grade) + suffixes[grade] \ |
| 74 | + + ' Grade ' + gender.title() + 's') |
| 75 | + |
| 76 | +# Lastly, write in the ranking inside each bar to aid in interpretation |
| 77 | +for rect in rects: |
| 78 | + # Rectangle widths are already integer-valued but are floating |
| 79 | + # type, so it helps to remove the trailing decimal point and 0 by |
| 80 | + # converting width to int type |
| 81 | + width = int(rect.get_width()) |
| 82 | + |
| 83 | + # Figure out what the last digit (width modulo 10) so we can add |
| 84 | + # the appropriate numerical suffix (e.g. 1st, 2nd, 3rd, etc) |
| 85 | + lastDigit = width % 10 |
| 86 | + # Note that 11, 12, and 13 are special cases |
| 87 | + if (width == 11) or (width == 12) or (width == 13): |
| 88 | + suffix = 'th' |
| 89 | + else: |
| 90 | + suffix = suffixes[lastDigit] |
| 91 | + |
| 92 | + rankStr = str(width) + suffix |
| 93 | + if (width < 5): # The bars aren't wide enough to print the ranking inside |
| 94 | + xloc = width + 1 # Shift the text to the right side of the right edge |
| 95 | + clr = 'black' # Black against white background |
| 96 | + align = 'left' |
| 97 | + else: |
| 98 | + xloc = 0.98*width # Shift the text to the left side of the right edge |
| 99 | + clr = 'white' # White on magenta |
| 100 | + align = 'right' |
| 101 | + |
| 102 | + yloc = rect.get_y()+rect.get_height()/2.0 #Center the text vertically in the bar |
| 103 | + ax1.text(xloc, yloc, rankStr, horizontalalignment=align, |
| 104 | + verticalalignment='center', color=clr, weight='bold') |
| 105 | + |
| 106 | +plt.show() |
| 107 | + |
0 commit comments