001    /**
002     * CollaborativeRetrievalMethod.java
003     * jCOLIBRI2 framework. 
004     * @author Juan A. Recio-García.
005     * GAIA - Group for Artificial Intelligence Applications
006     * http://gaia.fdi.ucm.es
007     * 11/11/2007
008     */
009    package jcolibri.extensions.recommendation.collaborative;
010    
011    import java.util.ArrayList;
012    import java.util.Collection;
013    import java.util.Iterator;
014    
015    import jcolibri.cbrcore.CBRCase;
016    import jcolibri.cbrcore.CBRQuery;
017    import jcolibri.extensions.recommendation.collaborative.MatrixCaseBase.RatingTuple;
018    import jcolibri.extensions.recommendation.collaborative.MatrixCaseBase.SimilarTuple;
019    import jcolibri.method.retrieve.RetrievalResult;
020    
021    /**
022     * This method returns cases depending on the recommendations of other users.
023     * <br>
024     * It uses a PearsonMatrix Case base to compute the similarity among neighbors.
025     * Then, cases are scored according to a rating that is estimated using the following
026     * formula:<br>
027     * <img src="collaborativerating.jpg"/>
028     * <p>
029     * See:<p>
030     * J. Kelleher and D. Bridge. An accurate and scalable collaborative recommender.
031     * Articial Intelligence Review, 21(3-4):193-213, 2004.
032     * 
033     *  
034     * @author Juan A. Recio-Garcia
035     * @author Developed at University College Cork (Ireland) in collaboration with Derek Bridge.
036     * @version 1.0
037     * @see jcolibri.test.recommenders.rec12.MoviesRecommender
038     */
039    public class CollaborativeRetrievalMethod
040    {
041        @SuppressWarnings("unchecked")
042        /**
043         * Returns a list of cases scored following the collaborative recommendation formulae.
044         * @param cb is the case base that contains the cases
045         * @param id of the user
046         * @param kItems is the number of items/ratings to return
047         * @param kUsers defines the number of users taken into account to score the cases. 
048         */
049        public static Collection<RetrievalResult> getRecommendation(PearsonMatrixCaseBase cb, CBRQuery query, int kUsers)
050        {
051            ArrayList<RetrievalResult> result = new ArrayList<RetrievalResult>();
052            
053            int id = (Integer)query.getID();
054            Collection<SimilarTuple> simil = cb.getSimilar(id);
055            
056            if(simil == null)
057            {
058                org.apache.commons.logging.LogFactory.getLog(CollaborativeRetrievalMethod.class).error("Id "+id+" does not exists");
059                return result;
060            }
061            
062            
063            ArrayList<SimilarTuple> select = new ArrayList<SimilarTuple>();
064            int i=0;
065            for(Iterator<SimilarTuple> iter = simil.iterator(); (iter.hasNext() && i<kUsers);i++)
066                select.add(iter.next());
067            
068            
069            /////// debug
070            System.out.println("\nQuery: "+ cb.getDescription(id));
071            System.out.println(cb.getRatingTuples(id).size()+" Ratings: "+cb.getRatingTuples(id));  
072            System.out.println("\nSimilar ratings:");
073            for(SimilarTuple st: select)
074            {
075                System.out.print(st.getSimilarity()+" <--- ");
076                System.out.println(cb.getDescription(st.getSimilarId()));
077                System.out.println(cb.getRatingTuples(st.getSimilarId()).size()+" Ratings: "+cb.getRatingTuples(st.getSimilarId()));
078            }
079            /////////////
080            
081            for(Integer solId : cb.getSolutions())
082            {
083                double mean = cb.getAverage(id);
084                double acum = 0;
085                double simacum = 0;
086                for(SimilarTuple st : select)
087                {
088                    int other = st.getSimilarId();
089                    double rating = findRating(cb, other, solId);
090                    double otherMean = cb.getAverage(other);
091                    acum += ((rating - otherMean) * st.getSimilarity());
092                    simacum += st.getSimilarity();
093                }
094                double res = mean + (acum/simacum);
095                
096                CBRCase c = new CBRCase();
097                c.setDescription(cb.getDescription(id));
098                c.setSolution(cb.getSolution(solId));
099                
100                result.add(new RetrievalResult(c,res));
101            }
102            
103            java.util.Collections.sort(result);
104    
105            return result;
106        }
107        
108        private static double findRating(PearsonMatrixCaseBase cb, int descId, int solId)
109        {
110            for(RatingTuple rt: cb.getRatingTuples(descId))
111            {
112                if(rt.getSolutionId() == solId)
113                    return rt.getRating();
114            }
115            return 0;
116        }
117    }