-------------------------------------------------------------------------------
# Name:        recommendations.py
# Purpose:
#
# Author:      xiaolexu
#
# Created:     01/09/2015
#-------------------------------------------------------------------------------


from math import sqrt

# Returns a distance-based similarity score for person1 and person2
def sim_distance(prefs, person1, person2):
    # Get the list of shared_items
    si = {}
    for item in prefs[person1]:
        if item in prefs[person2]:
            si[item] = 1

    # if they have no rating in common, return 0
    if len(si) == 0:
        return 0

    # Add up squares of all the differences
    sum_of_squares =  sum([pow(prefs[person1][item] - prefs[person2][item], 2)
                           for item in prefs[person1] if item in prefs[person2]])
    return 1/(1+sqrt(sum_of_squares))

# Returns the Pearson correlation coefficient for p1 and p2
def sim_pearson(prefs, p1, p2):
    si={}
    for item in prefs[p1]:
        if item in prefs[p2]:
            si[item]=1
    if len(si)==0:
        return 0
    # sum up all the preferences
    sum1=sum([prefs[p1][it] for it in si])
    sum2=sum([prefs[p2][it] for it in si])

    # sum up the squares
    sum1sq=sum([pow(prefs[p1][it], 2) for it in si])
    sum2sq=sum([pow(prefs[p2][it], 2) for it in si])

    # sum up the products
    sumP=sum([prefs[p1][it] * prefs[p2][it] for it in si])

    # Calculate Pearson score
    num=sumP-(sum1*sum2/len(si))
    den=sqrt((sum1sq-pow(sum1,2)/len(si))*(sum2sq-pow(sum2,2)/len(si)))
    if den==0: return 0
    r=num/den
    return r

# Returns the best matches for person from the prefs dictionary.
# Number of results and similarity function are optional params.
def topMatches(prefs, person, n=5, similarity=sim_pearson):
    scores=[(similarity(prefs, person, other), other)
                for other in prefs if other!=person]

    # sort the list
    scores.sort()
    scores.reverse()
    return scores[0:n]

# Gets recommendations for a person by using a weighted average
# of every other user's rankings
def getRecommendations(prefs, person, similarity=sim_pearson):
    totals={}
    simSums={}
    for other in prefs:
        if other == person: continue
        sim=similarity(prefs, person, other)

        # ignore scores of zero or lower
        if sim <=0 : continue
        for item in prefs[other]:
            # only score movies I haven't see yet
            if item not in prefs[person] or prefs[person][item]==0:
                # similarity * score
                totals.setdefault(item, 0)
                totals[item]+=prefs[other][item]*sim
                # sum of similarity
                simSums.setdefault(item, 0)
                simSums[item]+=sim

    # crerate normalized list
    rankings=[(total/simSums[item], item)
                for item, total in totals.items()]

    # return the sorted list
    rankings.sort()
    rankings.reverse()
    return rankings

def transformPrefs(prefs):
    result={}
    for person in prefs:
        for item in prefs[person]:
            result.setdefault(item, {})
            result[item][person] = prefs[person][item]

    return result

# A dictionary of movie critics and their ratings of a small
# set of movies
critics={
    'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5,
    'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You, Me and Dupree': 2.5,
    'The Night Listener': 3.0},

    'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5,
    'Just My Luck': 1.5, 'Superman Returns': 5.0, 'The Night Listener': 3.0,
    'You, Me and Dupree': 3.5},

    'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0,
    'Superman Returns': 3.5, 'The Night Listener': 4.0},

    'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0,
    'The Night Listener': 4.5, 'Superman Returns': 4.0,
    'You, Me and Dupree': 2.5},

    'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
    'Just My Luck': 2.0, 'Superman Returns': 3.0, 'The Night Listener': 3.0,
    'You, Me and Dupree': 2.0},

    'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
    'The Night Listener': 3.0, 'Superman Returns': 5.0, 'You, Me and Dupree': 3.5},

    'Toby': {'Snakes on a Plane':4.5,'You, Me and Dupree':1.0,'Superman Returns':4.0}
}