您的位置:首页 > 编程语言 > Python开发

<数据挖掘><python><一个小的推荐系统示例>

2012-12-14 14:12 1101 查看

一个小的推荐系统示例

Python是能编译的伪代码,开发效率高,计算能力很强,变量不用声明。还可以写脚本、写网站(主要是在html里面写python,估计没人会在Python里面写html吧)

一、Python也有类似java的机制——“PVM”(大家理解成python虚拟机,可以在多平台下运行)

二、环境搭建:(1)在windows下,直接上官网www.python.org 下载msi文件安装即可,可以用命令行,也有自己的GUI界面——IDLE;(2)如果是linux,那就自己找安装教程吧,也很简单的,如:在ubuntu下直接在shell下敲"apt-get install python2" ;(3)总之环境搭建很简单。

三、实验数据

一个测试集(80train.txt,8万行数据),一个训练集(test.txt,2万行数据),这两个数据都是来自某电影推荐网站的数据库,其中每一行有四个字段(196242 3 881250949),字段依次表示user,movie,score,time(单位:ms,经过分发现最后一个字段是没有什么用处的)。算法会从训练集中找到用户之间的相似度(这里是简化的皮尔逊算法),根据相似度*分数,然后求平均值得到推荐分数。测试集是用来检查你的预测是否正确的,标准为MAE(Mean Average Error)=(所有预测值跟真实值差值总和)/(预测数量),越接近0越准确

四、代码

# -*- coding: cp936 -*-
from math import *
import time
import random

##读取文件,返回值为嵌套字典
def Readf(filename):
dir={}
tmp={}

#读取文件
fp=open(filename, "r")
alllines=fp.readlines()
fp.close()

#读取每一行数据,忽略时间戳(应该说时间戳没有用、、)
for eachline in alllines:
item=eachline.split()
if dir.has_key(item[0]):
tmp=dir[item[0]]
else:
tmp={}
tmp[item[1]]=int(item[2])
dir[item[0]]=tmp
return  dir

##相似度算法,返回值越大说明越相近(r=range(-1,1))
def simplePEX(prefs,p1,p2):
si={}
for item in prefs[p1]:
if item in prefs[p2]: si[item]=1
n=len(si)                     #相同电影的数量
if n==0: return 1

#偏好求和
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 int(r*10+1)*0.1

##得到用户的所有电影的平均分
def getUserAverage(prefs,user):
scoresSum=0
count=0
for movie in prefs[user]:
scoresSum += prefs[user][movie]
count +=1
return scoresSum/count

def getRecommendations(prefs,person,similarity=simplePEX):
totals={}
simSums={}
#不要和自己比较
userAve=getUserAverage(prefs,person)
for other in prefs:
if other==person: continue
otherAve=getUserAverage(prefs,other)
sim=similarity(prefs,person,other)

#忽略评价值小于0.3的情况
if sim<=0.2: continue

for othermovie in prefs[other]:
if othermovie not in prefs[person]:
totals.setdefault(othermovie,0)
totals[othermovie]+=(prefs[other][othermovie]-otherAve)*sim
simSums.setdefault(othermovie,0)
simSums[othermovie]+=sim
rankings=[(int(total/simSums[item]+userAve+0.5),item) for item,total in totals.items()]
rankings.sort()
rankings.reverse()
return rankings

def printMAE():
_sum=0.0
count=0
train=Readf("80train.txt")
test=Readf("test.txt")

for person in test:
recommentations = getRecommendations(train,person)
for key in test[person].keys():
itemCount = 0
for item in recommentations:
if key == item[1]:
_sum += abs(item[0] - test[person][key])
count += 1
break
itemCount += 1
if itemCount == len(recommentations):
_sum += getUserAverage(train,person)
count += 1

if count==0: print "error!"
else:
print "Count",count
print "MAE =",_sum/count

#here is main()
start=time.time()
print "Start:"
printMAE()
end=time.time()
print "Time=",end - start,"s"
print "End!"


