From 952a45258b964c7f88186cd775c88b01426b36a5 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 12 Dec 2015 22:11:23 -0500 Subject: [PATCH 1/3] DOC: update example Update example to use 'modern' idioms --- examples/pylab_examples/barchart_demo2.py | 33 +++++++++++------------ 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/examples/pylab_examples/barchart_demo2.py b/examples/pylab_examples/barchart_demo2.py index 6db915d2d23d..7507a8101035 100644 --- a/examples/pylab_examples/barchart_demo2.py +++ b/examples/pylab_examples/barchart_demo2.py @@ -29,33 +29,32 @@ plt.subplots_adjust(left=0.115, right=0.88) fig.canvas.set_window_title('Eldorado K-8 Fitness Chart') pos = np.arange(numTests) + 0.5 # Center bars on the Y-axis ticks -rects = ax1.barh(pos, rankings, align='center', height=0.5, color='m') -ax1.axis([0, 100, 0, 5]) -plt.yticks(pos, testNames) +rects = ax1.barh(pos, rankings, align='center', height=0.5, color='m', + tick_label=testNames) + ax1.set_title('Johnny Doe') -plt.text(50, -0.5, 'Cohort Size: ' + str(cohortSize), - horizontalalignment='center', size='small') + +ax1.set_xlim([0, 100]) +ax1.xaxis.set_major_locator(MaxNLocator(11)) +ax1.xaxis.grid(True, linestyle='--', which='major', color='grey', alpha=.25) + +# Plot a solid vertical gridline to highlight the median position +ax1.axvline(50, color='grey', alpha=0.25) + +ax1.text(.5, -.07, 'Cohort Size: ' + str(cohortSize), + horizontalalignment='center', size='small', + transform=ax1.transAxes) # Set the right-hand Y-axis ticks and labels and set X-axis tick marks at the # deciles ax2 = ax1.twinx() -ax2.plot([100, 100], [0, 5], 'white', alpha=0.1) -ax2.xaxis.set_major_locator(MaxNLocator(11)) -xticks = plt.setp(ax2, xticklabels=['0', '10', '20', '30', '40', '50', '60', - '70', '80', '90', '100']) -ax2.xaxis.grid(True, linestyle='--', which='major', color='grey', - alpha=0.25) -# Plot a solid vertical gridline to highlight the median position -plt.plot([50, 50], [0, 5], 'grey', alpha=0.25) # Build up the score labels for the right Y-axis by first appending a carriage # return to each string and then tacking on the appropriate meta information # (i.e., 'laps' vs 'seconds'). We want the labels centered on the ticks, so if # there is no meta info (like for pushups) then don't add the carriage return to # the string - - def withnew(i, scr): if testMeta[i] != '': return '%s\n' % scr @@ -71,7 +70,6 @@ def withnew(i, scr): # make sure that the limits are set equally on both yaxis so the ticks line up ax2.set_ylim(ax1.get_ylim()) - ax2.set_ylabel('Test Scores') # Make list of numerical suffixes corresponding to position in a list # 0 1 2 3 4 5 6 7 8 9 @@ -108,6 +106,7 @@ def withnew(i, scr): # Center the text vertically in the bar yloc = rect.get_y() + rect.get_height()/2.0 ax1.text(xloc, yloc, rankStr, horizontalalignment=align, - verticalalignment='center', color=clr, weight='bold') + verticalalignment='center', color=clr, weight='bold', + clip_on=True) plt.show() From 8458613a4d3beabbb2ff280ad3b8bb625adff020 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 12 Dec 2015 23:01:40 -0500 Subject: [PATCH 2/3] DOC: major overhaul of demo - functionalize it - mouse over y value gives nice result --- examples/pylab_examples/barchart_demo2.py | 222 +++++++++++++--------- 1 file changed, 131 insertions(+), 91 deletions(-) diff --git a/examples/pylab_examples/barchart_demo2.py b/examples/pylab_examples/barchart_demo2.py index 7507a8101035..8244817a0f78 100644 --- a/examples/pylab_examples/barchart_demo2.py +++ b/examples/pylab_examples/barchart_demo2.py @@ -11,102 +11,142 @@ import numpy as np import matplotlib.pyplot as plt from matplotlib.ticker import MaxNLocator +from collections import namedtuple -student = 'Johnny Doe' -grade = 2 -gender = 'boy' -cohortSize = 62 # The number of other 2nd grade boys +Student = namedtuple('Student', ['name', 'grade', 'gender']) +Score = namedtuple('Score', ['score', 'percentile']) -numTests = 5 +# GLOBAL CONSTANTS testNames = ['Pacer Test', 'Flexed Arm\n Hang', 'Mile Run', 'Agility', 'Push Ups'] -testMeta = ['laps', 'sec', 'min:sec', 'sec', ''] -scores = ['7', '48', '12:52', '17', '14'] -rankings = np.round(np.random.uniform(0, 1, numTests)*100, 0) - - -fig, ax1 = plt.subplots(figsize=(9, 7)) -plt.subplots_adjust(left=0.115, right=0.88) -fig.canvas.set_window_title('Eldorado K-8 Fitness Chart') -pos = np.arange(numTests) + 0.5 # Center bars on the Y-axis ticks - -rects = ax1.barh(pos, rankings, align='center', height=0.5, color='m', - tick_label=testNames) - -ax1.set_title('Johnny Doe') - -ax1.set_xlim([0, 100]) -ax1.xaxis.set_major_locator(MaxNLocator(11)) -ax1.xaxis.grid(True, linestyle='--', which='major', color='grey', alpha=.25) - -# Plot a solid vertical gridline to highlight the median position -ax1.axvline(50, color='grey', alpha=0.25) - -ax1.text(.5, -.07, 'Cohort Size: ' + str(cohortSize), - horizontalalignment='center', size='small', - transform=ax1.transAxes) - -# Set the right-hand Y-axis ticks and labels and set X-axis tick marks at the -# deciles -ax2 = ax1.twinx() - -# Build up the score labels for the right Y-axis by first appending a carriage -# return to each string and then tacking on the appropriate meta information -# (i.e., 'laps' vs 'seconds'). We want the labels centered on the ticks, so if -# there is no meta info (like for pushups) then don't add the carriage return to -# the string -def withnew(i, scr): - if testMeta[i] != '': - return '%s\n' % scr +testMeta = dict(zip(testNames, ['laps', 'sec', 'min:sec', 'sec', ''])) + +def attach_ordinal(num): + """helper function to add ordinal string to integers + + 1 -> 1st + 56 -> 56th + """ + suffixes = dict((str(i), v) for i, v in + enumerate(['th', 'st', 'nd', 'rd', 'th', + 'th', 'th', 'th', 'th', 'th'])) + + v = str(num) + # special case early teens + if v in {'11', '12', '13'}: + return v + 'th' + return v + suffixes[v[-1]] + +def format_score(scr, test): + """ + Build up the score labels for the right Y-axis by first + appending a carriage return to each string and then tacking on + the appropriate meta information (i.e., 'laps' vs 'seconds'). We + want the labels centered on the ticks, so if there is no meta + info (like for pushups) then don't add the carriage return to + the string + """ + md = testMeta[test] + if md: + return '{}\n{}'.format(scr, md) else: return scr -scoreLabels = [withnew(i, scr) for i, scr in enumerate(scores)] -scoreLabels = [i + j for i, j in zip(scoreLabels, testMeta)] -# set the tick locations -ax2.set_yticks(pos) -# set the tick labels -ax2.set_yticklabels(scoreLabels) -# make sure that the limits are set equally on both yaxis so the ticks line up -ax2.set_ylim(ax1.get_ylim()) - -ax2.set_ylabel('Test Scores') -# Make list of numerical suffixes corresponding to position in a list -# 0 1 2 3 4 5 6 7 8 9 -suffixes = ['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th'] -ax2.set_xlabel('Percentile Ranking Across ' + str(grade) + suffixes[grade] - + ' Grade ' + gender.title() + 's') - -# Lastly, write in the ranking inside each bar to aid in interpretation -for rect in rects: - # Rectangle widths are already integer-valued but are floating - # type, so it helps to remove the trailing decimal point and 0 by - # converting width to int type - width = int(rect.get_width()) - - # Figure out what the last digit (width modulo 10) so we can add - # the appropriate numerical suffix (e.g., 1st, 2nd, 3rd, etc) - lastDigit = width % 10 - # Note that 11, 12, and 13 are special cases - if (width == 11) or (width == 12) or (width == 13): - suffix = 'th' - else: - suffix = suffixes[lastDigit] - - rankStr = str(width) + suffix - if (width < 5): # The bars aren't wide enough to print the ranking inside - xloc = width + 1 # Shift the text to the right side of the right edge - clr = 'black' # Black against white background - align = 'left' +def format_ycursor(y): + y = int(y) + if y < 0 or y >= len(testNames): + return '' else: - xloc = 0.98*width # Shift the text to the left side of the right edge - clr = 'white' # White on magenta - align = 'right' - - # Center the text vertically in the bar - yloc = rect.get_y() + rect.get_height()/2.0 - ax1.text(xloc, yloc, rankStr, horizontalalignment=align, - verticalalignment='center', color=clr, weight='bold', - clip_on=True) - -plt.show() + return testNames[y] + + +def plot_student_results(student, scores, cohort_size): + # create the figure + fig, ax1 = plt.subplots(figsize=(9, 7)) + fig.subplots_adjust(left=0.115, right=0.88) + fig.canvas.set_window_title('Eldorado K-8 Fitness Chart') + + pos = np.arange(len(testNames)) + 0.5 # Center bars on the Y-axis ticks + + rects = ax1.barh(pos, [scores[k].percentile for k in testNames], + align='center', + height=0.5, color='m', + tick_label=testNames) + + ax1.set_title(student.name) + + ax1.set_xlim([0, 100]) + ax1.xaxis.set_major_locator(MaxNLocator(11)) + ax1.xaxis.grid(True, linestyle='--', which='major', color='grey', alpha=.25) + + # Plot a solid vertical gridline to highlight the median position + ax1.axvline(50, color='grey', alpha=0.25) + # set X-axis tick marks at the deciles + cohort_label = ax1.text(.5, -.07, 'Cohort Size: {}'.format(cohort_size), + horizontalalignment='center', size='small', + transform=ax1.transAxes) + + # Set the right-hand Y-axis ticks and labels + ax2 = ax1.twinx() + + scoreLabels = [format_score(scores[k].score, k) for k in testNames] + + # set the tick locations + ax2.set_yticks(pos) + # make sure that the limits are set equally on both yaxis so the + # ticks line up + ax2.set_ylim(ax1.get_ylim()) + + # set the tick labels + ax2.set_yticklabels(scoreLabels) + + ax2.set_ylabel('Test Scores') + + ax2.set_xlabel( + 'Percentile Ranking Across {grade} Grade {gender}s'.format( + grade=attach_ordinal(student.grade), + gender=student.gender.title())) + + rect_labels = [] + # Lastly, write in the ranking inside each bar to aid in interpretation + for rect in rects: + # Rectangle widths are already integer-valued but are floating + # type, so it helps to remove the trailing decimal point and 0 by + # converting width to int type + width = int(rect.get_width()) + + rankStr = attach_ordinal(width) + if (width < 5): # The bars aren't wide enough to print the ranking inside + xloc = width + 1 # Shift the text to the right side of the right edge + clr = 'black' # Black against white background + align = 'left' + else: + xloc = 0.98*width # Shift the text to the left side of the right edge + clr = 'white' # White on magenta + align = 'right' + + # Center the text vertically in the bar + yloc = rect.get_y() + rect.get_height()/2.0 + label = ax1.text(xloc, yloc, rankStr, horizontalalignment=align, + verticalalignment='center', color=clr, weight='bold', + clip_on=True) + rect_labels.append(label) + + # make the interactive mouse over give the bar title + ax2.fmt_ydata = format_ycursor + # return all of the artists created + return {'fig': fig, + 'ax': ax1, + 'ax_right': ax2, + 'bars': rects, + 'perc_labels': rect_labels, + 'cohort_label': cohort_label} + +student = Student('Johnny Doe', 2, 'boy') +scores = dict(zip(testNames, + (Score(v, p) for v, p in + zip(['7', '48', '12:52', '17', '14'], + np.round(np.random.uniform(0, 1, len(testNames))*100, 0))))) +cohort_size = 62 # The number of other 2nd grade boys + +arts = plot_student_results(student, scores, cohort_size) From 6341cc214be025619bb2af28c41f2a5bb651234a Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 13 Dec 2015 13:35:20 -0500 Subject: [PATCH 3/3] STY: pep8 fixup --- examples/pylab_examples/barchart_demo2.py | 32 +++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/examples/pylab_examples/barchart_demo2.py b/examples/pylab_examples/barchart_demo2.py index 8244817a0f78..d942bd71f332 100644 --- a/examples/pylab_examples/barchart_demo2.py +++ b/examples/pylab_examples/barchart_demo2.py @@ -21,6 +21,7 @@ 'Push Ups'] testMeta = dict(zip(testNames, ['laps', 'sec', 'min:sec', 'sec', ''])) + def attach_ordinal(num): """helper function to add ordinal string to integers @@ -37,6 +38,7 @@ def attach_ordinal(num): return v + 'th' return v + suffixes[v[-1]] + def format_score(scr, test): """ Build up the score labels for the right Y-axis by first @@ -52,6 +54,7 @@ def format_score(scr, test): else: return scr + def format_ycursor(y): y = int(y) if y < 0 or y >= len(testNames): @@ -77,7 +80,8 @@ def plot_student_results(student, scores, cohort_size): ax1.set_xlim([0, 100]) ax1.xaxis.set_major_locator(MaxNLocator(11)) - ax1.xaxis.grid(True, linestyle='--', which='major', color='grey', alpha=.25) + ax1.xaxis.grid(True, linestyle='--', which='major', + color='grey', alpha=.25) # Plot a solid vertical gridline to highlight the median position ax1.axvline(50, color='grey', alpha=0.25) @@ -102,10 +106,10 @@ def plot_student_results(student, scores, cohort_size): ax2.set_ylabel('Test Scores') - ax2.set_xlabel( - 'Percentile Ranking Across {grade} Grade {gender}s'.format( - grade=attach_ordinal(student.grade), - gender=student.gender.title())) + ax2.set_xlabel(('Percentile Ranking Across ' + '{grade} Grade {gender}s').format( + grade=attach_ordinal(student.grade), + gender=student.gender.title())) rect_labels = [] # Lastly, write in the ranking inside each bar to aid in interpretation @@ -116,13 +120,18 @@ def plot_student_results(student, scores, cohort_size): width = int(rect.get_width()) rankStr = attach_ordinal(width) - if (width < 5): # The bars aren't wide enough to print the ranking inside - xloc = width + 1 # Shift the text to the right side of the right edge - clr = 'black' # Black against white background + # The bars aren't wide enough to print the ranking inside + if (width < 5): + # Shift the text to the right side of the right edge + xloc = width + 1 + # Black against white background + clr = 'black' align = 'left' else: - xloc = 0.98*width # Shift the text to the left side of the right edge - clr = 'white' # White on magenta + # Shift the text to the left side of the right edge + xloc = 0.98*width + # White on magenta + clr = 'white' align = 'right' # Center the text vertically in the bar @@ -146,7 +155,8 @@ def plot_student_results(student, scores, cohort_size): scores = dict(zip(testNames, (Score(v, p) for v, p in zip(['7', '48', '12:52', '17', '14'], - np.round(np.random.uniform(0, 1, len(testNames))*100, 0))))) + np.round(np.random.uniform(0, 1, + len(testNames))*100, 0))))) cohort_size = 62 # The number of other 2nd grade boys arts = plot_student_results(student, scores, cohort_size)