0x00 下載MovieLens數(shù)據(jù)
1) 從網(wǎng)站http://grouplens.org/datasets/movielens/下載數(shù)據(jù)
2) u.item文件包含兩列數(shù)據(jù)旨怠,為電影id和電影名稱的對應(yīng)關(guān)系;u.data文件包含四列數(shù)據(jù)缔莲,為用戶id篮绰,電影id,評價(jià),時(shí)間
3)讀入數(shù)據(jù)
<code>
movie_list = {} #id:title
with open("./u.item") as f:
for line in f.readlines():
(mid, title) = line.split('|')[0:2]
movie_list[mid] = title
pref_by_people = {}
with open("./u.data") as f:
for line in f.readlines():
(uid, mid, rating) = line.split('\t')[0:3]
if not uid in pref_by_people.keys():
pref_by_people[uid] = {}
pref_by_people[uid][movie_list[mid]] = int(rating)
</code>
4)數(shù)據(jù)類型轉(zhuǎn)換 {people:{movie:1}} -->> {movie:{people:1}}
<code>
def TransfromPref(pref):
re_pref = {}
for k1, v1 in pref.items():
for k2, v2 in v1.items():
if not k2 in re_pref.keys():
re_pref[k2] = {}
re_pref[k2][k1] = v2
return re_pref
pref_by_movie = TransfromPref(pref_by_people)
0x01 協(xié)同過濾兩種方式
協(xié)同過濾:常常被用于分辨某位特定顧客可能感興趣的東西扶欣,這些結(jié)論來自于對其他相似顧客對哪些產(chǎn)品感興趣的分析。
歐幾里德距離評價(jià)
1)以人們對事物的評價(jià)作為坐標(biāo)軸,比較兩人在坐標(biāo)空間的距離料祠,來判斷人們偏好的相近程度
2)代碼
<code>
def edclidean(prefs, person1, person2):
#找出兩人均做出評價(jià)的事物
both_list = [item for item in prefs[person1].keys() if item in prefs[person2].keys()]
#計(jì)算距離
dis = math.sqrt(sum([(prefs[person1][item] - perfs[person2][item]) ** 2 for item in both_list]))
#以距離的倒數(shù)為返回值骆捧,兩人偏好程度越就近,該值越大髓绽,假設(shè)不存在完全相同的兩人敛苇,不考慮dis為0的情況
return 1/dis
</code>
prefs中以python字典嵌套字典的方式存儲了每個(gè)人對于某時(shí)候的評價(jià)
prefs = {person:{item:1...}...},下同
皮爾遜相關(guān)系數(shù)評價(jià)
1)考慮到人們對于事物的評價(jià)標(biāo)準(zhǔn)不同顺呕,即有些人傾向打出更高的分?jǐn)?shù)枫攀,而其他人更傾向于打出較低的分?jǐn)?shù)诫欠,這是單純的考慮邏輯空間距離就不能很好的得出相近的偏好程度(例:person1對item1和item2打分分別為6和8妇斤,person2對item1和item2打分分別為8和10好爬,兩人分?jǐn)?shù)不相同腋逆,但兩人的評價(jià)有相同的趨勢咐蚯,此時(shí)也認(rèn)為兩人偏好相同)
2)皮爾遜相關(guān)系數(shù)計(jì)算
3)代碼
<code>
通過每個(gè)電影人們的評價(jià)計(jì)算出電影之間的相關(guān)系數(shù)瘩例,將上例中通過中每個(gè)人對電影的評價(jià)計(jì)算人們之間的相關(guān)系數(shù)同理
def Pearson(pref , movie1, movie2):
#找出對兩部電影都評論的人
people_list = [person for person in pref[movie1].keys() if person in pref[movie2].keys()]
n = len(people_list)
if n == 0:
return 0
#計(jì)算評價(jià)和
sum1 = sum([pref[movie1][person] for person in people_list])
sum2 = sum([pref[movie2][person] for person in people_list])
#計(jì)算評價(jià)平方和
sumSq1 = sum([pref[movie1][person] ** 2 for person in people_list])
sumSq2 = sum([pref[movie2][person] ** 2 for person in people_list])
#計(jì)算評價(jià)成績和
psum = sum([pref[movie1][person] * pref[movie2][person] for person in people_list])
# 皮爾遜相關(guān)系數(shù)計(jì)算
num = psum - sum1 * sum2 / n
den = sqrt((sumSq1 - (sum1 ** 2) / n) * (sumSq2 - (sum2 ** 2) / n))
if den == 0:
return 0
return num / den
</code>
0x02 根據(jù)電影推薦相關(guān)的電影
1)通過皮爾遜相關(guān)系數(shù)計(jì)算出每個(gè)電影的最相近的電影
<code>
def TopMatch(pref, movie, n = 5):
#計(jì)算給電影和每部電影的皮爾遜相關(guān)系數(shù)
scores = [(Pearson(pref_by_movie, movie, mov), mov) for mov in pref_by_movie.keys() if mov != movie]
#根據(jù)系數(shù)進(jìn)行排序惋戏,并由大到小排序
scores.sort(key = lambda x:x[0], reverse = True)
return scores[0:n]
</code>
2)對每部電影計(jì)算最接近電影炮捧,得到match_list
<code>
def CreateMatchList(pref = pref_by_movie):
match_list = {}
for movie in pref.keys():
match_list[movie] = TopMatch(pref, movie, 5)
return match_list
match_list = CreateMatchList()
</code>
0x03 為用戶推薦電影
1)找到我們已經(jīng)看過的電影和評價(jià)驰徊,從match_list中找出那些和我們看過的影片相近程度更高的沒看過的電影笤闯,用相關(guān)系數(shù)作為權(quán)值,計(jì)算每部電影的評分的加權(quán)平均值棍厂。
例:每一橫列出我們看過的電影第一列是我們對它的評分颗味,之后的沒兩列分別是新電影的相關(guān)系數(shù)和系數(shù)乘評分的值,總計(jì)記錄的相關(guān)系數(shù)的和與相關(guān)系數(shù)與評分的乘機(jī)的和牺弹,最后一橫做除法得到評分的加權(quán)平均數(shù)浦马,即推薦得分,分?jǐn)?shù)越高的越值得推薦
2)代碼
<code>
def get_recommanded_items(pref = pref_by_people, match_list = match_list, user = '1'):
try:
user_ratings = pref[user] #找出用戶看過的電影與評價(jià)
except KeyError:
print("no user")
return 0
scores = {} #記錄加權(quán)和
totalsim = {} #記錄評分和
for movie, rating in user_ratings.items(): #遍歷當(dāng)前用戶評分電影
for sim, sim_movie in match_list[movie]: #遍歷當(dāng)前電影相近電影
if sim_movie in user_ratings.keys(): #如果用戶看過該電影张漂,跳出本次循環(huán)
continue
#記錄加權(quán)和與評分和
if not sim_movie in scores.keys():
scores[sim_movie] = sim * rating
totalsim[sim_movie] = sim
scores[sim_movie] += sim * rating
totalsim[sim_movie] += sim
rankings = [(scores[sim_movie]/totalsim[sim_movie], sim_movie) for sim_movie in scores.keys() if totalsim[sim_movie] != 0]
#排序并取前5
rankings.sort(key=lambda x:x[0], reverse=True)
return rankings[0:5]
</code>
0x04 基于用戶進(jìn)行過濾還是基于物品進(jìn)行過濾
1)本文采用了基于物品方式的過濾晶默,即對每樣物品計(jì)算和它相似的物品。當(dāng)為某位用戶推薦時(shí)航攒,我們找到他過去評分靠前的物品磺陡,再找出這些物品相似的物品,用相關(guān)系數(shù)作為權(quán)值漠畜,計(jì)算每個(gè)物品的評分的加權(quán)平均值
2)基于用戶的推薦即對每位用戶求出和他取向相近的人币他,找出這些人中評分靠前的物品,用相關(guān)系數(shù)作為權(quán)值憔狞,計(jì)算每個(gè)物品的評分的加權(quán)平均值
3)物品間相似度的變化不會(huì)向人之間變化那么快蝴悉,即相關(guān)系數(shù)計(jì)算不用那么頻繁,可以事先計(jì)算好瘾敢,不用在每次推薦時(shí)都計(jì)算
4)針對大量數(shù)據(jù)推薦列表時(shí)拍冠,基于物品的過濾明顯比基于用戶的過濾快硝枉,不過需要額外的存儲空間開銷。
5)對于稀疏數(shù)據(jù)集倦微,基于物品的推薦明顯優(yōu)于基于用戶的推薦妻味;對于密集數(shù)據(jù)集,兩者效果類似
0x05 測試
輸入用戶id得到推薦電影的前5位
第一次推薦由于需要計(jì)算match_list欣福,花費(fèi)較長時(shí)間