# Comparison problem

For VK33:

There were actually 2 things i needed:

Method1:

The first was simply to calculate the average rating given to every cd.

Method2:

The second was to calculate a users average rating.

Step 1 Then for user 1, cd 1, take the average rating from the rating given to cd1.
Step 2 Do this for user2 as well.
Step 3 Then mulitply these 2 values together.

This will give total1

Repeat step1, only this time square the result
Repeat step2, only this time square the result
Repeat step3
Step 4 - Get the square root of this value

This will give total2

Divide total1 by total2

Do this for every cd that both users have rated and add all the values together

LVL 2
###### Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Commented:
override_y2k.  Can you post a link to the other question that you are referring to.  This is a collaborative site.  For a variety of reasons (not least of which is that you didn't mention vk33 in the question title ;-)), vk33 may not see this.  Someone else may be able to help ;-)

Thanx.
0
Commented:
0
Author Commented:
Oh sorry jimmack.The link has been provided by JakobA.
0
Commented:
Hi!

1. Calculating average cd's rating:

// first we accumulate rating sums here, then we'll divide them by number of rating records
HashMap averageRatings = new HashMap();
// number of rating records
HashMap numRatings = new HashMap();

// getting sums and rating numbers...
Iterator iter = userArrayList.iterator();
while (iter.hasNext()) {
User user = (User)iter.next();
Iterator iter2 = user.getpairArray().iterator();
while (iter2.hasNext()) {
Pair pair = (Pair)iter2.next();
Float average = (Float)averageRatings.get(new Integer(pair.getId()));
if (average == null) {
averageRatings.put(new Integer(pair.getId()), new Float(pair.getRatings()));
} else {
averageRatings.put(new Integer(pair.getId()), new Float(pair.getRatings() + average.floatValue()));
}
Integer num = (Integer)numRatings.get(new Integer(pair.getId()));
if (num == null) {
numRatings.put(new Integer(pair.getId()), new Integer(1));
} else {
numRatings.put(new Integer(pair.getId()), new Integer(num + 1));
}
}
}
iter = averageRatings.keySet().iterator();

// dividing sums by rating numbers...
while (iter.hasNext()) {
Integer id = (Integer)iter.next();
float sum = ((Float)averageRatings.get(id)).floatValue();
int num = ((Integer)numRatings.get(id)).intValue();
averageRatings.put(id,new Float(sum/num));
}

That's it. You have your average ratings in the averageRatings map. To display the results do the following:

Iterator iter = averageRatings.keySet().iterator();
while (iter.hasNext()) {
Integer id = (Integer)iter.next();
Float average = (Float)averageRatings.get(id);
System.out.println (id.intValue() + ": " + average.floatValue());
}

Regards!
0
Commented:
2. The second task looks very much similar to the one given before in the previous topic. So,  only some changes needed. Ok, let's assume that you've done calculating average ratings for all cd's before...

private static double compareUsers(User user1, User user2) {
double sum = 0;
Iterator iter = user1.getpairArray().iterator();
while (iter.hasNext()) {
Pair pair1 = (Pair)iter.next();
Iterator iter2 = user2.getpairArray().iterator();
while (iter2.hasNext()) {
Pair pair2 = (Pair)iter.next();
if (pair1.getId() == pair2.getId()) {

// Step 1-1: getting average rating for cd1
float average1 = ((Float)averageRatings.get(new Integer(pair1.getId()))).floatValue();

// Step 1-2: getting average rating for cd2
float average2 = ((Float)averageRatings.get(new Integer(pair2.getId()))).floatValue();

// Step 1-3: multiplying -> total1
double total1 = average1*average2;

// Step 2-1: getting (average rating for cd1)^2
double average1sqr = average1*average1;

// Step 2-2: getting (average rating for cd2)^2
double average2sqr = averate2 * average2;

// Step 2-3: multiplying, getting square root -> total2
double total2 = Math.sqrt(average1sqr*average2sqr);

// Step 3: sum += total1/total2
sum += total1/total2;
}
}
}
return sum;
}

This method processes all the cd's rated by both users. If you need to process every user with every other one, just add the process method from the previous topic, it'll work without changes.

I tried to make comments all along the code. But if you have any questions - feel free to ask.

Regards!
0
Author Commented:
Just a small change required:

In Step 1.1 the users average rating is the average in question.So the average rating a user gave to a cd should be calculated and subtracted from the rating they gave to a cd.Its not actually the average cd rating that is being taken away.That is just a different part of the problem.

So for example:

User Rating
1       2
1       3
1       1
...............

Users average rating is 2 and then it'll be 2-2, 2-3, 2-1 ......

Thx!
0
Author Commented:
You were dying to get that HashMap in werent you! :)
0
Author Commented:
Also, it would be preferable not to use a hashmap when calculating a users average rating.

