/*
TagRecommender:
A framework to implement and evaluate algorithms for the recommendation
of tags.
Copyright (C) 2013 Dominik Kowald
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package common;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Calculator for the Pearson similarity between two user ratings
* @author elacic
*
*/
public class PearsonSimilarityCalculator {
/**
* Calculates the Pearson-similarity based on the resource ratings of a user and the resource ratings of his neighbor
* @param userRatings resource ratings of the user
* @param neighborRatings resource ratings of the user's neighbor
* @return the Pearson similarity
*/
public static double getPearsonSim(List<Bookmark> userRatings, List<Bookmark> neighborRatings) {
double differenceSum = 0.0;
double userSquareDifferenceSum = 0.0;
double neighboorSquareDifferenceSum = 0.0;
Map<Integer, List<Double>> coratedItems = new HashMap<Integer, List<Double>>();
double userRatingAverage = calculateAverageAndFillCoratedItems(userRatings, coratedItems);
double neighboorRatingAverage = calculateAverageAndFillCoratedItems(neighborRatings, coratedItems);
for (Integer resourceId : coratedItems.keySet()) {
List<Double> resourceRatings = coratedItems.get(resourceId);
if (resourceRatings.size() < 2) {
continue;
}
Double userResourceRating = resourceRatings.get(0);
Double neighboorResourceRating = resourceRatings.get(1);
differenceSum += (userResourceRating - userRatingAverage) * (neighboorResourceRating - neighboorRatingAverage);
userSquareDifferenceSum += Math.pow( (userResourceRating - userRatingAverage) , 2);
neighboorSquareDifferenceSum += Math.pow( (neighboorResourceRating - neighboorRatingAverage) , 2);
}
return differenceSum / (Math.sqrt(userSquareDifferenceSum) * Math.sqrt(neighboorSquareDifferenceSum));
}
/**
* Calculates the average of a user's ratings and fills the coratedItems map
* @param userRatings
* @param coratedItems
* @return
*/
private static double calculateAverageAndFillCoratedItems(List<Bookmark> userRatings, Map<Integer, List<Double>> coratedItems) {
double ratingSum = 0.0;
for (Bookmark userRating : userRatings) {
double filteredRating = filterRating(userRating.getRating());
ratingSum += filteredRating;
fillCoratedItems(coratedItems, userRating.getResourceID(), filteredRating);
}
return ratingSum / userRatings.size();
}
/**
* Fills the mapping on the rating for a resource
* @param coratedItems mapping for resource rating which will be filled
* @param resource resource that is rated
* @param filteredRating rating for the resource
*/
private static void fillCoratedItems(Map<Integer, List<Double>> coratedItems, Integer resource, double filteredRating) {
List<Double> resourceRating = null;
if (coratedItems.containsKey(resource)) {
resourceRating = coratedItems.get(resource);
} else {
resourceRating = new ArrayList<Double>();
}
resourceRating.add(filteredRating);
coratedItems.put(resource, resourceRating);
}
/**
* For read only ratings which were set to -1 by convention in the CUL dataset
* @param rating rating to be filtered
* @return filtered rating
*/
private static double filterRating(double rating) {
if (rating == -1) {
return 3;
}
return rating;
}
}