Annotation of capa/capa51/Historic/capa_stats.c, revision 1.2
1.2 ! albertel 1: /* early version of some statistics for CAPA
! 2: Copyright (C) 1992-2000 Michigan State University
! 3:
! 4: The CAPA system is free software; you can redistribute it and/or
! 5: modify it under the terms of the GNU Library General Public License as
! 6: published by the Free Software Foundation; either version 2 of the
! 7: License, or (at your option) any later version.
! 8:
! 9: The CAPA system is distributed in the hope that it will be useful,
! 10: but WITHOUT ANY WARRANTY; without even the implied warranty of
! 11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
! 12: Library General Public License for more details.
! 13:
! 14: You should have received a copy of the GNU Library General Public
! 15: License along with the CAPA system; see the file COPYING. If not,
! 16: write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
! 17: Boston, MA 02111-1307, USA.
! 18:
! 19: As a special exception, you have permission to link this program
! 20: with the TtH/TtM library and distribute executables, as long as you
! 21: follow the requirements of the GNU GPL in regard to all of the
! 22: software in the executable aside from TtH/TtM.
! 23: */
! 24:
1.1 albertel 25: #include <stdio.h>
26: #include <string.h>
27: #include <math.h>
28: #include <ctype.h>
29: #ifdef __sun
30: #include <unistd.h>
31: #endif
32: #ifdef NeXT
33: #include <sys/file.h>
34: #endif
35: #include <stdlib.h>
36:
37: /*--------------------------------------------------------------*/
38: /* Constant Values for "hgr*.out". */
39: /*--------------------------------------------------------------*/
40: #define MAX_SECTION_NUMBER 3 /* maximum number of char for section number */
41: #define MAX_STUDENT_NUMBER 9 /* maximum number of char for student number */
42: #define MAX_STUDENT_NAME 30 /* maximum number of char for student name */
43: #define MAX_PROBLEM_NUMBER 150 /* maximum number of problems in a set */
44: #define FILE_NAME_LENGTH 256 /* maximum length of file name */
45: #define MAX_CLASS_SIZE 1024 /* maximum number of students in a class */
46:
47:
48: /*--------------------------------------------------------------*/
49: /* Constant Values for "set*.db". */
50: /*--------------------------------------------------------------*/
51: #define MAX_TRIES 100 /* tries 0..99 */
52:
53:
54: /*--------------------------------------------------------------*/
55: /* One line of record in "hgr*.out". */
56: /*--------------------------------------------------------------*/
57: typedef struct{
58: char section[MAX_SECTION_NUMBER+1];
59: char s_number[MAX_STUDENT_NUMBER+1];
60: char s_name[MAX_STUDENT_NAME+1];
61: char problem_number[MAX_PROBLEM_NUMBER];
62: char problem_score[MAX_PROBLEM_NUMBER];
63: } HGR_record;
64:
65: int GradedProblems=0; /* The number of problems in a HG'd record */
66: int NumberOfUpdates=0; /* The number of records need updated in hgr*.db */
67: int NumberOfProblems=0;
68: char *progname;
69: HGR_record hgr_record[MAX_CLASS_SIZE];
70:
71:
72: /*--------------------------------------------------------------*/
73: /* One line of record in "set*.out". */
74: /*--------------------------------------------------------------*/
75: typedef struct{
76: int valid; /* if a registered student */
77: char s_number[MAX_STUDENT_NUMBER+1]; /* student number */
78: char answer[MAX_PROBLEM_NUMBER]; /* a string of answers */
79: char tries[3*MAX_PROBLEM_NUMBER];
80: } SET_record;
81:
82: char line1[512],line2[512],line3[512]; /* The first three lines in set*.db */
83: int NumberOfStudents=0; /* Number of students in set*.db */
84: int ValidStudents=0; /* No. of Valid students in set*.db */
85: SET_record set_record[MAX_CLASS_SIZE];
86:
87:
88: /*--------------------------------------------------------------*/
89: /* */
90: /* Statistics for the number of tries in "set*.db". */
91: /* */
92: /*--------------------------------------------------------------*/
93: int s[MAX_PROBLEM_NUMBER][MAX_TRIES];
94: int t[MAX_CLASS_SIZE][MAX_PROBLEM_NUMBER];
95: int YesCnt[MAX_PROBLEM_NUMBER];
96: int yesCnt[MAX_PROBLEM_NUMBER];
97: int correct[MAX_PROBLEM_NUMBER];
98: int Weight[MAX_PROBLEM_NUMBER];
99: int TotalTries[MAX_PROBLEM_NUMBER];
100:
101: typedef struct{
102: int total;
103: int score[MAX_PROBLEM_NUMBER];
104: }Student;
105:
106: int GuyNumberOfStudents;
107: Student student[MAX_CLASS_SIZE];
108: #define PARTIAL 0
109: #define NOPARTIAL 1
110: /*--------------------------------------------------------------*/
111: /* */
112: /* */
113: /*--------------------------------------------------------------*/
114: void usage()
115: {
116: printf("USAGE: %s [-s set] [-t LargeTry] [-n NumberLargeTry]\n", progname);
117: }
118:
119:
120:
121: /*--------------------------------------------------------------*/
122: /* */
123: /* Return the maximum score of a problem. */
124: /* */
125: /*--------------------------------------------------------------*/
126: int Problem_Score(score_ptr,problem_number)
127: char *score_ptr;
128: char problem_number;
129: {
130: char *tmp_ptr;
131: char score;
132:
133: tmp_ptr=strchr(score_ptr,problem_number);
134: tmp_ptr=tmp_ptr+2;
135: sscanf(tmp_ptr, "%c", &score);
136: return(score-'0');
137: }
138:
139:
140: /*--------------------------------------------------------------*/
141: /* */
142: /* Check if the list of scores is valid */
143: /* */
144: /*--------------------------------------------------------------*/
145: int Valid_Score(scorelist,score_ptr)
146: char *scorelist;
147: char *score_ptr;
148: {
149: char *problem;
150: int score=-1;
151: int value=1;
152:
153: GradedProblems=0;
154: problem = scorelist;
155: while (*problem == '('){
156: GradedProblems++;
157: problem++;
158: score=Problem_Score(score_ptr,*problem);
159: problem=problem+2;
160: if (*problem != '*'&&((*problem-'0')>score)){
161: value=0;
162: }
163: problem=problem+3;
164: }
165: return(value);
166: }
167:
168: /*--------------------------------------------------------------*/
169: /* */
170: /* Open the hand graded file "./record/set`set`.out", */
171: /* and return the file pointer. */
172: /* */
173: /*--------------------------------------------------------------*/
174: FILE *Open_Read(filename)
175: char filename[FILE_NAME_LENGTH];
176: {
177: FILE *fp;
178:
179: if ((fp=fopen(filename,"r"))==NULL) {
180: printf("Error: can't open %s\n",filename);
181: exit(1);
182: }
183: return(fp);
184: }
185:
186: /*--------------------------------------------------------------*/
187: /* */
188: /* Open the hand graded file "./record/set`set`.out", */
189: /* and return the file pointer. */
190: /* */
191: /*--------------------------------------------------------------*/
192: FILE *Open_Write(filename)
193: char filename[FILE_NAME_LENGTH];
194: {
195: FILE *fp;
196:
197: if ((fp=fopen(filename,"w"))==NULL) {
198: printf("Error: can't open %s\n",filename);
199: exit(1);
200: }
201: return(fp);
202: }
203:
204:
205: int studentCompare(const void *a,const void *b)
206: {
207: Student *voida,*voidb;
208: voida=(Student *)a;
209: voidb=(Student *)b;
210: if (voida->total < voidb->total)
211: return 1;
212: if (voida->total > voidb->total)
213: return -1;
214: return 0;
215: }
216:
217: /*--------------------------------------------------------------*/
218: /* */
219: /* */
220: /*--------------------------------------------------------------*/
221: void Read_Record(fp_set)
222: FILE *fp_set;
223: {
224: int count,i,j;
225: int done,len;
226: int problem;
227: int problem_cnt;
228: char fmtbuf[128];
229: char nextline[512];
230: char temp;
231:
232: fgets(line1,511,fp_set);
233: sscanf(line1,"%d",&problem_cnt);
234:
235: fgets(line2,511,fp_set);
236: len=strlen(line2); /* weight line */
237: for(i=0;i<problem_cnt;i++) {
238: Weight[i] = line2[i] - '0'; /* put weight into a global array */
239: }
240:
241: fgets(line3,511,fp_set); /* get rid of hand grading line */
242: /* here problem_cnt should be the same as NumberOfProblems */
243: NumberOfProblems=strlen(line2)-1;
244: sprintf(fmtbuf,"%%%dc %%%dc%%%dc",
245: MAX_STUDENT_NUMBER, NumberOfProblems+1, 3*NumberOfProblems);
246:
247: done=0;
248: count=0;
249: while (!done){
250: done=!fgets(nextline,511,fp_set);
251: len=strlen(nextline);
252: if (!done){
253: count++;
254: sscanf(nextline, fmtbuf,
255: set_record[count].s_number,
256: set_record[count].answer,
257: set_record[count].tries);
258: /* printf("(%s) (%s) (%s)\n",
259: set_record[count].s_number,
260: set_record[count].answer,
261: set_record[count].tries); */
262: set_record[count].valid=0;
263: for (i=0;i<NumberOfProblems;i++){
264: if (set_record[count].answer[i]!='-')
265: set_record[count].valid=1;
266: }
267: }
268: }
269: NumberOfStudents=count;
270: printf("%d",NumberOfStudents);
271: fclose(fp_set);
272:
273: for(j=0,i=0;i<NumberOfStudents;j++,i++)
274: {
275: for(problem=0;problem<NumberOfProblems;problem++)
276: {
277: temp=set_record[i].answer[problem];
278: switch (temp)
279: {
280: case 'y':
281: case 'Y':
282: student[j].score[problem]=Weight[problem];
283: student[j].total+=Weight[problem];
284: break;
285: case 'n':
286: case 'N':
287: student[j].score[problem]=0;
288: break;
289: default:
290: if (isdigit(temp))
291: {
292: student[j].score[problem]=temp-'0';
293: student[j].total+=student[j].score[problem];
294: }
295: else
296: {
297: student[j].score[problem]=0;
298: student[j].total+=student[j].score[problem];
299: fprintf(stderr,"Invalid score for student ");
300: fprintf(stderr,"%s, problem",set_record[i].s_number);
301: fprintf(stderr,"%d, score == ",problem);
302: fprintf(stderr,"%c skipping them\n",temp);
303: j--;
304: goto skip_student;
305: }
306: }
307: }
308: skip_student: j=j;
309: }
310: GuyNumberOfStudents=j;
311: qsort((char *)student,GuyNumberOfStudents,sizeof(Student),studentCompare);
312: }
313:
314:
315:
316: /*--------------------------------------------------------------*/
317: /* */
318: /* t[i][j]: the number of tries for the `i`th student, the */
319: /* `j`th problem in set*.db file. */
320: /* t[i][*]=set_record[i].tries */
321: /* s[i][j]: the number of students who work on the `i`th */
322: /* problem for `j` tries. */
323: /* */
324: /*--------------------------------------------------------------*/
325: void Sort_By_Tries()
326: {
327: int i,j,try;
328: char *tmp;
329:
330: /* initialization */
331: for (i=0;i<MAX_PROBLEM_NUMBER;i++){
332: YesCnt[i] = 0;
333: yesCnt[i] = 0;
334: correct[i] = 0;
335: TotalTries[i]=0;
336: for (j=0; j<MAX_TRIES; j++){
337: s[i][j]=0;
338: }
339: for (j=0;j<MAX_CLASS_SIZE;j++){
340: t[j][i]=0;
341: }
342: }
343:
344: for (i=0;i<NumberOfStudents;i++){
345: tmp=set_record[i].tries;
346: for (j=0; j<NumberOfProblems; j++){
347: sscanf(tmp+3*j,"%d,",&try);
348: if ((try>=0) && (try <=99)){
349: s[j][try]++;
350: t[i][j]=try;
351: TotalTries[j]=TotalTries[j]+try;
352: }
353: }
354: }
355:
356: }
357:
358:
359: double GetDegreeOfDiscrim(int problem,int mode)
360: {
361: int i,numTopRight=0,numBottomRight=0,twentySevenPercent;
362: double dd;
363:
364: twentySevenPercent=(int)ceil(.27*(double)GuyNumberOfStudents);
365: for(i=0;i<twentySevenPercent;i++)
366: {
367: switch (mode)
368: {
369: case PARTIAL:
370: if (student[i].score[problem] > 0) { numTopRight++; }
371: if (student[GuyNumberOfStudents-i].score[problem]>0){numBottomRight++;}
372: break;
373: case NOPARTIAL:
374: if (student[i].score[problem] == Weight[problem]) { numTopRight++; }
375: if (student[GuyNumberOfStudents-i].score[problem] == Weight[problem])
376: { numBottomRight++; }
377: break;
378: }
379: }
380: dd=(((((double)numTopRight)/((double)twentySevenPercent))-
381: (((double)numBottomRight)/((double)twentySevenPercent))));
382: return 100.0*dd;
383: }
384:
385: double GetDifficulty(int problem, int mode)
386: {
387: int i,numWrong=0;
388: for(i=0;i<GuyNumberOfStudents;i++)
389: {
390: switch(mode)
391: {
392: case PARTIAL:
393: if (student[i].score[problem] == 0) { numWrong++; }
394: break;
395: case NOPARTIAL:
396: if (student[i].score[problem] < Weight[problem]) { numWrong++; }
397: break;
398: }
399: }
400: return (100.0*((double)numWrong/(double)GuyNumberOfStudents));
401: }
402:
403: #define MAXSCORE 58
404: void doScoreHistogram(void)
405: {
406: int hist[MAXSCORE],i,numGoneBy=0;
407: for(i=0;i<MAXSCORE;i++)
408: hist[i]=0;
409: for(i=0;i<GuyNumberOfStudents;i++)
410: {
411: hist[student[i].total]++;
412: }
413: printf("Scr Num Tot\n");
414: for(i=0;i<MAXSCORE;i++)
415: {
416: numGoneBy+=hist[i];
417: printf("%3d %3d %3d\n",i,hist[i],numGoneBy);
418: }
419: }
420:
421: /*--------------------------------------------------------------*/
422: /* */
423: /* This is only used to print out the statistics in s[][], */
424: /* which stores the number of occurences in a form of */
425: /* s[problem_number-1][tries]. This is not normally used. */
426: /* For a global view only. */
427: /* */
428: /*--------------------------------------------------------------*/
429: void Print_moment(avg,sd,sd3,m,students)
430: float avg[MAX_PROBLEM_NUMBER];
431: float sd[MAX_PROBLEM_NUMBER];
432: float sd3[MAX_PROBLEM_NUMBER];
433: int m[MAX_PROBLEM_NUMBER];
434: int students[MAX_PROBLEM_NUMBER];
435: {
436: int i;
437: float dod,disP,disNP,difP,difNP;
438:
439: printf("\nThis is the statistics for each problem:\n");
440: printf("Prob# MxTries avg. s.d. s.k. #Stdnts ");
441: printf(" #Yes #yes Tries DoDiff Dis(P) Dif(P) Dis(NP) Dif(NP)\n");
442: for (i=0;i<NumberOfProblems;i++){
443: dod=1.0-((float)(YesCnt[i]+yesCnt[i]+correct[i])/(float)TotalTries[i]);
444: disP=GetDegreeOfDiscrim(i,PARTIAL);
445: difP=GetDifficulty(i,PARTIAL);
446: disNP=GetDegreeOfDiscrim(i,NOPARTIAL);
447: difNP=GetDifficulty(i,NOPARTIAL);
448: printf("P %2d:",i+1);
449: printf("%7d %8.2f %7.2f %6.2f %5d %5d %5d %5d %5.2f %5.2f %5.2f %6.2f %6.2f",
450: m[i],avg[i],sd[i],sd3[i],students[i],YesCnt[i],yesCnt[i],
451: TotalTries[i],dod,disP,difP,disNP,difNP);
452: printf("\n");
453: }
454: doScoreHistogram();
455: }
456:
457:
458: /*--------------------------------------------------------------*/
459: /* */
460: /* This is only used to print out the statistics in s[][], */
461: /* which stores the number of occurences in a form of */
462: /* s[problem_number-1][tries]. This is not normally used. */
463: /* For a global view only. */
464: /* */
465: /*--------------------------------------------------------------*/
466: void Print_Tries()
467: {
468: int i,j;
469:
470: printf("\nThis is the sumary of tries:\n");
471: for (i=0;i<NumberOfProblems;i++){
472: printf("P %d:",i+1);
473: for (j=0; j<MAX_TRIES; j++)
474: if (s[i][j]!=0)
475: printf("%d( %d) ",j,s[i][j]);
476: printf("\n");
477: }
478: }
479:
480: /*--------------------------------------------------------------*/
481: /* */
482: /* This is only used to print out the statistics in s[][], */
483: /* which stores the number of occurences in a form of */
484: /* s[problem_number-1][tries]. This is not normally used. */
485: /* For a global view only. */
486: /* */
487: /*--------------------------------------------------------------*/
488:
489: void Average_Tries()
490: {
491: float avg[MAX_PROBLEM_NUMBER];
492: float sd[MAX_PROBLEM_NUMBER];
493: float sd3[MAX_PROBLEM_NUMBER];
494: int students[MAX_PROBLEM_NUMBER];
495: int m[MAX_PROBLEM_NUMBER];
496: int i,j;
497: float tmp1,tmp2;
498: float sum;
499:
500: /* max tries for each problem */
501: for (i=0;i<NumberOfProblems;i++){
502: m[i]=0;
503: for (j=0;j<NumberOfStudents;j++){
504: if (t[j][i]>m[i]){
505: m[i]=t[j][i];
506: }
507: }
508: }
509: for (i=0;i<NumberOfStudents;i++){
510: if (set_record[j].valid){
511: ValidStudents++;
512: }
513: }
514:
515: /* first moment */
516: for (i=0;i<NumberOfProblems;i++){
517: avg[i]=0.0; /* initialization */
518: students[i]=0;
519: for (j=1;j<NumberOfStudents;j++){
520: if (set_record[j].valid){
521: avg[i]=avg[i]+t[j][i]; /* sumation actually */
522: students[i]=students[i]+1;
523: }
524: }
525: avg[i]=avg[i]/students[i]; /* real average */
526: }
527:
528: /* second moment */
529: for (i=0;i<NumberOfProblems;i++){
530: sd[i]=0.0;
531: sum=0.0;
532: for (j=0;j<NumberOfStudents;j++){
533: if (set_record[j].valid){
534: tmp1=(float)t[j][i];
535: tmp2=(tmp1-avg[i])*(tmp1-avg[i]);
536: sum=sum+tmp2;
537: }
538: sd[i]=sum/(float)(students[i]-1);
539: sd[i]=sqrt((double) sd[i]);
540: }
541: }
542:
543: /* third moment, skewness */
544: for (i=0;i<NumberOfProblems;i++){
545: sd3[i]=0.0;
546: sum=0.0;
547: for (j=0;j<NumberOfStudents;j++){
548: if (set_record[j].valid){
549: tmp1=(float)t[j][i];
550: tmp2=(tmp1-avg[i])*(tmp1-avg[i])*(tmp1-avg[i]);
551: sum=sum+tmp2;
552: }
553: sd3[i]=sum/(float)(students[i]);
554: sd3[i]=sd3[i]/(sd[i]*sd[i]*sd[i]);
555: }
556: }
557: Print_moment(avg,sd,sd3,m,students); /* print mean, sd, skewness */
558: }
559:
560: /*--------------------------------------------------------------*/
561: /* */
562: /* */
563: /*--------------------------------------------------------------*/
564: void Percentage_Scores(set)
565: int set;
566: {
567: int i,j;
568: int total_weight = 0,
569: total_scores=0;
570: float percentage;
571:
572: for (i=0;i<NumberOfStudents;i++){
573: if (set_record[i].valid){
574: for (j=0;j<NumberOfProblems;j++){
575: total_weight = total_weight+Weight[j];
576: switch (set_record[i].answer[j]){
577: case 'Y': YesCnt[j] = YesCnt[j]+1;
578: total_scores = total_scores + Weight[j];
579: break;
580: case 'y': yesCnt[j] = yesCnt[j]+1;
581: total_scores = total_scores + Weight[j];
582: break;
583: default : if (set_record[i].answer[j] >= '0' &&
584: set_record[i].answer[j] <= '9' ) {
585: correct[i]=correct[i]+1;
586: total_scores=total_scores+(set_record[i].answer[j]-'0');
587: }
588: break;
589: }
590: }
591: }
592: }
593:
594: percentage = (float)total_scores / (float)total_weight;
595: percentage = percentage * 100.0;
596: printf("\nThe percentage score for set%d.db is %7.2f%%\n",set,percentage);
597: /*printf("Total Number of Students in set%d.db is %d\n",set,ValidStudents);*/
598:
599: }
600:
601: /*--------------------------------------------------------------*/
602: /* */
603: /* */
604: /*--------------------------------------------------------------*/
605: void Large_Tries(LargeTry, NumberLargeTry)
606: int LargeTry, NumberLargeTry;
607: {
608: int i,j;
609: int count;
610: int credit; /* Number of credits should be taken off */
611:
612:
613: printf("\nHere is a list of students who have %d tries more than %d times: \n\n", LargeTry, NumberLargeTry);
614:
615: for (i=0;i<NumberOfStudents;i++){
616: count=0;
617: credit=0;
618: for (j=0;j<NumberOfProblems;j++){
619: if (t[i][j]>=LargeTry){
620: count++;
621: if (set_record[i].answer[j]=='Y' ||
622: set_record[i].answer[j]=='y')
623: credit ++;
624: }
625: }
626: if (count >= NumberLargeTry){
627: printf("(%d) %s \n",credit, set_record[i].s_number);
628: printf("%s %s \n", set_record[i].answer, set_record[i].tries);
629: }
630: }
631:
632: }
633:
634: /*--------------------------------------------------------------*/
635: /* */
636: /* */
637: /*--------------------------------------------------------------*/
638: int main(argc,argv)
639: int argc;
640: char *argv[];
641:
642: {
643:
644: int set=1;
645: int inputNotOK=1;
646: int LargeTry=0,NumberLargeTry=0;
647: FILE *fp_set;
648: char set_file[FILE_NAME_LENGTH];
649: char path[FILE_NAME_LENGTH];
650: char filename[FILE_NAME_LENGTH];
651:
652: for( progname = *argv++; --argc; argv++) {
653: if ( argv[0][0] == '-' ) {
654: switch(argv[0][1]) {
655: case 's': set = atoi(argv[1]); break;
656: case 't': LargeTry = atoi(argv[1]); break;
657: case 'n': NumberLargeTry = atoi(argv[1]); break;
658: default: usage(); break;
659: }
660: }
661: }
662:
663: while ( inputNotOK ) {
664: puts("Enter the ABSOLUTE path of class");
665: scanf("%s", path);
666: if( access(path, F_OK) == -1 ) {
667: } else {
668: sprintf(filename,"%s/records",path);
669: if( access(filename, F_OK) == -1 ) {
670: puts("There isn't a records dir in this CLASS directory");
671: puts("Please Specify another calss");
672: } else {
673: inputNotOK = 0;
674: }
675: }
676: }
677: chdir(path);
678: sprintf(set_file, "records/set%d.db",set);
679: /* sprintf(out_file, "records/set%d.out",set); */
680: fp_set=Open_Read(set_file);
681: /* fp_out=Open_Write(out_file); */
682:
683: Read_Record(fp_set);
684:
685: Sort_By_Tries();
686: /* Print_Tries(); */
687: Percentage_Scores(set);
688: Average_Tries();
689:
690: Large_Tries(LargeTry, NumberLargeTry);
691: return 0;
692: }
693:
694:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>