Thx for the help.
0
Commented:
There is a simple formula for calculating a running average:

newAverage =  ( newCount + ( oldNrOfCounts * oldAverage ) ) / newNrOfCounts;
0
Author Commented:
Erm yes i have no problem in working out an average.My problem is iterating through the ArrayList of users searching for a given cd and extracting its rating, then looping throuh every user and checking if they have rated it and then repeating this process for every cd.

That is the problem.
0
Author Commented:
So this is the order in which things should occur, a slight change of your step by step:

private static double compareUsers(User user1, User user2) {
double sum = 0;
Iterator iter = user1.getpairArray().iterator();
while (iter.hasNext()) {
Pair pair1 = (Pair)iter.next();
Iterator iter2 = user2.getpairArray().iterator();
while (iter2.hasNext()) {
Pair pair2 = (Pair)iter.next();
if (pair1.getId() == pair2.getId()) {

// Step 1-1: get average rating for user1
// Step 1-2: subtract this from the rating given by user1 to cd1 -> total1

// Step 1-3: get average rating for user2
// Step 1-4: subtract this from the rating given by user2 to cd1 -> total2

// Step 1-5: multiplying -> total3
//double total3 = total1*total2;

// Step 2-1: getting average rating for user1
// Step 2-2: subtract this from the rating given by user1 to cd1 -> total4
// Step 2-3: square this value

// Step 2-4: getting average rating for user2
// Step 2-5: subtract this from the rating given by user2 to cd1 -> total5
// Step 2-6: square this value

// Step 2-7: multiplying, getting square root -> total2
//double total6 = Math.sqrt(total4*total5);

// Step 3: sum += total3/total6
//sum += total3/total6;

//Repeat this for every cd both users have rated
//Repeat this for every user
}
}
}
return sum;
}
0
Commented:
Hi!

1. To get the average rating for specified user:

private static double averageRating(User user) {
double sum = 0;
int num = 0;
Iterator iter = user.getpairArray().iterator();
while (iter.hasNext()) {
Pair tmp = (Pair)iter.next();
sum += tmp.getRatings();
num++;
}
if (num == 0)
return 0;
return sum/num;
}

2. Changes in the compareUsers() method:

private static double compareUsers(User user1, User user2) {
double sum = 0;
Iterator iter = user1.getpairArray().iterator();
while (iter.hasNext()) {
Pair pair1 = (Pair)iter.next();
Iterator iter2 = user2.getpairArray().iterator();
while (iter2.hasNext()) {
Pair pair2 = (Pair)iter.next();
if (pair1.getId() == pair2.getId()) {

// Step 1-1: get average rating for user1
double average1 = averageRating(user1);

// Step 1-2: subtract this from the rating given by user1 to cd1 -> total1
double total1 = pair1.getRatings() - average1;

// Step 1-3: get average rating for user2
double average2 = averageRating(user2);

// Step 1-4: subtract this from the rating given by user2 to cd1 -> total2
double total2 = pair2.getRatings() - average2;

// Step 1-5: multiplying -> total3
double total3 = total1*total2;

// Step 2-1: getting average rating for user1
// Step 2-2: subtract this from the rating given by user1 to cd1 -> total4
// Step 2-3: square this value
double total4 = total1*total1;

// Step 2-4: getting average rating for user2
// Step 2-5: subtract this from the rating given by user2 to cd1 -> total5
// Step 2-6: square this value
double total5 = total2*total2;

// Step 2-7: multiplying, getting square root -> total6
double total6 = Math.sqrt(total4*total5);

// Step 3: sum += total3/total6
sum += total3/total6;
}
}
}
return sum;
}

3. So, this processing is already repeated for all the cd's both users have rated (see the nested while-loops). To apply this to all the users call the process() method. It's only mission is iterating through all possible pairs of users and calling compareUsers() method for them:

public static double[][] process(ArrayList userArrayList) {
Object[] users = userArrayList.toArray();
double[][] users_diffs = new double[users.length-1][users.length-1];
for (int i=0; i<users.length-1; i++)
for (int j=i+1; j<users.length; j++) {
User user1 = (User)users[i];
User user2 = (User)users[j];
users_diffs[i][j] = compareUsers(user1,user2);
}
return users_diffs;
}

4. As for the HashMap... Hmm, a good programmer is a lazy programmer. :) I just simplify the code. The ONLY purpose of my using HashMap here is simplifying futher access to the results. And one more thing. Look, I need the number of records processed for every cd, right? Then I'll divide the sum by this number and get the average. OK, to keep this numbers for all the cd's I need either new class (another pair, but cd->num) or a HashMap which is actually a set of pairs too. Of course there's one more workaround: 2 arrayLists (one for cdId's and one for numbers). But isn't it too complicated for such a simple task? I don't really understand why not using the provided functionality, especially if we do not change anything in the interface between your code and mine. :)

