#!/usr/bin/env python3 # # Copyright (C) 2022 Jonathan A. Poritz # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # import sys import warnings import csv import numpy as np import matplotlib.pyplot as plt import argparse def pairgraph(first_dataset,first_medianline,ylim,second_dataset,second_medianline,main_title,first_boxtitle,first_medianstr,first_meanstr,first_stdstr,second_boxtitle,second_medianstr,second_meanstr,second_stdstr): bins=[5*i for i in range(20)] plt.rc('text', usetex=True) fig, axs = plt.subplots(2) axs[0].hist(np.array(first_dataset),bins,histtype='bar',color='white',alpha=0.8,edgecolor='black') if ylim: axs[0].set_ylim((0,ylim)) axs[0].set_xlim((0,100)) axs[0].axvline(x=first_medianline) axs[1].hist(np.array(second_dataset),bins,histtype='bar',color='white',alpha=0.8,edgecolor='black') if ylim: axs[1].set_ylim((0,ylim)) axs[1].set_xlim((0,100)) axs[1].axvline(x=second_medianline) fig.text(0.04, 0.5, 'Number of Participants', va='center', rotation='vertical') plt.xlabel('Percent Correct') fig.text(.5, .9, main_title, ha='center') fig.text(.17, .69, first_boxtitle+'\nmedian='+first_medianstr+'\n$\overline{x}='+first_meanstr+'$\n$\sigma='+first_stdstr+'$\n$n='+str(len(first_dataset))+'$', bbox=dict(fc="none")) fig.text(.17, .26, second_boxtitle+'\nmedian='+second_medianstr+'\n$\overline{x}='+second_meanstr+'$\n$\sigma='+second_stdstr+'$\n$n='+str(len(second_dataset))+'$', bbox=dict(fc="none")) plt.show() return if not sys.warnoptions: warnings.simplefilter("ignore") parser = argparse.ArgumentParser(description='Make graphs associated with pre- and post-test of material in the Creative Commons Certificate coures.') parser.add_argument("--input_file", help="CSV file that contains test results as produced by Google Forms, based on the test dating 17 Jan 2022, but including a column FW (i.e., column with Python list index 178) put in by hand that indicates P or F or depending upon whether that participant eventually passed, failed, or their final status is unknown. Default is 'results.csv'.", default="results.csv") group=parser.add_mutually_exclusive_group() group.add_argument("--up", help="treat participants whose pass/fail status in the course is unknown as if they passed (conflicts with '--uf')", action="store_true") group.add_argument("--uf", help="treat participants whose pass/fail status in the course is unknown as if they failed (conflicts with '--up')", action="store_true") parser.add_argument("--prepostpass", help="show paired graphs of pre- and post-scores, for only passing folks", action="store_true") parser.add_argument("--prepostall", help="show paired graphs of pre- and post-scores, for all students (P&F)", action="store_true") parser.add_argument("--pretestpassfail", help="show paired graphs of pass vs fail students on pre-tests", action="store_true") parser.add_argument("--unit",type=int,choices=[1,2,3,4,5],help="consider only the score for parts of test corresponding to the specified unit of the course; when missing, use the entire test score") parser.add_argument("--ylim",type=int,help="the upper limit of y-axes of the histograms; when absent, picks automatically",default=0) parser.add_argument("--ed", help="show graphs for educator section participants", action="store_true") parser.add_argument("--lib", help="show graphs for librarian section participants", action="store_true") parser.add_argument("--glam", help="show graphs for GLAM section participants", action="store_true") parser.add_argument("--allsections", help="show graphs which combine participants from all sections (ed, lib, and GLAM)", action="store_true") args = parser.parse_args() fdin=open(args.input_file,"r",newline='') rdr=csv.reader(fdin) r=next(rdr) u1=[13,16,18,20,22,25,28] u2=[31,34,37,40,43,45,47,49,52,55,58,61,64,67,70] u3=[73,76,79,82,85,88,91,94,97,100,103,106,109] u4=[112,115,118,121,124,127,130,133,136,138,140,142,144,147] u5=[150,152,154,156,158,160,163,166,168,170,173] unit_colnums=[0,u1,u2,u3,u4,u5] scores={'ed':{'pre':{'p':[],'f':[],'u':[],'pf':[]},'post':{'p':[],'f':[],'u':[],'pf':[]}},'lib':{'pre':{'p':[],'f':[],'u':[],'pf':[]},'post':{'p':[],'f':[],'u':[],'pf':[]}},'glam':{'pre':{'p':[],'f':[],'u':[],'pf':[]},'post':{'p':[],'f':[],'u':[],'pf':[]}},'all':{'pre':{'p':[],'f':[],'u':[],'pf':[]},'post':{'p':[],'f':[],'u':[],'pf':[]}}} for r in rdr: if args.unit: points=0 for i in unit_colnums[args.unit]: if len(r[i])>0: points += int(r[i][:r[i].index('.00')]) score = 100*points/len(unit_colnums[args.unit]) title_frag="Unit "+str(args.unit) else: score = int(r[2][:r[2].index('.00')])/.6 title_frag="Over-all" if r[3]=='Will take in the future': time='pre' elif r[3]=='Already taken': time='post' else: raise ValueError("pre/post error: "+r[3]) if r[6]=='CC Certificate for Educators': version='ed' elif r[6]=='CC Certificate for Librarians': version='lib' elif r[6]=='CC Certificate for GLAM': version='glam' else: raise ValueError("ed/lib/glam error: "+r[6]) if len(r)<179: if args.up: success='p' elif args.uf: success='f' else: success='u' elif r[178]=='P': success='p' elif r[178]=='F': success='f' else: if args.up: success='p' elif args.uf: success='f' else: success='u' scores[version][time][success].append(score) scores[version][time]['pf'].append(score) scores['all'][time][success].append(score) scores['all'][time]['pf'].append(score) means={} medians={} medianv={} stdevs={} for ve in ['ed','lib','glam','all']: means[ve]={} medianv[ve]={} medians[ve]={} stdevs[ve]={} for ti in ['pre','post']: means[ve][ti]={} medianv[ve][ti]={} medians[ve][ti]={} stdevs[ve][ti]={} for su in ['p','f','u','pf']: data = scores[ve][ti][su] if len(data): means[ve][ti][su]=str(round(np.mean(data),2)) medval=np.median(data) medianv[ve][ti][su]=medval medians[ve][ti][su]=str(round(medval,1)) stdevs[ve][ti][su]=str(round(np.std(data),2)) else: means[ve][ti][su]='undef' medianv[ve][ti][su]=-1 medians[ve][ti][su]='undef' stdevs[ve][ti][su]='undef' sections_to_graph=[] if args.ed: sections_to_graph.append('ed') if args.lib: sections_to_graph.append('lib') if args.glam: sections_to_graph.append('glam') if args.allsections: sections_to_graph.append('all') if args.prepostpass: if args.ed: pairgraph(first_dataset=scores['ed']['pre']['p'], ylim=args.ylim, first_medianline=medianv['ed']['pre']['p'], second_dataset=scores['ed']['post']['p'], second_medianline=medianv['ed']['post']['p'], main_title='Histograms of '+title_frag+' Score\nBefore and After Cert Course\nEducator Section (only passing students)', first_boxtitle='Before Course', first_medianstr=medians['ed']['pre']['p'], first_meanstr=means['ed']['pre']['p'], first_stdstr=stdevs['ed']['pre']['p'], second_boxtitle='After Course', second_medianstr=medians['ed']['post']['p'], second_meanstr=means['ed']['post']['p'], second_stdstr=stdevs['ed']['post']['p']) if args.lib: pairgraph(first_dataset=scores['lib']['pre']['p'], ylim=args.ylim, first_medianline=medianv['lib']['pre']['p'], second_dataset=scores['lib']['post']['p'], second_medianline=medianv['lib']['post']['p'], main_title='Histograms of '+title_frag+' Score\nBefore and After Cert Course\nLibrarian Section (only passing students)', first_boxtitle='Before Course', first_medianstr=medians['lib']['pre']['p'], first_meanstr=means['lib']['pre']['p'], first_stdstr=stdevs['lib']['pre']['p'], second_boxtitle='After Course', second_medianstr=medians['lib']['post']['p'], second_meanstr=means['lib']['post']['p'], second_stdstr=stdevs['lib']['post']['p']) if args.glam: pairgraph(first_dataset=scores['glam']['pre']['p'], ylim=args.ylim, first_medianline=medianv['glam']['pre']['p'], second_dataset=scores['glam']['post']['p'], second_medianline=medianv['glam']['post']['p'], main_title='Histograms of '+title_frag+' Score\nBefore and After Cert Course\nGLAM Section (only passing students)', first_boxtitle='Before Course', first_medianstr=medians['glam']['pre']['p'], first_meanstr=means['glam']['pre']['p'], first_stdstr=stdevs['glam']['pre']['p'], second_boxtitle='After Course', second_medianstr=medians['glam']['post']['p'], second_meanstr=means['glam']['post']['p'], second_stdstr=stdevs['glam']['post']['p']) if args.allsections: pairgraph(first_dataset=scores['all']['pre']['p'], ylim=args.ylim, first_medianline=medianv['all']['pre']['p'], second_dataset=scores['all']['post']['p'], second_medianline=medianv['all']['post']['p'], main_title='Histograms of '+title_frag+' Score\nBefore and After Cert Course\nEducator, Librarian, and GLAM Sections (only passing students)', first_boxtitle='Before Course', first_medianstr=medians['all']['pre']['p'], first_meanstr=means['all']['pre']['p'], first_stdstr=stdevs['all']['pre']['p'], second_boxtitle='After Course', second_medianstr=medians['all']['post']['p'], second_meanstr=means['all']['post']['p'], second_stdstr=stdevs['all']['post']['p']) if args.prepostall: if args.ed: pairgraph(first_dataset=scores['ed']['pre']['pf'], ylim=args.ylim, first_medianline=medianv['ed']['pre']['pf'], second_dataset=scores['ed']['post']['pf'], second_medianline=medianv['ed']['post']['pf'], main_title='Histograms of '+title_frag+' Score\nBefore and After Cert Course\nEducator Section', first_boxtitle='Before Course', first_medianstr=medians['ed']['pre']['pf'], first_meanstr=means['ed']['pre']['pf'], first_stdstr=stdevs['ed']['pre']['pf'], second_boxtitle='After Course', second_medianstr=medians['ed']['post']['pf'], second_meanstr=means['ed']['post']['pf'], second_stdstr=stdevs['ed']['post']['pf']) if args.lib: pairgraph(first_dataset=scores['lib']['pre']['pf'], ylim=args.ylim, first_medianline=medianv['lib']['pre']['pf'], second_dataset=scores['lib']['post']['pf'], second_medianline=medianv['lib']['post']['pf'], main_title='Histograms of '+title_frag+' Score\nBefore and After Cert Course\nLibrarian Section', first_boxtitle='Before Course', first_medianstr=medians['lib']['pre']['pf'], first_meanstr=means['lib']['pre']['pf'], first_stdstr=stdevs['lib']['pre']['pf'], second_boxtitle='After Course', second_medianstr=medians['lib']['post']['pf'], second_meanstr=means['lib']['post']['pf'], second_stdstr=stdevs['lib']['post']['pf']) if args.glam: pairgraph(first_dataset=scores['glam']['pre']['pf'], ylim=args.ylim, first_medianline=medianv['glam']['pre']['pf'], second_dataset=scores['glam']['post']['pf'], second_medianline=medianv['glam']['post']['pf'], main_title='Histograms of '+title_frag+' Score\nBefore and After Cert Course\nGLAM Section', first_boxtitle='Before Course', first_medianstr=medians['glam']['pre']['pf'], first_meanstr=means['glam']['pre']['pf'], first_stdstr=stdevs['glam']['pre']['pf'], second_boxtitle='After Course', second_medianstr=medians['glam']['post']['pf'], second_meanstr=means['glam']['post']['pf'], second_stdstr=stdevs['glam']['post']['pf']) if args.allsections: pairgraph(first_dataset=scores['all']['pre']['pf'], ylim=args.ylim, first_medianline=medianv['all']['pre']['pf'], second_dataset=scores['all']['post']['pf'], second_medianline=medianv['all']['post']['pf'], main_title='Histograms of '+title_frag+' Score\nBefore and After Cert Course\nEducator, Librarian, and GLAM Sections', first_boxtitle='Before Course', first_medianstr=medians['all']['pre']['pf'], first_meanstr=means['all']['pre']['pf'], first_stdstr=stdevs['all']['pre']['pf'], second_boxtitle='After Course', second_medianstr=medians['all']['post']['pf'], second_meanstr=means['all']['post']['pf'], second_stdstr=stdevs['all']['post']['pf']) if args.pretestpassfail: if args.ed: pairgraph(first_dataset=scores['ed']['pre']['f'], ylim=args.ylim, first_medianline=medianv['ed']['pre']['f'], second_dataset=scores['ed']['pre']['p'], second_medianline=medianv['ed']['pre']['p'], main_title='Histograms of '+title_frag+' Pre-Test Score\nAmong Students Who Eventually Fail or Pass Course\nEducator Section', first_boxtitle='Eventual Fail', first_medianstr=medians['ed']['pre']['f'], first_meanstr=means['ed']['pre']['f'], first_stdstr=stdevs['ed']['pre']['f'], second_boxtitle='Eventual Pass', second_medianstr=medians['ed']['pre']['p'], second_meanstr=means['ed']['pre']['p'], second_stdstr=stdevs['ed']['pre']['p']) if args.lib: pairgraph(first_dataset=scores['lib']['pre']['f'], ylim=args.ylim, first_medianline=medianv['lib']['pre']['f'], second_dataset=scores['lib']['pre']['p'], second_medianline=medianv['lib']['pre']['p'], main_title='Histograms of '+title_frag+' Pre-Test Score\nAmong Students Who Eventually Fail or Pass Course\nLibrarian Section', first_boxtitle='Eventual Fail', first_medianstr=medians['lib']['pre']['f'], first_meanstr=means['lib']['pre']['f'], first_stdstr=stdevs['lib']['pre']['f'], second_boxtitle='Eventual Pass', second_medianstr=medians['lib']['pre']['p'], second_meanstr=means['lib']['pre']['p'], second_stdstr=stdevs['lib']['pre']['p']) if args.glam: pairgraph(first_dataset=scores['glam']['pre']['f'], ylim=args.ylim, first_medianline=medianv['glam']['pre']['f'], second_dataset=scores['glam']['pre']['p'], second_medianline=medianv['glam']['pre']['p'], main_title='Histograms of '+title_frag+' Pre-Test Score\nAmong Students Who Eventually Fail or Pass Course\nGLAM Section', first_boxtitle='Eventual Fail', first_medianstr=medians['glam']['pre']['f'], first_meanstr=means['glam']['pre']['f'], first_stdstr=stdevs['glam']['pre']['f'], second_boxtitle='Eventual Pass', second_medianstr=medians['glam']['pre']['p'], second_meanstr=means['glam']['pre']['p'], second_stdstr=stdevs['glam']['pre']['p']) if args.allsections: pairgraph(first_dataset=scores['all']['pre']['f'], ylim=args.ylim, first_medianline=medianv['all']['pre']['f'], second_dataset=scores['all']['pre']['p'], second_medianline=medianv['all']['pre']['p'], main_title='Histograms of '+title_frag+' Pre-Test Score\nAmong Students Who Eventually Fail or Pass Course\nEducator, Librarian, and GLAM Sections', first_boxtitle='Eventual Fail', first_medianstr=medians['all']['pre']['f'], first_meanstr=means['all']['pre']['f'], first_stdstr=stdevs['all']['pre']['f'], second_boxtitle='Eventual Pass', second_medianstr=medians['all']['pre']['p'], second_meanstr=means['all']['pre']['p'], second_stdstr=stdevs['all']['pre']['p'])