初学数据挖掘——相似性度量(一)
好久没有写这个了。也就是在去年到今年这个时间段里,同时决定好几件事情。第一:考研。第二:以后方向就是大数据或者是叫数据挖掘。这两件事当然是有联系的,第一件事就是考研考到北京,接着研究生的方向就是数据挖掘了吧。在一边准备考研的同时,还必须得一边准备着数据挖掘方面的知识。无奈本科前三年这方面接触得极少,只好利用现在的时间来恶补了。
不久前买了一边《集体智慧编程》,开篇即开始讲算法,或者是整本书都是在讲算法,而第一个算法就是——相似度度量。这个在现在用得非常多,在QQ音乐等音乐播放器上有类似“猜你喜欢”,淘宝、亚马逊上也有“猜你喜欢”,进各种网页有各种和你最近逛淘宝的商品品种广告,包括哪怕是搜索也肯定是和相似度有关,不可否认,这是有大量用途的一个算法。所以我就以这本书为主,贴出在学习过程的一些代码、注释等。
书中开篇相似度度量方法一共详细讲了两个算法:一:欧几里得距离;二:皮尔逊相关度评价。当然相似性度量远远不止这两种,http://www.chinaz.com/web/2011/1008/212684.shtml 在这里我找到了有关距离和相似性度量的一些算法。我另外写出了“Jaccard相似度(狭义)”和“曼哈顿距离(城市街区距离)”相应代码,对了,相应的算法代码语言是Python2.7。在这里解释一下关于“距离”和“相关系数”:“距离”和“相关系数”是两个相反的概念,“距离”数值越大说明其“相似度”越低也就是数值越小。
我先依次介绍四种算法,最后再给出四种算法的所有代码,由于在代码中的注释也已经足够,所以仅简单介绍四种算法相应的数学公式和解释说明。
一:欧几里得距离。别被这个名称给吓着,我第一次看见这个就给吓着了,其实就是两点之间的距离。比如在二维坐标系中点A(x1,y1),B(x2,y2),他们的欧几里得距离就是,用坐标来直观表示吧。推广到n维坐标,A(x1,x2,x3……),B(y1,y2,y3……)欧几里得距离则是,这个恕我不能用坐标直观来表达了:)。那么每个点的每个“维”代表的是什么意思呢?坐标系怎么和相似性度量扯上联系呢?我们不妨假设一个场景。小明和小红评价3部电影,小明对电影《左耳》的评价是3分,电影《何以笙箫默》的评价是4分,电影《速度与激情》的评价是5分,而小红对应的则是2分,5分,1分。我们需要根据对电影的评分来判断小明和小红是否兴趣相投或者兴趣相似,这时就是相似性度量。我们把小明在坐标轴上设为A点,对3部电影的评分分别代表3个维度,同理小红则设为B点。小明则对电影的评分则是A(3,4,5),小红则是B(2,5,1)。这时候我们计算他们之间的欧几里得距离,他们之间的距离越长,说明他们两个的相似性越低,反之,相似度则越高。所以,欧几里得距离——就是坐标轴上两点之间的距离。
二:皮尔逊相关系数。这个就直接甩公式了。至于公式里的为什么我还不懂:(。有两个公式,第一个是相对于总体:。cov(x,y)表示协方差,E(x)和μx表示期望,σ表示标准差。第二个是相对于样本,代码中即是样本公式:,最后一个公式是代码中的公式。
三:Jaccard相似度(狭义)。还存在一个广义Jaccard相似度,狭义Jaccard相似度在某些方面并不大适用,因为它只能判断两者中的元素是否一致,拿上例中的电影例子来说就是,小明对有且只有对三个电影做出了评价,同样小红也是有且只有对三个电影做出了评价,注意是做出了评价,而不是做出了什么评价,这个时候他们的相似度就是1,也就是相同。公式:。
四:曼哈顿距离。这个曼哈顿距离也称作“城市街区距离”,之所以是这样的称呼,我们画个图就明白了。,在实际的街区中,我们需要从A点到B点,我们不能走“欧几里得距离”也就是虚线的路线,因为实际当中不可能这么走,只能走它的实线距离,所以称作“城市街区距离”。呃,至于在什么时候用,我暂时也不知道:(。
下面就开始直接贴代码了吧,四个代码的算法在一起。
1 # -*- coding:utf-8 -*- 2 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}, 3 '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}, 4 'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0, 'Superman Returns': 3.5, 'The Night Listener': 4.0}, 5 '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}, 6 '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}, 7 '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}, 8 'Toby': {'Snakes on a Plane': 4.5, 'You, Me and Dupree': 1.0, 'Superman Returns': 4.0}, 9 'Yu':{'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}} 10 11 from math import sqrt 12 print u"距离与相关系数:它们之间是相反的,若距离越短(距离的数值越小),则相似度越大(相似度的数值越大)" 13 # 欧几里得距离 14 def sim_distance(prefs, person1, person2): 15 # 得到两者同时评价过的电影的列表 16 si = { } 17 for item in prefs[person1]: 18 if item in prefs[person2]: 19 si[item] = 1 20 # 若不存在同时评价过的电影则返回0 21 if len(si) == 0 : return 0 22 23 # 计算所有差值的平方和 24 sum_of_squares = sum([pow(prefs[person1][item] - prefs[person2][item], 2) 25 for item in prefs[person1] if item in prefs[person2]]) # sum()函数中的参数是一个list,sum([item for item in a if item in b]) 26 27 return 1 / (1+sqrt(sum_of_squares)) 28 print u"欧几里得距离(最后给出的数值,实际上是给出了相似度评价):" 29 print(sim_distance(critics, 'Lisa Rose', 'Gene Seymour')) 30 31 # 皮尔逊相关系数 32 def sim_pearson(prefs, p1, p2): 33 si = { } 34 for item in prefs[p1]: 35 if item in prefs[p2]: 36 si[item] = 1 37 n = len(si) 38 if n == 0: 39 return 1 #如果两者不存在同时评论过的电影时,返回1 40 41 #对所有偏好求和 42 sum1 = sum([prefs[p1][it] for it in si]) 43 sum2 = sum([prefs[p2][it] for it in si]) 44 #求平方和 45 sum1Sq = sum([pow(prefs[p1][it], 2) for it in si]) 46 sum2Sq = sum([pow(prefs[p2][it], 2) for it in si]) 47 #求乘积之和 48 pSum = sum([prefs[p1][it] * prefs[p2][it] for it in si]) 49 50 #计算皮尔逊评价值 51 num = pSum - (sum1 * sum2 / n) 52 den = sqrt((sum1Sq-pow(sum1, 2)/n) * (sum2Sq-pow(sum2, 2)/n)) 53 54 if den == 0: return 0 55 r = num/den 56 57 return r 58 59 print u"皮尔逊相关系数:" 60 print (sim_pearson(critics, 'Lisa Rose', 'Gene Seymour')) 61 62 # Jaccard相似度(狭义)——只能用于判断两者之间是否一致,而不能根据其评分来判定相似度 63 def sim_jaccard(prefs, per1, per2): 64 si_union = { } #并集 65 si_inter = { } #交集 66 si_union = dict(prefs[per1], **prefs[per2]) 67 68 for item in prefs[per1]: 69 if item in prefs[per2]: 70 si_inter[item] = min(prefs[per1][item], prefs[per2][item]) 71 72 sum1 = len(si_inter) 73 sum2 = len(si_union) 74 75 if (sum2 == 0): return 0 76 77 r = float(sum1) / sum2 78 return r 79 80 print u"Jaccard相似度(狭义)——只能用于判断两者之间是否一致,而不能根据其评分来判定相似度:" 81 print sim_jaccard(critics, 'Lisa Rose', 'Gene Seymour') 82 83 #曼哈顿距离(城市街区距离 ) 84 def sim_manhattan(prefs, p1, p2): 85 si = { } 86 for item in p1: 87 if item in p2: si[item] = 1 88 if len(item) == 0: return 1 89 90 sum_of_minus = sum([abs(prefs[p1][item] - prefs[p2][item]) 91 for item in prefs[p1] if item in prefs[p2]]) 92 return 1 / (sum_of_minus+1) 93 print u"曼哈顿距离(最后得到的数值也是相似度):" 94 print sim_manhattan(critics, 'Lisa Rose', 'Gene Seymour')