Что не так с этой функцией Python из "Программирование Коллективного разума"? - PullRequest
4 голосов
/ 14 сентября 2009

Это рассматриваемая функция. Он рассчитывает коэффициент корреляции Пирсона для p1 и p2, который должен быть числом от -1 до 1.

Когда я использую это с реальными пользовательскими данными, иногда возвращается число больше 1, как в этом примере:

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

    n=len(si)

    sum1=sum([prefs[p1][it] for it in si])
    sum2=sum([prefs[p2][it] for it in si])

    sum1Sq=sum([pow(prefs[p1][it],2) for it in si])
    sum2Sq=sum([pow(prefs[p2][it],2) for it in si]) 

    pSum=sum([prefs[p1][it]*prefs[p2][it] for it in si])

    num=pSum-(sum1*sum2/n)
    den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))

    if den==0: return 0

    r=num/den

    return r

critics = {
    'user1':{
        'item1': 3,
        'item2': 5,
        'item3': 5,
        },

    'user2':{
        'item1': 4,
        'item2': 5,
        'item3': 5,
        }
}

print sim_pearson(critics, 'user1', 'user2', )

1.15470053838

Ответы [ 4 ]

8 голосов
/ 14 сентября 2009

Похоже, вы неожиданно используете целочисленное деление. Я внес следующие изменения, и ваша функция вернула 1.0:

num=pSum-(1.0*sum1*sum2/n)
den=sqrt((sum1Sq-1.0*pow(sum1,2)/n)*(sum2Sq-1.0*pow(sum2,2)/n))

См. PEP 238 для получения дополнительной информации об операторе деления в Python. Альтернативный способ исправить ваш код выше:

from __future__ import division
2 голосов
/ 15 сентября 2009

Целочисленное деление сбивает с толку. Это работает, если вы сделаете n float:

n=float(len(si))
2 голосов
/ 14 сентября 2009

Ну, я потратил минуту, чтобы прочитать код, но, кажется, если вы измените свои входные данные на float , это будет работать

1 голос
/ 15 сентября 2009

Ну, я не смог точно выяснить, что не так с логикой в ​​вашей функции, поэтому я просто переопределил ее, используя определение коэффициента Пирсона:

from math import sqrt

def sim_pearson(p1,p2):
    keys = set(p1) | set(p2)
    n = len(keys)

    a1 = sum(p1[it] for it in keys) / n
    a2 = sum(p2[it] for it in keys) / n

#    print(a1, a2)

    sum1Sq = sum((p1[it] - a1) ** 2 for it in keys)
    sum2Sq = sum((p2[it] - a2) ** 2 for it in keys) 

    num = sum((p1[it] - a1) * (p2[it] - a2) for it in keys)
    den = sqrt(sum1Sq * sum2Sq)

#    print(sum1Sq, sum2Sq, num, den)
    return num / den

critics = {
    'user1':{
        'item1': 3,
        'item2': 5,
        'item3': 5,
        },

    'user2':{
        'item1': 4,
        'item2': 5,
        'item3': 5,
        }
}

assert 0.999 < sim_pearson(critics['user1'], critics['user1']) < 1.0001

print('Your example:', sim_pearson(critics['user1'], critics['user2']))
print('Another example:', sim_pearson({1: 1, 2: 2, 3: 3}, {1: 4, 2: 0, 3: 1}))

Обратите внимание, что в вашем примере коэффициент Пирсона равен всего 1.0, поскольку векторы (-4/3, 2/3, 2/3) и (-2/3, 1/3, 1/3) параллельны.

...