Best regards!
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Author Commented:
Ok, i just have 1 more silly question for you and that's how where will i put the hashmap into my fileparser class.

I've awarded the points already, but if you could tell me that would be great, as i'm not exactly an expert when it come to hashmaps and hashtables :)

BTW: Point taken bout lazy programming :)
0
Commented:
Well, HashMap is not actually very different from ArrayList. The only difference is that it has two associated lists - keys and values. When you put a new record you specify both key and value: HashMap.put(Object key, Object value). When you get the value back, you just specify the key: HashMap.get(Object key). And you can iterate through both keys and values as if they were separate collections: HashMap.keySet().iterator() and HashMap.values().iterator();

But I don't see any HashMaps left in the last release (the accepted answer)... :)

Best regards!
0
Author Commented:
There were  none in that , put i did still need a mhod for calculating the average rating given to every cd(as well as a users average rating).

Thats why i was looking at your average method using hashmaps
0
Commented:
ok, no problem, define a HashMap in your parser class and create a private method for calculating cd-s averages:

class Parser {
private HashMap averageRatings = new HashMap();
private userArrayList = new ArrayList();
...

private calculateAverageRatings() {
// number of rating records
HashMap numRatings = new HashMap();

// getting sums and rating numbers...
Iterator iter = userArrayList.iterator();
while (iter.hasNext()) {
User user = (User)iter.next();
Iterator iter2 = user.getpairArray().iterator();
while (iter2.hasNext()) {
Pair pair = (Pair)iter2.next();
Float average = (Float)averageRatings.get(new Integer(pair.getId()));
if (average == null) {
averageRatings.put(new Integer(pair.getId()), new Float(pair.getRatings()));
} else {
averageRatings.put(new Integer(pair.getId()), new Float(pair.getRatings() + average.floatValue()));
}
Integer num = (Integer)numRatings.get(new Integer(pair.getId()));
if (num == null) {
numRatings.put(new Integer(pair.getId()), new Integer(1));
} else {
numRatings.put(new Integer(pair.getId()), new Integer(num + 1));
}
}
}
iter = averageRatings.keySet().iterator();

// dividing sums by rating numbers...
while (iter.hasNext()) {
Integer id = (Integer)iter.next();
float sum = ((Float)averageRatings.get(id)).floatValue();
int num = ((Integer)numRatings.get(id)).intValue();
averageRatings.put(id,new Float(sum/num));
}
}

That's it. Your averageRatings hashmap is referenced from the class and thus you can use this reference in your methods. Remember that you need to do the same with your userArrayList. But you can also pass it as an argument of calculateAverageRatings() method.

Regards!
0
Author Commented:
When i layout the code like this, it throws up errors

class FileParser {
public static void main(String[] args) throws Exception {

private HashMap averageRatings = new HashMap();
private userArrayList = new ArrayList();

new FileReader("C:/dataset.txt"); // to open the file
BufferedReader inFile = new BufferedReader(fr);     // to read the file
String line;     // String variable used to hold each line of the file
StringTokenizer tokenizer;     // string tokenizer to parse each file line
ArrayList userArrayList = new ArrayList();
int id = 0;
int index = 0;     // Index that we increment each time through the loop
User user = null;
int movieId;     // value to hold movie id
float ratings;     // value to hold movie ratings
ArrayList pair = null;
while((line = inFile.readLine()) != null) {
tokenizer = new StringTokenizer(line);     // tokenize the file line
// now add the user id to the user id array
id = Integer.parseInt(tokenizer.nextToken());
// initialize the movie id variable
movieId = Integer.parseInt(tokenizer.nextToken());
// initialize the cd ratings variable
ratings = Float.parseFloat(tokenizer.nextToken());
// Check out if the user has already been added to the list
Iterator iter = userArrayList.iterator();

while (iter.hasNext()) {
User tmp = (User)iter.next();
if (tmp.getUserId() == id) {
user = tmp;
break;
}
}

if (user == null) {
pair = new ArrayList();
user = new User(id,pair);
} else

// incrememnt index
index++;
}

}

private calculateAverageRatings() {
// number of rating records
HashMap numRatings = new HashMap();

// getting sums and rating numbers...
Iterator iter = userArrayList.iterator();
while (iter.hasNext()) {
User user = (User)iter.next();
Iterator iter2 = user.getpairArray().iterator();
while (iter2.hasNext()) {
Pair pair = (Pair)iter2.next();
Float average = (Float)averageRatings.get(new Integer(pair.getId()));
if (average == null) {
averageRatings.put(new Integer(pair.getId()), new Float(pair.getRatings()));
} else {
averageRatings.put(new Integer(pair.getId()), new Float(pair.getRatings() + average.floatValue()));
}
Integer num = (Integer)numRatings.get(new Integer(pair.getId()));
if (num == null) {
numRatings.put(new Integer(pair.getId()), new Integer(1));
} else {
numRatings.put(new Integer(pair.getId()), new Integer(num + 1));
}
}
}
iter = averageRatings.keySet().iterator();

// dividing sums by rating numbers...
while (iter.hasNext()) {
Integer id = (Integer)iter.next();
float sum = ((Float)averageRatings.get(id)).floatValue();
int num = ((Integer)numRatings.get(id)).intValue();
averageRatings.put(id,new Float(sum/num));
}
}

"FileParser.java": Error #: 215 : invalid method declaration; return type required at line 84, column 36

The calculateAverage Ratings method is looking for a return type. I'm unsure on how to fix this error.
0
Author Commented:
Should i be returning sum/num?
0
Commented:
your averageRatings and userArrayList should be defined in the class, not in the main method:

class FileParser {
private HashMap averageRatings = new HashMap();
private userArrayList = new ArrayList();

public static void main(String[] args) throws Exception {
new FileReader("C:/dataset.txt"); // to open the file
BufferedReader inFile = new BufferedReader(fr);     // to read the file
String line;     // String variable used to hold each line of the file
StringTokenizer tokenizer;     // string tokenizer to parse each file line
int id = 0;
int index = 0;     // Index that we increment each time through the loop
User user = null;
int movieId;     // value to hold movie id
float ratings;     // value to hold movie ratings
ArrayList pair = null;
while((line = inFile.readLine()) != null) {
tokenizer = new StringTokenizer(line);     // tokenize the file line
// now add the user id to the user id array
id = Integer.parseInt(tokenizer.nextToken());
// initialize the movie id variable
movieId = Integer.parseInt(tokenizer.nextToken());
// initialize the cd ratings variable
ratings = Float.parseFloat(tokenizer.nextToken());
// Check out if the user has already been added to the list
Iterator iter = userArrayList.iterator();

while (iter.hasNext()) {
User tmp = (User)iter.next();
if (tmp.getUserId() == id) {
user = tmp;
break;
}
}

if (user == null) {
pair = new ArrayList();
user = new User(id,pair);
} else

// incrememnt index
index++;
}
calculateAverageRatings();
}

private calculateAverageRatings() {
// number of rating records
HashMap numRatings = new HashMap();

// getting sums and rating numbers...
Iterator iter = userArrayList.iterator();
while (iter.hasNext()) {
User user = (User)iter.next();
Iterator iter2 = user.getpairArray().iterator();
while (iter2.hasNext()) {
Pair pair = (Pair)iter2.next();
Float average = (Float)averageRatings.get(new Integer(pair.getId()));
if (average == null) {
averageRatings.put(new Integer(pair.getId()), new Float(pair.getRatings()));
} else {
averageRatings.put(new Integer(pair.getId()), new Float(pair.getRatings() + average.floatValue()));
}
Integer num = (Integer)numRatings.get(new Integer(pair.getId()));
if (num == null) {
numRatings.put(new Integer(pair.getId()), new Integer(1));
} else {
numRatings.put(new Integer(pair.getId()), new Integer(num + 1));
}
}
}
iter = averageRatings.keySet().iterator();

// dividing sums by rating numbers...
while (iter.hasNext()) {
Integer id = (Integer)iter.next();
float sum = ((Float)averageRatings.get(id)).floatValue();
int num = ((Integer)numRatings.get(id)).intValue();
averageRatings.put(id,new Float(sum/num));
}
}
}

Regards!
0
Author Commented:
It still throws the same error, ie it is looking for a return type in the calculateAverageRatings method
0
Commented:
ah, yes,

private static void calculateAverageRatings()

private HashMap averageRatings = new HashMap();
private userArrayList = new ArrayList();
to
private static HashMap averageRatings = new HashMap();
private static ArrayList userArrayList = new ArrayList();

Regards!
0
Author Commented:
The compiler doesn't like this line:

numRatings.put(new Integer(pair.getId()), new Integer(num + 1));

Gives this error:
"FileParser.java": Error #: 375 : operator + cannot be applied to (java.lang.Integer, int) at line 110, column 74
0
Commented:
numRatings.put(new Integer(pair.getId()), new Integer(num.intValue() + 1));
0
Author Commented:
Great thx v much for the help.
0
Author Commented:
Maybe you can help we with this problem:

http://www.experts-exchange.com/Programming/Programming_Languages/Java/Q_20805158.html

Bit unsure on how to do it.
0
###### It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Java

From novice to tech pro — start learning today.