【Educoder数据挖掘实训】相似度与相异度
开挖!!!!!!
T1 用相关系数计算直线之间的相似度
这关分为没啥关系的三部分,分别是欧几里得相关系数、余弦相关系数和泊松相关系数。
他们的公式都比较直观:
欧几里得相关系数的计算方式是按照对应点的相对距离来算的,也就是 d ( x , y ) = ∑ i = 1 n ( x i − y i ) 2 d(x,y) = \sqrt{\sum\limits_{i = 1}^{n}(x_i - y_i)^2} d(x,y)=i=1∑n(xi−yi)2 .
余弦相关系数则将两个数据看做两个 n n n维向量,计算方式为: c o s ( θ ) = ∑ i = 1 n ( x i × y i ) ∑ i = 1 n x i 2 ∑ i = 1 n y i 2 cos(\theta) = \frac{\sum\limits_{i = 1}^n (x_i\times y_i)}{\sum\limits_{i = 1}^{n}x_i^2\sum\limits_{i = 1}^{n}y_i^2} cos(θ)=i=1∑nxi2i=1∑nyi2i=1∑n(xi×yi)。
泊松相关系数将两个数据看成是数据集,计算方式为: ρ X Y = E ( ( X − E X ) ( Y − E Y ) ) D X D Y \rho_{XY} = \frac{E((X - EX)(Y - EY))}{\sqrt{DX}\sqrt{DY}} ρXY=DX DY E((X−EX)(Y−EY))。
其中第一部分代码只需要按照公式复刻一遍即可。
泊松部分的代码需要认真看一眼实训,实训给出的样例中提及了日期,只需要复制另一个人的日期即可。
代码如下:
python
# 欧几里得相关系数
def euclidean(p, q):
# 如果两数据集数目不同,计算两者之间都对应有的数
same = 0
for i in p:
if i in q:
same += 1
# 计算欧几里德距离,并将其标准化
# ***********Begin**********
e = sum([(p[i] - q[i]) ** 2 for i in range(same)])
# ************End***********
return 1 / (1 + e ** 0.5)
print("欧几里得计算出的相似度为",euclidean([1, 2, 3, 4, 5], [2, 4, 6,8, 10]))
# 余弦相似度
def cosine_similarity(x, y):
xx = 0.0
yy = 0.0
xy = 0.0
for i in range(len(x)):
xx += x[i] * x[i]
yy += y[i] * y[i]
xy += x[i] * y[i]
xx_sqrt = xx ** 0.5
yy_sqrt = yy ** 0.5
cos = xy/(xx_sqrt*yy_sqrt)
return cos
print('余弦相关系数计算出的相似度为',cosine_similarity([5,3],[5,8]))
#泊松相关系数
import scipy
import numpy
from scipy.stats import pearsonr
########## Begin ##########
x =numpy.array([0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1])
########## End ##########
y =numpy.array([1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3])
r_row, p_value = pearsonr(x, y)
print ("用户(UID)84001033与用户(UID)84001003从2004/1/20到4/20/20这段日期的相似度为",r_row)
T2 基于相似度度量的商品推荐
这一关主要提及了 J a c c a r d Jaccard Jaccard 公式和余弦相似度,并在代码里要求复现余弦相似度的过程。
这个过程的核心思想是,将用户的喜欢商品列表转换成:每个商品被哪些用户喜欢。
我们将喜欢列表成为表 1 1 1,被喜欢列表成为表 2 2 2。
在构造系数的时候,对于两个不同的用户取二者表 1 1 1的交集计算数量加到一个横纵元素都为全体用户的表中,例如用户 A A A和用户 B B B共同喜欢三个商品 a , b , c a,b,c a,b,c,那么空表中 ( A , B ) , ( B , A ) (A,B),(B,A) (A,B),(B,A)都为 3 3 3。
然后我们用余弦计算公式把该表中所有整数都计算为余弦相似度。
对于每个用户 P P P在推荐商品的时候,对于一个商品 x x x, x x x的表 2 2 2中每一个用户除去 P P P的余弦值加在一起,成为 x x x的推荐度。取所有 P P P的表 1 1 1里没有的商品中推荐值最大的商品推荐给 P P P即可。
python
# (一):找到与目标用户兴趣相似的用户集合
########## Begin ##########
# 目标用户(A用户喜欢a、b、d商品)
target_user = {'A':['a','b','d']}
print(f'目标用户:{target_user}')
# 相似用户用户()
alike_user = {'B': ['a','c'],'C': ['b','e'],'D':['c','d','e']}
print(f'相似用户:{alike_user}')
########## End ##########
# 倒排表
"""
a A B C
"""
# 总共商品类型
key_value = []
value1 = target_user.values()
for item in value1:
for good in item:
# 如果不再就添加到键值
if good not in key_value:
key_value.append(good)
value2 = alike_user.values()
########## Begin ##########
for item in value2:
for good in item:
if good not in key_value:
key_value.append(good)
print(f'总共商品类型:{key_value}')
########## End ##########
new_table = []
for good in key_value:
new_dict = {}
user_list = []
# 目标用户
key_value_list = target_user.items()
# print(key_value_list)
for key_value in key_value_list:
key = key_value[0]
value = key_value[1]
if (good in value) & (key not in user_list):
user_list.append(key)
# new_dict[good] = user_list
# new_table.append(new_dict)
# 相似用户
key_value_list = alike_user.items()
# print(key_value_list)
for key_value in key_value_list:
key = key_value[0]
value = key_value[1]
if (good in value) & (key not in user_list):
user_list.append(key)
new_dict[good] = user_list
new_table.append(new_dict)
print(new_table)
########## Begin ##########
# 计算余弦相似度
import pandas as pd
import numpy as np
df = pd.DataFrame(data=np.zeros((4,4)), columns=['A','B','C','D'],index=['A','B','C','D'])
print(df)
# 统计交集
for item in new_table:
print(list(item.values())[0])
label = list(item.values())[0]
x = label[0]
y = label[1]
df.loc[x,y] = df.loc[x,y] + 1
df.loc[y,x] = df.loc[y,x] + 1
print(df)
########## End ##########
# 计算两两之间的相似度
count_list = {}
for i in ['A','B','C','D']:
count = df.loc[i,:].sum()
count_list[i] = count
print(count_list)
# 计算余弦相似度
########## Begin ##########
for i in ['A','B','C','D']:
for j in ['A', 'B', 'C', 'D']:
df.loc[i,j] = df.loc[i,j] / np.sqrt(count_list[i] * count_list[j])
########## End ##########
print(df)
########## Begin ##########
# 计算p(A,c)和p(A,e)
p_Ac = df.loc['A','B'] + df.loc['A','D']
print(f'p(A,c):{p_Ac}')
p_Ae = df.loc['A','C'] + df.loc['A','D']
print(f'p(A,e):{p_Ae}')
########## End ##########
if p_Ac > p_Ae:
print("用户A对c商品更感兴趣,将e商品推荐给A")
elif p_Ac < p_Ae:
print("用户A对e商品更感兴趣,将e商品推荐给A")
else:
print("用户A对c商品和e商品同样感兴趣!")
补充:
- 代码里有个明显笔误,无论如何都推荐商品 e e e。
- 这种推荐方法其实很容易有两个商品的值都一样的情况,当然在本题本题中不会出现。一般我们应对这种问题的处理方法就是将两个商品打包或者继续继续观察这两个商品的受众。