/* early version of some statistics for CAPA
Copyright (C) 1992-2000 Michigan State University
The CAPA system is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The CAPA system 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the CAPA system; see the file COPYING. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
As a special exception, you have permission to link this program
with the TtH/TtM library and distribute executables, as long as you
follow the requirements of the GNU GPL in regard to all of the
software in the executable aside from TtH/TtM.
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#ifdef __sun
#include <unistd.h>
#endif
#ifdef NeXT
#include <sys/file.h>
#endif
#include <stdlib.h>
/*--------------------------------------------------------------*/
/* Constant Values for "hgr*.out". */
/*--------------------------------------------------------------*/
#define MAX_SECTION_NUMBER 3 /* maximum number of char for section number */
#define MAX_STUDENT_NUMBER 9 /* maximum number of char for student number */
#define MAX_STUDENT_NAME 30 /* maximum number of char for student name */
#define MAX_PROBLEM_NUMBER 150 /* maximum number of problems in a set */
#define FILE_NAME_LENGTH 256 /* maximum length of file name */
#define MAX_CLASS_SIZE 1024 /* maximum number of students in a class */
/*--------------------------------------------------------------*/
/* Constant Values for "set*.db". */
/*--------------------------------------------------------------*/
#define MAX_TRIES 100 /* tries 0..99 */
/*--------------------------------------------------------------*/
/* One line of record in "hgr*.out". */
/*--------------------------------------------------------------*/
typedef struct{
char section[MAX_SECTION_NUMBER+1];
char s_number[MAX_STUDENT_NUMBER+1];
char s_name[MAX_STUDENT_NAME+1];
char problem_number[MAX_PROBLEM_NUMBER];
char problem_score[MAX_PROBLEM_NUMBER];
} HGR_record;
int GradedProblems=0; /* The number of problems in a HG'd record */
int NumberOfUpdates=0; /* The number of records need updated in hgr*.db */
int NumberOfProblems=0;
char *progname;
HGR_record hgr_record[MAX_CLASS_SIZE];
/*--------------------------------------------------------------*/
/* One line of record in "set*.out". */
/*--------------------------------------------------------------*/
typedef struct{
int valid; /* if a registered student */
char s_number[MAX_STUDENT_NUMBER+1]; /* student number */
char answer[MAX_PROBLEM_NUMBER]; /* a string of answers */
char tries[3*MAX_PROBLEM_NUMBER];
} SET_record;
char line1[512],line2[512],line3[512]; /* The first three lines in set*.db */
int NumberOfStudents=0; /* Number of students in set*.db */
int ValidStudents=0; /* No. of Valid students in set*.db */
SET_record set_record[MAX_CLASS_SIZE];
/*--------------------------------------------------------------*/
/* */
/* Statistics for the number of tries in "set*.db". */
/* */
/*--------------------------------------------------------------*/
int s[MAX_PROBLEM_NUMBER][MAX_TRIES];
int t[MAX_CLASS_SIZE][MAX_PROBLEM_NUMBER];
int YesCnt[MAX_PROBLEM_NUMBER];
int yesCnt[MAX_PROBLEM_NUMBER];
int correct[MAX_PROBLEM_NUMBER];
int Weight[MAX_PROBLEM_NUMBER];
int TotalTries[MAX_PROBLEM_NUMBER];
typedef struct{
int total;
int score[MAX_PROBLEM_NUMBER];
}Student;
int GuyNumberOfStudents;
Student student[MAX_CLASS_SIZE];
#define PARTIAL 0
#define NOPARTIAL 1
/*--------------------------------------------------------------*/
/* */
/* */
/*--------------------------------------------------------------*/
void usage()
{
printf("USAGE: %s [-s set] [-t LargeTry] [-n NumberLargeTry]\n", progname);
}
/*--------------------------------------------------------------*/
/* */
/* Return the maximum score of a problem. */
/* */
/*--------------------------------------------------------------*/
int Problem_Score(score_ptr,problem_number)
char *score_ptr;
char problem_number;
{
char *tmp_ptr;
char score;
tmp_ptr=strchr(score_ptr,problem_number);
tmp_ptr=tmp_ptr+2;
sscanf(tmp_ptr, "%c", &score);
return(score-'0');
}
/*--------------------------------------------------------------*/
/* */
/* Check if the list of scores is valid */
/* */
/*--------------------------------------------------------------*/
int Valid_Score(scorelist,score_ptr)
char *scorelist;
char *score_ptr;
{
char *problem;
int score=-1;
int value=1;
GradedProblems=0;
problem = scorelist;
while (*problem == '('){
GradedProblems++;
problem++;
score=Problem_Score(score_ptr,*problem);
problem=problem+2;
if (*problem != '*'&&((*problem-'0')>score)){
value=0;
}
problem=problem+3;
}
return(value);
}
/*--------------------------------------------------------------*/
/* */
/* Open the hand graded file "./record/set`set`.out", */
/* and return the file pointer. */
/* */
/*--------------------------------------------------------------*/
FILE *Open_Read(filename)
char filename[FILE_NAME_LENGTH];
{
FILE *fp;
if ((fp=fopen(filename,"r"))==NULL) {
printf("Error: can't open %s\n",filename);
exit(1);
}
return(fp);
}
/*--------------------------------------------------------------*/
/* */
/* Open the hand graded file "./record/set`set`.out", */
/* and return the file pointer. */
/* */
/*--------------------------------------------------------------*/
FILE *Open_Write(filename)
char filename[FILE_NAME_LENGTH];
{
FILE *fp;
if ((fp=fopen(filename,"w"))==NULL) {
printf("Error: can't open %s\n",filename);
exit(1);
}
return(fp);
}
int studentCompare(const void *a,const void *b)
{
Student *voida,*voidb;
voida=(Student *)a;
voidb=(Student *)b;
if (voida->total < voidb->total)
return 1;
if (voida->total > voidb->total)
return -1;
return 0;
}
/*--------------------------------------------------------------*/
/* */
/* */
/*--------------------------------------------------------------*/
void Read_Record(fp_set)
FILE *fp_set;
{
int count,i,j;
int done,len;
int problem;
int problem_cnt;
char fmtbuf[128];
char nextline[512];
char temp;
fgets(line1,511,fp_set);
sscanf(line1,"%d",&problem_cnt);
fgets(line2,511,fp_set);
len=strlen(line2); /* weight line */
for(i=0;i<problem_cnt;i++) {
Weight[i] = line2[i] - '0'; /* put weight into a global array */
}
fgets(line3,511,fp_set); /* get rid of hand grading line */
/* here problem_cnt should be the same as NumberOfProblems */
NumberOfProblems=strlen(line2)-1;
sprintf(fmtbuf,"%%%dc %%%dc%%%dc",
MAX_STUDENT_NUMBER, NumberOfProblems+1, 3*NumberOfProblems);
done=0;
count=0;
while (!done){
done=!fgets(nextline,511,fp_set);
len=strlen(nextline);
if (!done){
count++;
sscanf(nextline, fmtbuf,
set_record[count].s_number,
set_record[count].answer,
set_record[count].tries);
/* printf("(%s) (%s) (%s)\n",
set_record[count].s_number,
set_record[count].answer,
set_record[count].tries); */
set_record[count].valid=0;
for (i=0;i<NumberOfProblems;i++){
if (set_record[count].answer[i]!='-')
set_record[count].valid=1;
}
}
}
NumberOfStudents=count;
printf("%d",NumberOfStudents);
fclose(fp_set);
for(j=0,i=0;i<NumberOfStudents;j++,i++)
{
for(problem=0;problem<NumberOfProblems;problem++)
{
temp=set_record[i].answer[problem];
switch (temp)
{
case 'y':
case 'Y':
student[j].score[problem]=Weight[problem];
student[j].total+=Weight[problem];
break;
case 'n':
case 'N':
student[j].score[problem]=0;
break;
default:
if (isdigit(temp))
{
student[j].score[problem]=temp-'0';
student[j].total+=student[j].score[problem];
}
else
{
student[j].score[problem]=0;
student[j].total+=student[j].score[problem];
fprintf(stderr,"Invalid score for student ");
fprintf(stderr,"%s, problem",set_record[i].s_number);
fprintf(stderr,"%d, score == ",problem);
fprintf(stderr,"%c skipping them\n",temp);
j--;
goto skip_student;
}
}
}
skip_student: j=j;
}
GuyNumberOfStudents=j;
qsort((char *)student,GuyNumberOfStudents,sizeof(Student),studentCompare);
}
/*--------------------------------------------------------------*/
/* */
/* t[i][j]: the number of tries for the `i`th student, the */
/* `j`th problem in set*.db file. */
/* t[i][*]=set_record[i].tries */
/* s[i][j]: the number of students who work on the `i`th */
/* problem for `j` tries. */
/* */
/*--------------------------------------------------------------*/
void Sort_By_Tries()
{
int i,j,try;
char *tmp;
/* initialization */
for (i=0;i<MAX_PROBLEM_NUMBER;i++){
YesCnt[i] = 0;
yesCnt[i] = 0;
correct[i] = 0;
TotalTries[i]=0;
for (j=0; j<MAX_TRIES; j++){
s[i][j]=0;
}
for (j=0;j<MAX_CLASS_SIZE;j++){
t[j][i]=0;
}
}
for (i=0;i<NumberOfStudents;i++){
tmp=set_record[i].tries;
for (j=0; j<NumberOfProblems; j++){
sscanf(tmp+3*j,"%d,",&try);
if ((try>=0) && (try <=99)){
s[j][try]++;
t[i][j]=try;
TotalTries[j]=TotalTries[j]+try;
}
}
}
}
double GetDegreeOfDiscrim(int problem,int mode)
{
int i,numTopRight=0,numBottomRight=0,twentySevenPercent;
double dd;
twentySevenPercent=(int)ceil(.27*(double)GuyNumberOfStudents);
for(i=0;i<twentySevenPercent;i++)
{
switch (mode)
{
case PARTIAL:
if (student[i].score[problem] > 0) { numTopRight++; }
if (student[GuyNumberOfStudents-i].score[problem]>0){numBottomRight++;}
break;
case NOPARTIAL:
if (student[i].score[problem] == Weight[problem]) { numTopRight++; }
if (student[GuyNumberOfStudents-i].score[problem] == Weight[problem])
{ numBottomRight++; }
break;
}
}
dd=(((((double)numTopRight)/((double)twentySevenPercent))-
(((double)numBottomRight)/((double)twentySevenPercent))));
return 100.0*dd;
}
double GetDifficulty(int problem, int mode)
{
int i,numWrong=0;
for(i=0;i<GuyNumberOfStudents;i++)
{
switch(mode)
{
case PARTIAL:
if (student[i].score[problem] == 0) { numWrong++; }
break;
case NOPARTIAL:
if (student[i].score[problem] < Weight[problem]) { numWrong++; }
break;
}
}
return (100.0*((double)numWrong/(double)GuyNumberOfStudents));
}
#define MAXSCORE 58
void doScoreHistogram(void)
{
int hist[MAXSCORE],i,numGoneBy=0;
for(i=0;i<MAXSCORE;i++)
hist[i]=0;
for(i=0;i<GuyNumberOfStudents;i++)
{
hist[student[i].total]++;
}
printf("Scr Num Tot\n");
for(i=0;i<MAXSCORE;i++)
{
numGoneBy+=hist[i];
printf("%3d %3d %3d\n",i,hist[i],numGoneBy);
}
}
/*--------------------------------------------------------------*/
/* */
/* This is only used to print out the statistics in s[][], */
/* which stores the number of occurences in a form of */
/* s[problem_number-1][tries]. This is not normally used. */
/* For a global view only. */
/* */
/*--------------------------------------------------------------*/
void Print_moment(avg,sd,sd3,m,students)
float avg[MAX_PROBLEM_NUMBER];
float sd[MAX_PROBLEM_NUMBER];
float sd3[MAX_PROBLEM_NUMBER];
int m[MAX_PROBLEM_NUMBER];
int students[MAX_PROBLEM_NUMBER];
{
int i;
float dod,disP,disNP,difP,difNP;
printf("\nThis is the statistics for each problem:\n");
printf("Prob# MxTries avg. s.d. s.k. #Stdnts ");
printf(" #Yes #yes Tries DoDiff Dis(P) Dif(P) Dis(NP) Dif(NP)\n");
for (i=0;i<NumberOfProblems;i++){
dod=1.0-((float)(YesCnt[i]+yesCnt[i]+correct[i])/(float)TotalTries[i]);
disP=GetDegreeOfDiscrim(i,PARTIAL);
difP=GetDifficulty(i,PARTIAL);
disNP=GetDegreeOfDiscrim(i,NOPARTIAL);
difNP=GetDifficulty(i,NOPARTIAL);
printf("P %2d:",i+1);
printf("%7d %8.2f %7.2f %6.2f %5d %5d %5d %5d %5.2f %5.2f %5.2f %6.2f %6.2f",
m[i],avg[i],sd[i],sd3[i],students[i],YesCnt[i],yesCnt[i],
TotalTries[i],dod,disP,difP,disNP,difNP);
printf("\n");
}
doScoreHistogram();
}
/*--------------------------------------------------------------*/
/* */
/* This is only used to print out the statistics in s[][], */
/* which stores the number of occurences in a form of */
/* s[problem_number-1][tries]. This is not normally used. */
/* For a global view only. */
/* */
/*--------------------------------------------------------------*/
void Print_Tries()
{
int i,j;
printf("\nThis is the sumary of tries:\n");
for (i=0;i<NumberOfProblems;i++){
printf("P %d:",i+1);
for (j=0; j<MAX_TRIES; j++)
if (s[i][j]!=0)
printf("%d( %d) ",j,s[i][j]);
printf("\n");
}
}
/*--------------------------------------------------------------*/
/* */
/* This is only used to print out the statistics in s[][], */
/* which stores the number of occurences in a form of */
/* s[problem_number-1][tries]. This is not normally used. */
/* For a global view only. */
/* */
/*--------------------------------------------------------------*/
void Average_Tries()
{
float avg[MAX_PROBLEM_NUMBER];
float sd[MAX_PROBLEM_NUMBER];
float sd3[MAX_PROBLEM_NUMBER];
int students[MAX_PROBLEM_NUMBER];
int m[MAX_PROBLEM_NUMBER];
int i,j;
float tmp1,tmp2;
float sum;
/* max tries for each problem */
for (i=0;i<NumberOfProblems;i++){
m[i]=0;
for (j=0;j<NumberOfStudents;j++){
if (t[j][i]>m[i]){
m[i]=t[j][i];
}
}
}
for (i=0;i<NumberOfStudents;i++){
if (set_record[j].valid){
ValidStudents++;
}
}
/* first moment */
for (i=0;i<NumberOfProblems;i++){
avg[i]=0.0; /* initialization */
students[i]=0;
for (j=1;j<NumberOfStudents;j++){
if (set_record[j].valid){
avg[i]=avg[i]+t[j][i]; /* sumation actually */
students[i]=students[i]+1;
}
}
avg[i]=avg[i]/students[i]; /* real average */
}
/* second moment */
for (i=0;i<NumberOfProblems;i++){
sd[i]=0.0;
sum=0.0;
for (j=0;j<NumberOfStudents;j++){
if (set_record[j].valid){
tmp1=(float)t[j][i];
tmp2=(tmp1-avg[i])*(tmp1-avg[i]);
sum=sum+tmp2;
}
sd[i]=sum/(float)(students[i]-1);
sd[i]=sqrt((double) sd[i]);
}
}
/* third moment, skewness */
for (i=0;i<NumberOfProblems;i++){
sd3[i]=0.0;
sum=0.0;
for (j=0;j<NumberOfStudents;j++){
if (set_record[j].valid){
tmp1=(float)t[j][i];
tmp2=(tmp1-avg[i])*(tmp1-avg[i])*(tmp1-avg[i]);
sum=sum+tmp2;
}
sd3[i]=sum/(float)(students[i]);
sd3[i]=sd3[i]/(sd[i]*sd[i]*sd[i]);
}
}
Print_moment(avg,sd,sd3,m,students); /* print mean, sd, skewness */
}
/*--------------------------------------------------------------*/
/* */
/* */
/*--------------------------------------------------------------*/
void Percentage_Scores(set)
int set;
{
int i,j;
int total_weight = 0,
total_scores=0;
float percentage;
for (i=0;i<NumberOfStudents;i++){
if (set_record[i].valid){
for (j=0;j<NumberOfProblems;j++){
total_weight = total_weight+Weight[j];
switch (set_record[i].answer[j]){
case 'Y': YesCnt[j] = YesCnt[j]+1;
total_scores = total_scores + Weight[j];
break;
case 'y': yesCnt[j] = yesCnt[j]+1;
total_scores = total_scores + Weight[j];
break;
default : if (set_record[i].answer[j] >= '0' &&
set_record[i].answer[j] <= '9' ) {
correct[i]=correct[i]+1;
total_scores=total_scores+(set_record[i].answer[j]-'0');
}
break;
}
}
}
}
percentage = (float)total_scores / (float)total_weight;
percentage = percentage * 100.0;
printf("\nThe percentage score for set%d.db is %7.2f%%\n",set,percentage);
/*printf("Total Number of Students in set%d.db is %d\n",set,ValidStudents);*/
}
/*--------------------------------------------------------------*/
/* */
/* */
/*--------------------------------------------------------------*/
void Large_Tries(LargeTry, NumberLargeTry)
int LargeTry, NumberLargeTry;
{
int i,j;
int count;
int credit; /* Number of credits should be taken off */
printf("\nHere is a list of students who have %d tries more than %d times: \n\n", LargeTry, NumberLargeTry);
for (i=0;i<NumberOfStudents;i++){
count=0;
credit=0;
for (j=0;j<NumberOfProblems;j++){
if (t[i][j]>=LargeTry){
count++;
if (set_record[i].answer[j]=='Y' ||
set_record[i].answer[j]=='y')
credit ++;
}
}
if (count >= NumberLargeTry){
printf("(%d) %s \n",credit, set_record[i].s_number);
printf("%s %s \n", set_record[i].answer, set_record[i].tries);
}
}
}
/*--------------------------------------------------------------*/
/* */
/* */
/*--------------------------------------------------------------*/
int main(argc,argv)
int argc;
char *argv[];
{
int set=1;
int inputNotOK=1;
int LargeTry=0,NumberLargeTry=0;
FILE *fp_set;
char set_file[FILE_NAME_LENGTH];
char path[FILE_NAME_LENGTH];
char filename[FILE_NAME_LENGTH];
for( progname = *argv++; --argc; argv++) {
if ( argv[0][0] == '-' ) {
switch(argv[0][1]) {
case 's': set = atoi(argv[1]); break;
case 't': LargeTry = atoi(argv[1]); break;
case 'n': NumberLargeTry = atoi(argv[1]); break;
default: usage(); break;
}
}
}
while ( inputNotOK ) {
puts("Enter the ABSOLUTE path of class");
scanf("%s", path);
if( access(path, F_OK) == -1 ) {
} else {
sprintf(filename,"%s/records",path);
if( access(filename, F_OK) == -1 ) {
puts("There isn't a records dir in this CLASS directory");
puts("Please Specify another calss");
} else {
inputNotOK = 0;
}
}
}
chdir(path);
sprintf(set_file, "records/set%d.db",set);
/* sprintf(out_file, "records/set%d.out",set); */
fp_set=Open_Read(set_file);
/* fp_out=Open_Write(out_file); */
Read_Record(fp_set);
Sort_By_Tries();
/* Print_Tries(); */
Percentage_Scores(set);
Average_Tries();
Large_Tries(LargeTry, NumberLargeTry);
return 0;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>