五、数据处理过程

1. 数据预处理。将 80train.txt 和 test.txt 的条目(每一行)一条一条进行处理,忽略时间戳生成python的数据结构——dictionary,其结构如下:
{ user0:{movie0:scores0,moive1:scores1,......},user1:{movie0:scores0,......} ...... }
2. 脏数据去除。本来以为时间戳可以去掉一些脏数据。但是发现时间戳根本没有什么用处(理由在下面),所以我认为所有的数据都是有用的。

那么为什么没用?
① 如果时间戳的单位为ms:
Name : 216
Movie:
658 3 1970-01-11 04:30:45
Name : 217
Movie:
27 1 1970-01-11 06:57:50
2 3 1970-01-11 06:57:49 ......
结果时间值为1970年1月11号凌晨左右。
② 那如果单位是s呢?
Name : 216
Movie:
658 3 1997-11-23 00:30:29

Name : 217
Movie:
27 1 1998-03-05 03:53:31
2 3 1998-03-05 03:49:42

Name : 214
Movie:
1129 4 1998-04-15 19:24:09
334 3 1998-04-02 18:42:20
117 4 1998-04-02 18:54:01
131 3 1998-04-02 19:14:25
137 4 1998-04-02 18:53:47
275 3 1998-04-02 18:49:28

Name : 215
Movie:
421 4 1998-04-01 13:01:44
197 4 1998-04-01 12:55:57
212 2 1998-04-01 13:01:20
输出的结果为1998年1月至12月。虽然时间不同,但是对于特定的某一个用户,时间是基本一样的。这些数据很明显跟用户没有关系,因为整个程序是基于用户的,所以我觉得时间戳跟用户没有关系。通俗一点,时间戳没有什么作用。
用户间的相似度。采用的是皮尔逊算法,返回值大小区间为[-1,1]。值越大,用户的相似程度越高。
3. 简化皮尔逊算法。普通皮尔逊的返回值为[-1,1]的浮点数。这里,我们只采用后小数点后一位
4. 但是真正能用的只能是[0,1],这是之前的想法,但是考虑到当值用户之间的相似度很小的时候(也是正数),我们大胆推测其间已经不是很相似了,换句话说就是“不相似值”已经很接近“1”了,对于这部分数据(相似度较小),我们最终采取去除的操作。去除方法:本来的想法是去除[0,1]相似值较小的n%的,但这个比较耗运行时间。最终方法是只选用相似值为[0.3 , 1]的数据。
5. 推荐算法的实现。对于一个特定的人,例如:用户:“20”,电影:“211”。那么我们对每个除自己外的人算皮尔逊值,选取[0.3 , 1]区间的数据。然后在每个其他用户的字典里找电影“211”,如果存在“211”,那么读取该用户的“211”评分,乘于相似度,最终取所有平均值。返回。
6. MAE。读取test.txt 产生的字典,循环读取每个用户的每部电影,然后对推荐评分和test评分进行处理,最终得出MAE。但是并不是每一部电影都有推荐,那么这是为什么呢?因为有些电影没有人看过,没有看过的电影怎么推荐呢?这个问题我也问过老师,老师说了两种方法:1.给中间值;2.给最常出现的的评分。而我想了一个个人觉得更好的方法——给该用户所有电影的最低分。理由:之所以没有算出推荐分数,不是因为没有没人看过这部电影,而是因为有该电影评分的没人和有相似度,有相似度的却没看过这部电影。在现实生活中是有可能发生的,不常见而已。
六、输出结果

Start:

Count 20000

MAE = 0.7616

Time= 67.7969999313 s

End!

欢迎下载数据代码:文件名:Data Mining-python.rar,访问地址:pan.xiaomi.com/file/id_59304594890555457.htm(小米网盘,记得加http://)

在下才疏学浅,上面是我的一些私人分享,如有不正确的地方,还请留言指正。——jack
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: