Link to home
Start Free TrialLog in
Avatar of override_y2k
override_y2k

asked on

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

Avatar of jimmack
jimmack

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.
Avatar of override_y2k

ASKER

Oh sorry jimmack.The link has been provided by JakobA.
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!
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!
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!
You were dying to get that HashMap in werent you! :)
Also, it would be preferable not to use a hashmap when calculating a users average rating.

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

      newAverage =  ( newCount + ( oldNrOfCounts * oldAverage ) ) / newNrOfCounts;
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.
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;
 }
ASKER CERTIFIED SOLUTION
Avatar of vk33
vk33

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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 :)
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!
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
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!
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();

    FileReader fr =
      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();
          pair.add(new Pair(movieId, ratings));
          user = new User(id,pair);
          userArrayList.add(user);
        } else
          user.getpairArray().add(new Pair(movieId, ratings));

          // 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.
Should i be returning sum/num?
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 {
    FileReader fr =
      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();
          pair.add(new Pair(movieId, ratings));
          user = new User(id,pair);
          userArrayList.add(user);
        } else
          user.getpairArray().add(new Pair(movieId, ratings));

          // 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!
It still throws the same error, ie it is looking for a return type in the calculateAverageRatings method
ah, yes,

private static void calculateAverageRatings()

And please, change
    private HashMap averageRatings = new HashMap();
    private userArrayList = new ArrayList();
to
    private static HashMap averageRatings = new HashMap();
    private static ArrayList userArrayList = new ArrayList();

Regards!
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
numRatings.put(new Integer(pair.getId()), new Integer(num.intValue() + 1));
Great thx v much for the help.
Maybe you can help we with this problem:

https://www.experts-exchange.com/questions/20805158/Generating-predictions.html

Bit unsure on how to do it.