package edu.umn.cs.recsys.uu; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import edu.umn.cs.recsys.dao.*; import org.grouplens.lenskit.ItemScorer; import org.grouplens.lenskit.RecommenderBuildException; import org.grouplens.lenskit.core.LenskitConfiguration; import org.grouplens.lenskit.core.LenskitRecommender; import org.grouplens.lenskit.data.dao.EventDAO; import org.grouplens.lenskit.data.dao.ItemDAO; import org.grouplens.lenskit.data.dao.UserDAO; import org.grouplens.lenskit.vectors.SparseVector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Main class for running the user-user assignment. Each command line argument is a user:item * pair to score. * * <ul> * <li>Revision 3: make compatible with Java 6</li> * <li>Revision 2: use root locale for output formatting</li> * </ul> * * @author <a href="http://www.grouplens.org">GroupLens Research</a> */ public class UUMain { private static final Logger logger = LoggerFactory.getLogger("uu-assignment"); /** * Main entry point to the program. * @param args The <tt>user:item</tt> pairs to score. */ public static void main(String[] args) { Map<Long,Set<Long>> toScore = parseArgs(args); LenskitConfiguration config = configureRecommender(); LenskitRecommender rec; try { rec = LenskitRecommender.build(config); } catch (RecommenderBuildException e) { logger.error("error building recommender", e); System.exit(2); throw new AssertionError(); // to de-confuse unreachable code detection } // Get the item scorer and go! ItemScorer scorer = rec.getItemScorer(); assert scorer != null; // Also get the item title DAO, so we can look up movie titles ItemTitleDAO titleDAO = rec.get(ItemTitleDAO.class); for (Map.Entry<Long,Set<Long>> scoreRequest: toScore.entrySet()) { long user = scoreRequest.getKey(); Set<Long> items = scoreRequest.getValue(); logger.info("scoring {} items for user {}", items.size(), user); // We call the score method that takes a set of items. // AbstractItemScorer delegates this method to the one you are supposed to implement. SparseVector scores = scorer.score(user, items); for (long item: items) { String score; if (scores.containsKey(item)) { score = String.format(Locale.ROOT, "%.4f", scores.get(item)); } else { score = "NA"; } String title = titleDAO.getItemTitle(item); System.out.format("%d,%d,%s,%s\n", user, item, score, title); } } } /** * Parse the command line arguments. * @param args The command line arguments. * @return A map of users to the sets of items to score for them. */ private static Map<Long, Set<Long>> parseArgs(String[] args) { Pattern pat = Pattern.compile("(\\d+):(\\d+)"); Map<Long, Set<Long>> map = Maps.newHashMap(); for (String arg: args) { Matcher m = pat.matcher(arg); if (m.matches()) { long uid = Long.parseLong(m.group(1)); long iid = Long.parseLong(m.group(2)); if (!map.containsKey(uid)) { map.put(uid, Sets.<Long>newHashSet()); } map.get(uid).add(iid); } else { logger.error("unparseable command line argument {}", arg); } } return map; } /** * Create the LensKit recommender configuration. * @return The LensKit recommender configuration. */ // LensKit configuration API generates some unchecked warnings, turn them off @SuppressWarnings("unchecked") private static LenskitConfiguration configureRecommender() { LenskitConfiguration config = new LenskitConfiguration(); // configure the rating data source config.bind(EventDAO.class) .to(MOOCRatingDAO.class); config.set(RatingFile.class) .to(new File("data/ratings.csv")); // use custom item and user DAOs // our item DAO has title information config.bind(ItemDAO.class) .to(MOOCItemDAO.class); // and title file config.set(TitleFile.class) .to(new File("data/movie-titles.csv")); // our user DAO can look up by user name config.bind(UserDAO.class) .to(MOOCUserDAO.class); config.set(UserFile.class) .to(new File("data/users.csv")); // use the TF-IDF scorer you will implement to score items config.bind(ItemScorer.class) .to(SimpleUserUserItemScorer.class); return config; } }