Statistical Measures Application: Grade Calculator
Learn how to create a grade calculator using different statistical measures.
Grade calculator (application program)
Given the students’ information, including their roll numbers, names, and achieved marks, the program does the following:
- It reads the students’ data from a file called
Semester.txt
. - It assigns the grade to each student based on the marks achieved (using the bell curve strategy).
- It counts the total number of each grade received.
- It compiles and saves the result in a new file called
Results.txt
.
What we see in the image above is a bell curve used for grading when we want to assign the grades over a normal distribution. Bell curving is usually used when we want to assign grades close to some specific range (the mean). This way, it conforms more closely to a bell-shaped curve, where most scores cluster around the average and fewer scores are at the extremes.
For this, we first calculate the mean and then assign the grade C to a range around the mean. Then, as seen in the image above, we add half the standard deviation to the mean (0.5 * std deviation + mean) to get the grade B. To assign the grade A, we add one and a half of the standard deviation to the mean (1.5 * std deviation + mean).
Similarly, we subtract half the standard deviation from the mean (-0.5 * std deviation + mean) to get the grade D. For the grade F, we subtract one and a half of the standard deviation from the mean (-1.5 * std deviation + mean).
This sums up the bell-curving method.
Implementation walkthrough
We will discuss all the steps one by one.
Reading the students’ data
Let’s start our program by loading the data using the function loadData()
.
void loadData(const char fname[], string RNs[], string Names[], double Marks[], int &total_records);
Parameters
const char fname[]
: This is the file name where the data exists.string RNs[]
: This stores roll numbers read from the file.string Names[]
: This shows the stored names of the students read from the file.double Marks[]
: This stores the obtained marks of the students read from the file.int &total_records
: This specifies the number of records read from the file.
Implementation Details
- First, the
loadData()
function will open the file specified byfname
for reading. - Then, the first set of characters are read from the opened file that specify the number of records present in the file; therefore, this value is stored inside the
total_records
variable. - Then we loop the
total_records
number of times to read that many lines from the file and process them accordingly.
Let’s first grasp how the data is formatted inside the file to understand how we process the records. Each record corresponds to a single line where we have the following:
- The first nine characters specify the roll number. Alternatively, we could also say that until a non-whitespace character is encountered, all the preceding characters constitute a roll number.
- Then we get several characters (with a mixture of whitespace characters) that denote a student’s name until the
.
character is encountered. - Lastly, we have the obtained marks of the respective student.
Hence, to process this, we’ll do the following inside the loop:
- Use the
>>
operator to read and store the contents from the file into theRNs
array. Since there are now whitespace characters in the roll number, this will work. - Then, we’ll use the
ignore()
function on the reader to ignore the tab character (\t
). - Next, we’ll use the
getline()
function to read the characters (including whitespace characters) until the.
character is encountered and store them inside theName
array. - Lastly, we’ll again use the
>>
operator to read and store the contents from the file into theMarks
array.
The loadData()
function would then be as follows:
void loadData(const char fname[], string RNs[], string Names[], double Marks[], int &total_records){ifstream Rdr(fname);Rdr>>total_records;for(int i=0; i<total_records; i++){Rdr>>RNs[i];Rdr.ignore(); // discards the residual newlinegetline(Rdr, Names[i], '.');Rdr>>Marks[i];}}
Assigning grades
To the compute grades we’ll declare the grades()
function with the following signature:
char grade(double amarks, double mu, double sd);
Parameters
double amarks
: This specifies the obtained marks of a student.double mu
: This specifies the mean of the class.double sd
: This specifies the standard deviation of the class.
Implementation Details
- The function will simply contain
if
conditions that reflect the strategy discussed above for assigning grades to students by utilizing the passed marks, mean, and standard deviation. - When a condition is met, the appropriate grade is returned to the callee.
The above method is invoked inside another function, computeEveryLetterGrade()
, for each student. This function is responsible for computing the mean and standard deviation for the whole class by utilizing the auxiliary functions discussed in the previous lesson, mean()
, variance()
, and standardDeviation()
.
The following is the function’s signature:
void computeEveryLetterGrade(double Marks[], char Gs[], int gradeCounts[], int total_records);
Parameters
double Marks[]
: This contains the obtained marks of the students.char Gs[]
: This contains the obtained grades of the students.int gradeCounts[]
: This contains the count of different student grades.int total_records
: This specifies the total number of records to process.
Implementation Details
- Firstly, we compute the mean and standard deviation by utilizing the
mean()
andstandardDeviation()
functions. - Then, we loop
total_records
number of times and, in each iteration, do the following:- We use the
grade()
function to compute the grade assigned to thei
th student and store it inside theGs
array. - Next, based on the grade obtained by the current student, we increment the corresponding entry of the
gradeCounts
array.- We accomplish this by checking whether the grade is
F
, and only proceed with the following strategy if it isn’t; otherwise, we increment the index4
of the array. - To index the correct grade count to increment, we pass
Gs[ri]-'A'
as the index. Since bothGs[ri]
and'A'
represent characters that, in a subtraction context, are cast toint
, the appropriate index is yielded. The table below provides a dry run for different grades to clarify this:
- We accomplish this by checking whether the grade is
- We use the
Gs[ri] - char |
Gs[ri] - int |
'A' - int |
Gs[ri]-'A' - int |
---|---|---|---|
'A' |
65 |
65 |
65-65 = 0 |
'B' |
66 |
65 |
66-65 = 1 |
'C' |
67 |
65 |
67-65 = 2 |
'D' |
68 |
65 |
68-65 = 3 |
Since
'F'-'A'
would result in70-65
=5
, whereas the index we need is4
, we cater the gradeF
case separately.
char grade(double amarks, double mu, double sd){if(amarks>=sd*1.5+mu){return 'A';}else if(amarks>=sd*0.5+mu){return 'B';}else if(amarks>=sd*(-0.5)+mu){return 'C';}else if(amarks>=sd*(-1.5)+mu){return 'D';}else{return 'F';}}void computeEveryLetterGrade(double Marks[], char Gs[], int gradeCounts[], int total_records){double mu = mean(Marks, total_records);double sd = standardDeviation(Marks, total_records);for(int ri=0; ri<total_records; ri++){Gs[ri] = grade(Marks[ri], mu, sd);if(Gs[ri]!='F')gradeCounts[Gs[ri]-'A']++;elsegradeCounts[4]++; // F Grade, this is only because we do not have any E grade}}
Printing the result
To print the result, we declare the printCompiledResult()
function with the following signature:
void printCompiledResult(string RNs[], string Names[], double Marks[], char Gs[], int gradeCounts[], int total_records);
Parameters
string RNs[]
: This contains the roll numbers of the students.string Names[]
: This contains the students’ names.double Marks[]
: This contains the obtained marks of the students.char Gs[]
: This contains the computed grades of the students.int gradeCounts[]
: This contains the overall grade statistics.int total_records
: This contains the total number of records to display.
Implementation Details
- To print all the records, we simply iterate
total_records
number of times, and in each iteration print thei
th content of theRNs
,Names
,Marks
, andGs
arrays in a formatted manner. - Lastly, we also print the
gradeCounts
array independently, coupled with the grade it represents.
void printCompiledResult(string RNs[], string Names[], double Marks[], char Gs[], int gradeCounts[], int total_records){for(int i=0; i<total_records; i++){cout<<i+1<<".\t"<<RNs[i]<<" "<<left<<setw(23)<<Names[i]<<"\t"<<Marks[i]<<"\t"<< Gs[i] <<endl;}cout << "________________________________________"<<endl;cout << gradeCounts[0]<<" \tAs "<<endl;cout << gradeCounts[1]<<" \tBs "<<endl;cout << gradeCounts[2]<<" \tCs "<<endl;cout << gradeCounts[3]<<" \tDs "<<endl;cout << gradeCounts[4]<<" \tFs "<<endl;}
Saving the result
To save the result, we declare the saveCompiledResult()
function with the following signature:
saveCompiledResult(const char fname[], string RNs[], string Names[], double Marks[], char Gs[], int gradeCounts[], int total_records)
Parameters
const char fname[]
: This contains the name of the file to save the result in.string RNs[]
: This contains the roll numbers of the students.string Names[]
: This contains the students’ names.double Marks[]
: This contains the obtained marks of the students.char Gs[]
: This contains the computed grades of the students.int gradeCounts[]
: This contains the overall grade statistics.int total_records
: This contains the total number of records to save.
Implementation Details
- We first open a file writer for the file name specified in
fname
. - Then, similar to how we displayed data in the
printCompiledResult()
function, we use the same approach to save data to the file. However, to accomplish this, we use the file writer object as opposed tocout
.
void saveCompiledResult(const char fname[], string RNs[], string Names[], double Marks[], char Gs[], int gradeCounts[], int total_records){ofstream Wrt(fname);for(int i=0; i<total_records; i++){Wrt<<i+1<<".\t"<<RNs[i]<<" "<<left<<setw(23)<<Names[i]<<"\t"<<Marks[i]<<"\t"<< Gs[i] <<endl;}Wrt << "________________________________________"<<endl;Wrt << gradeCounts[0]<<" As "<<endl;Wrt << gradeCounts[1]<<" Bs "<<endl;Wrt << gradeCounts[2]<<" Cs "<<endl;Wrt << gradeCounts[3]<<" Ds "<<endl;Wrt << gradeCounts[5]<<" Fs "<<endl;}
Complete implementation
We have provided the complete implementation below for you to run and test. Note that the last line of the main()
function contains a system()
function call. This is done to print the contents written in the Results.txt
file.
38 BSCS17067 Noor Awais. 100 BSCS19012 Muhammad Saad. 80.97 BSCS19025 Amna Akbar. 73.88 BSCS19082 Zain-ul-Abidin. 79.42 BSCS20001 Samee Haider. 95.88 BSCS20002 Umer Khalid. 92.36 BSCS20004 Sarah John. 90.94 BSCS20007 Muhammad Abdul Rehman. 85.17 BSCS20009 Nadeem. 81.62 BSCS20010 Muhammad Nawaz. 96.91 BSCS20013 Uzair Aamir. 85.41 BSCS20014 Muhammad Daniyal. 85.50 BSCS20015 Muhammad Abdullah. 88.47 BSCS20019 Hajirah Awan. 83.30 BSCS20020 Rameesha Fayyaz. 79.22 BSCS20031 Taqi Raza. 88.28 BSCS20032 Sheharyar Kemal. 84.12 BSCS20035 Faizan Aziz. 96.93 BSCS20036 Farrukh Hussain. 92.31 BSCS20037 Abdul Sammad. 61.88 BSCS20041 Muhammad Hasan Masroor. 84.25 BSCS20046 Faizan Imam. 84.33 BSCS20047 Noor Ali. 88.15 BSCS20049 Syeda Nayab Fatima. 74.42 BSCS20054 Ans Nazir. 84.43 BSCS20056 Muhammad Sabooh Taha. 98.31 BSCS20057 AizazUl Haq. 92.22 BSCS20060 Atashia. 82.45 BSCS20063 Fareeha. 84.43 BSCS20064 Usama. 83.47 BSCS20065 Osman Naveed. 86.3 BSCS20066 Ahsan Anwar. 69.05 BSCS20070 Uzair Amin. 90.12 BSCS20072 Ateeq Idrees. 86.60 BSCS20075 Hoorain Zeesahn. 77.10 BSCS20076 Jennifer. 86.73 BSCS20078 Abdullah Mushtaq. 96.68 BSCS20080 Muhammad Areeb Qamar. 80.72
In line 85, gradeCounts[Gs[ri]-'A']++;
is incrementing an element of the gradeCounts[]
array based on the value of the current letter grade. The expression Gs[ri] - 'A'
returns the numeric value of the current letter grade.
In the ASCII table, the letter 'A'
has the value of 65
and 'F'
has the value of 70
. So, when a grade Gs[ri]
is 'A'
, it has a value of 65
, 'B'
has a value of 66
, 'C'
has a value of 67
and so on till 'F'
has a value of 70
.
So, when a grade Gs[ri]
is 'A'
, Gs[ri] - 'A'
evaluates to 0
, 'B'
evaluates to 1
, 'C'
evaluates to 2
, and so on. This allows the code to use the value of the current letter grade to index the corresponding element in the gradeCounts[]
array and increment it.