/* TagRecommender: A framework to implement and evaluate algorithms for the recommendation of tags. Copyright (C) 2013 Dominik Kowald, Emanuel Lacic 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 itemrecommendations; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import processing.BLLCalculator; import common.Bookmark; import common.Utilities; import file.BookmarkReader; /** * Tag-weighted, time-weighted and combined strategy used by Zheng & Li * @author elacic * */ public class ZhengApproach { Map<Integer, Map<Integer, List<Integer>>> userResourceTagMaping; Map<Integer, Map<Integer, Long>> userResourceTimeMaping; Map<Integer, Long> userMaxTimeMaping; Map<Integer, Long> userMinTimeMaping; public ZhengApproach(List<Bookmark> trainList) { userResourceTagMaping = new HashMap<Integer, Map<Integer,List<Integer>>>(); userResourceTimeMaping = new HashMap<Integer, Map<Integer, Long>>(); userMaxTimeMaping = new HashMap<Integer, Long>(); userMinTimeMaping = new HashMap<Integer, Long>(); for (Bookmark trainData : trainList) { Integer userID = trainData.getUserID(); Integer resID = trainData.getResourceID(); List<Integer> tags = trainData.getTags(); Long day = TimeUnit.SECONDS.toDays(Long.parseLong(trainData.getTimestamp())); Map<Integer, List<Integer>> resourceTagMaping = userResourceTagMaping.get(userID); Map<Integer, Long> resourceTimeMaping = userResourceTimeMaping.get(userID); if (resourceTagMaping == null) { resourceTagMaping = new HashMap<Integer, List<Integer>>(); } if (resourceTimeMaping == null) { resourceTimeMaping = new HashMap<Integer, Long>(); } resourceTagMaping.put(resID, tags); resourceTimeMaping.put(resID, day); Long maxUserTime = userMaxTimeMaping.get(userID); Long minUserTime = userMinTimeMaping.get(userID); if (maxUserTime == null) { maxUserTime = day; minUserTime = day; } else { if (maxUserTime < day) { maxUserTime = day; } if (minUserTime > day) { minUserTime = day; } } userMaxTimeMaping.put(userID, maxUserTime); userMinTimeMaping.put(userID, minUserTime); userResourceTagMaping.put(userID, resourceTagMaping); userResourceTimeMaping.put(userID, resourceTimeMaping); } } /** * Gets the tag weight for a resource of a user * @param userID user * @param resourceID respource with tags from user * @return tag weight */ public Double getTagWeight(Integer userID, Integer resourceID) { if (! userResourceTagMaping.containsKey(userID) || ! userResourceTagMaping.get(userID).containsKey(resourceID)) { return 0.0; } //get user's tag for the provided resource Map<Integer, List<Integer>> resourceTagMaping = userResourceTagMaping.get(userID); List<Integer> tags = resourceTagMaping.get(resourceID); Double resourceTagScore = 0.0; List<Integer> tagsByUser = new ArrayList<Integer>(); Set<Integer> uniqueTagsUsedByUser = new HashSet<Integer>(); for (List<Integer> tagsAtResource : resourceTagMaping.values()) { tagsByUser.addAll(tagsAtResource); uniqueTagsUsedByUser.addAll(tagsAtResource); } for (Integer tag : tags) { resourceTagScore += getTagScore(userID, tag, tagsByUser, uniqueTagsUsedByUser); } return resourceTagScore; } /** * Calculates the tag score for the tag-weighted strategy * @param userID the user using the provided tag * @param tag the tag for which the tag score will be calculated * @param uniqueTagsUsedByUser unique tags used by the user * @param tagsByUser all tags used by the user (a tag can occur multiple times if it has been used at more resources) * @return tag score */ private Double getTagScore(int userID, int tag, List<Integer> tagsByUser, Set<Integer> uniqueTagsUsedByUser) { Double tagScore = (double) Collections.frequency(tagsByUser, tag); Integer frequencySum = 0; for (Integer uniqueTagUsed : uniqueTagsUsedByUser) { frequencySum += Collections.frequency(tagsByUser, uniqueTagUsed); } return tagScore / (double) frequencySum; } /** * Calculates the time weight which denotes the degree with which a user's interests have declined * @param userID the user * @param resourceID * @return */ public Double getTimeWeight(int userID, int resourceID) { Long minTime = userMinTimeMaping.get(userID); Long maxTime = userMaxTimeMaping.get(userID); if (minTime == null && maxTime == null) { minTime = 0L; maxTime = 1L; } if (minTime == maxTime) { minTime = 0L; } double hlu = maxTime - minTime; // TODO: half life of each user return Math.exp( - Math.log(2) * getTime(userID, resourceID) / hlu ); } /** * Takes the value of 0 for the last tagging day of a user, 1 for the penultimate tagging day, etc. * In other words, the maximum day of the user (the last day) minus the day of tagging the resource * @param userId * @param resourceID * @return */ private Long getTime(int userId, int resourceID) { Long time = 0L; if (userResourceTimeMaping.containsKey(userId) && userResourceTimeMaping.get(userId).containsKey(resourceID)) { time = userMaxTimeMaping.get(userId) - userResourceTimeMaping.get(userId).get(resourceID); } return time; } /** * Calculates the combined tag-time weight * @param userID user * @param resourceID resource of the user * @param lambda parameter to adjust the significance of the tag and time weights * @return tag-time weight */ public Double getTagTimeWeight(int userID, int resourceID, double lambda) { return (lambda * getTagWeight(userID, resourceID)) + ((1 - lambda) * getTimeWeight(userID, resourceID)); } /** * Calculates the combined tag-time weight * @param tagWeight * @param timeWeight * @param lambda * @return */ public Double getTagTimeWeight(Double tagWeight, Double timeWeight, double lambda) { return (lambda * tagWeight) + ((1 - lambda) * timeWeight); } }