diff options
author | Camil Staps | 2016-09-03 21:47:27 +0200 |
---|---|---|
committer | Camil Staps | 2016-09-03 21:47:27 +0200 |
commit | 3ca995db695d5b20d17c8bffb348c8ddc59ab469 (patch) | |
tree | 82b6fb571bfa5fac8c4e5c0a0524410f5e2b6bdc | |
parent | texformat.py: create latex formats for students (diff) |
plotgrades: fixes for new blackboard settings, added --group-by
-rwxr-xr-x | plotgrades.py | 57 |
1 files changed, 43 insertions, 14 deletions
diff --git a/plotgrades.py b/plotgrades.py index 165d0bc..f3c8baa 100755 --- a/plotgrades.py +++ b/plotgrades.py @@ -2,14 +2,16 @@ import argparse import csv from functools import partial +import itertools import matplotlib.pyplot as plt import numpy +from operator import itemgetter import re def readcsv(filename): """Read a (BlackBoard) CSV file into a header and data""" with open(filename, 'r') as csvfile: - reader = csv.reader(csvfile, delimiter=',', quotechar='"') + reader = csv.reader(csvfile, delimiter='\t', quotechar='"') data = [] header = next(reader) for row in reader: @@ -37,9 +39,9 @@ def remove_zeros(matrix): want to distort the boxplots. This function removes zeros on a per-column basis.""" m_new = [] - for col in numpy.transpose(matrix): + for col in matrix: m_new.append([v for v in col if v != 0.0]) - return numpy.transpose(m_new) + return m_new def normalise(matrix): """Normalise grades to a 0-100 range @@ -53,15 +55,16 @@ def normalise(matrix): def strip_header(h): """Strip common BlackBoard additions in the CSV header""" - return h.split('instructor.download.column.total')[0] + return h.split('[Total Pts:')[0] def remove_empty_lists(headers, data): """Remove empty columns and the corresponding headers from a matrix""" new_headers, new_data = [], [] - for h, d in zip(headers, data): - if d != []: + for h, col in zip(headers, numpy.transpose(data)): + if not all([x == 0.0 for x in col]): new_headers.append(h) - new_data.append(d) + new_data.append(col) + return new_headers, new_data def header_regex_callback(header, regex='', invert=False): @@ -97,23 +100,47 @@ def check_participant_callback(headers, data, participant_callback): new_data.append(d) return new_data +def make_groups(headers, data, column): + i = 0 + for h in headers: + if column in h: + break + i += 1 + + data = list(numpy.transpose(data)) + data.sort(key=itemgetter(i)) + data = [list(x) for _, x in itertools.groupby(data, itemgetter(i))] + new_headers = [] + new_data = [] + j = 0 + for h in headers: + for group in data: + if not all([x[j] == 0.0 for x in group]): + new_headers.append(h + ' (group: ' + str(group[0][i]) + ')') + new_data.append([x[j] for x in group]) + j += 1 + + return new_headers, new_data + def plotgrades(headers, data, skip=0, - participant_callback=lambda x:True, header_callback=lambda x:True): + participant_callback=lambda x:True, header_callback=lambda x:True, + group_participants_column=None): """Plot grades corresponding to headers in a boxplot""" data = check_participant_callback(headers, data, participant_callback) - headers, data = headers[skip:], data[skip:] + headers, data = map(strip_header, headers[skip:]), data[skip:] data = parse_floats(data) - data = remove_zeros(data) headers, data = remove_empty_lists(headers, data) + if group_participants_column is not None: + headers, data = make_groups(headers, data, group_participants_column) + data = remove_zeros(data) headers, data = check_header_callback(headers, data, header_callback) data = normalise(data) - headers = [strip_header(h) for h in headers] if len(data) > 0: plt.boxplot(data) ax = plt.gca() - plt.xticks(range(0, len(data) + 1), [''] + headers, rotation=90) + plt.xticks(range(1, len(data) + 1), headers, rotation=90) ax.set_ylim([0,100]) plt.show() @@ -128,6 +155,8 @@ def parse_args(): help='Restrict what grades are shown with a regex') pars.add_argument('-wh', '--where-has', metavar='regex', default='', help='Only count participants with a grade for a matching item') + pars.add_argument('-g', '--group-by', metavar='column', default=None, + help='Group participants by this column') pars.add_argument('-iw', '--invert-where', action='store_true', help='Invert --where regex') pars.add_argument('-iwh', '--invert-where-has', action='store_true', @@ -146,8 +175,8 @@ def main(): header_callback=partial(header_regex_callback, regex=args.where, invert=args.invert_where), participant_callback=partial(participant_wherehas_callback, - regex=args.where_has, invert=args.invert_where_has)) + regex=args.where_has, invert=args.invert_where_has), + group_participants_column=args.group_by) if __name__ == '__main__': main() - |