Click to See Complete Forum and Search --> : Accumulating statistics using C++ algorithms


UprightMan
10-22-2001, 12:02 PM
Hey all, I'm stuck on a project for one of my classes, and I thought some of you might be able to help me out.

Project description: we're given an input file which has student information of the form:
student id, section id, variable number quiz grades, variable number of project grades, and variable number of test grades all separated by semicolons.
ie 5555 ; 50000 ; 10 10 10 5 5 ; 50 60 40; 150 ;
4444 ; 50000 ; 10 10 ; 50 40 ; 190 200 ;
....

I have a class that contains a vector of type Student. Student is a struct which contains the data members: stud_id, section_id, vector<double> quizs, vector<double> projects, vector<double> tests.

The class has several public functions that are supposed to calculate various statistics about the given input, i.e. total number of quizes for the class, mean quiz grade, etc.

I have written the methods that reads in the data but I'm stumped as to calculating the statistics because we have to do without explicit looping.
I think if I could see one example on this topic (for instance, how would you use an algorithm to find the total number of quizes for the entire class) that I could knock this project out pretty quickly.

Here is some of the header file

class Grades
{
vector<Student> students;
struct Students
{
int uid;
int sid;
vector<double> quizs;
vector<double> projs;
vector<double> tests;
};
public:
int total_quizs () const;

[snip]
};

[ 22 October 2001: Message edited by: UprightMan ]

bwkaz
10-22-2001, 12:35 PM
For the total number of quizzes, you could add a function to your class called add_quiz or something, that adds one quiz. Then increment an internal counter, and return the counter value whenever someone calls the total_quizzes function.

As for the mean, ummm.... you could keep a private variable, sum_of_quizzes, and then when you're asked for the mean, just return (double)sum_of_quizzes / total_quizzes(). I'd typecast to double to keep more decimal places than int keeps (0 decimals here is probably not the Right Thing). Just make sure quiz_mean() returns a float.

I think you can do the rest yourself.

UprightMan
10-22-2001, 01:34 PM
I'm not quite following you on the the counter. I don't have to worry about quizes being added after I read in all the data. Furthermore, I can't add any fields that are to be passed into the methods. I can only add private methods and/or private function objects.

I think what I need is an algorithm, such as accumulate, that implicitly loops through the quiz<double> vector for each element of vector<Student> counting the number of quizes. Then I'll return this count.

Stuka
10-22-2001, 04:34 PM
umm...for your quiz/project counts, vector has a size() method. Creative use of the for_each() algorithm function could provide you with your mean values...in other words..you've been given the STL, so make use of it!

UprightMan
10-22-2001, 05:06 PM
Right, if I could loop it wouldn't be a problem:
count = 0;
for (int i = 0; i < student.size(); i++)
count += students[i].quizs.size();

then count would be the total amount of quizs, right?

I can't use a loop. I have to do this with the STL algorithms which is what is causing me problems.

Hmm, maybe I could use for_each to to find the totals. Something like for_each vector element of student, I call the size method on whatever I'm totalling (quizs, etc)...I'll tinker with this idea.

/me pulls hair

[ 22 October 2001: Message edited by: UprightMan ]

pinoy
10-23-2001, 01:06 AM
I don't think for_each is very appropriate for this. Why not write accumulate yourself, it will however use a loop. Most <algorithm>'s do.

Such as:

template <class Ret, class Int>
accumulate(InIt begin, InIt end)
{
Ret total = 0;
for (; begin != end; begin++)
total += *begin;
return total;
}

//...
int total = accumulate<int>(grades.begin(), grades.end());

UprightMan
10-23-2001, 04:18 AM
You are right. Accumulate is better. I can't write my own accumulate though.

I did this
:
struct how_many
{
int operator() (int count, Student s)
{ return count += s.quizs.size(); }
};

int total_quizs () const
{
return accumulate(students.begin(), students.end(), 0, how_many());
}

This seems to work. My only problem now is making how_many work with pointers to members so I can use the same how_many to total up projs and tests.

[ 23 October 2001: Message edited by: UprightMan ]

[ 23 October 2001: Message edited by: UprightMan ]

f'lar
10-23-2001, 11:09 AM
From what I gather, the point of this isn't necessarily whether or not it's the best way to do it: the point is to gain experience using the STL, so that when it is the best time to use it you'll know how.

UprightMan
10-23-2001, 11:13 AM
That's precisely the point f'lar.

Now, do you have any ideas on how I would use a pointer to class data members in order generalize my function object? :)

Stuka
10-23-2001, 11:40 AM
OK - I guess I'm a shade confused. Do you need stats for each student, or for the whole vector of students? (Or both?) The methodology is about the same either way, I think. Hmmm...this is a fun one! ;)

UprightMan
10-23-2001, 12:04 PM
I have to gather data for the entire class first. Then I do some things with individual students.

Right now, I'm trying to figure out how to use min_element with a comparison function (represented by a function object) in order to pick out the lowest quiz, proj, test scores for the entire class.

Stuka
10-23-2001, 03:35 PM
That's easy - for_each to step through your vector of students - the function pointer for the for_each should point to a function that compares min_element of the quiz grades with a (static? depends on specifics) variable holding the lowest quiz grade...

UprightMan
10-24-2001, 11:24 AM
I can't have any static variables so I can't think of any way to store the lowest quiz each time for_each steps.

What I was trying is this:

struct min_v
{
double operator()(const Student& s1, const Student& s2)
{
if ( *(std::min_element(s1._quizs.begin(), s1._quizs.end()))
< *(std::min_element(s2._quizs.begin(), s2._quizs.end())))
return *(std::min_element(s1._quizs.begin(), s1._quizs.end()));
else return *(std::min_element(s2._quizs.begin(), s2._quizs.end()));

}
};

double Grades::quizs_minv () const
{
assert(_invariant());
return *std::min_element(_students.begin(), _students.end(), min_v());
}



I think I'm getting a pointer to the student with the lowest score...but I can't figure out how to return that lowest score...

[ 24 October 2001: Message edited by: UprightMan ]