传送门:Python关联分析
关联规则推荐的本来就是很热门的产品,因为代表同时发生频率越高,关联性越强。在互联网时代会背弃长尾效应,让差异扩大,2/8定律会一定程度上扩充至1/9,助长马太效应。所以推荐冷门产品会使用协同过滤。
具体实现步骤及代码如下:
- 第一步:计算两者之间的相似度(欧氏距离/皮尔逊相似度)
- 第二步:根据用户相似度,向目标用户推荐产品
代码如下:
- 数据准备:下载movielen数据集
解压读取movies.csv和ratings.csv文件
import pandas as pd
# 读取数据
movies = pd.read_csv(r'C:\Users\hexiaomin\Desktop\ml-latest-small\ml-latest-small\movies.csv')
ratings = pd.read_csv(r'C:\Users\hexiaomin\Desktop\ml-latest-small\ml-latest-small\ratings.csv') ##这里注意如果路径的中文件名开头是r,要转义。
![](https://img.haomeiwen.com/i20129852/35f129d8a6da434f.png)
![](https://img.haomeiwen.com/i20129852/07a118248a2c205c.png)
data = pd.merge(movies, ratings, on='movieId') # 通过量数据框之间的movieId连接
data[['userId', 'rating', 'movieId', 'title']].sort_values('userId').to_csv(r'C:\Users\hexiaomin\Desktop\ml-latest-small\ml-latest-small\data.csv', index=False)
# 采用Python字典来表示每位用户评论的电影和评分
file = open(r'C:\Users\hexiaomin\Desktop\ml-latest-small\ml-latest-small\data.csv', 'r', encoding='UTF-8')
# 读取data.csv中每行中除了名字的数据
data ={} ## 存放每位用户评论的电影和评分
for line in file.readlines()[1:]:
# 注意这里不是readline()
line = line.strip().split(',') # 去掉每行头尾空白
# 如果字典中没有某位用户,则使用用户ID来创建这位用户
if not line[0] in data.keys():
data[line[0]] = {line[3]:line[1]}
# 否则直接添加以该用户ID为key字典中
else:
data[line[0]][line[3]] = line[1]
# print(data)
![](https://img.haomeiwen.com/i20129852/21df539889a84129.png)
"""
计算任何两位用户之间的相似度,由于每位用户评论的电影不完全一样,所以首先要找到两位用户共同评论过的电影,
然后计算两者之间的欧式距离,最后计算两者之间的相似度
"""
from math import *
def Euclidean(user1, user2):
#取出两位用户评论过的电影和评分
user1_data = data[user1]
user2_data = data[user2]
distance = 0
# 找到两位用户都评论过的电影,并计算欧式距离
for key in user1_data.keys():
if key in user2_data.keys():
#注意,distance越小,表示两者越相似
distance += pow(float(user1_data[key])-float(user2_data[key]),2) # pow() 通过内置的方法直接调用,内置方法会把参数作为整型,而 math 模块则会把参数转换为 float。
return 1/(1+sqrt(distance)) # 这里返回值越大,相似度越大
# 计算某个用户与其他用户的相似度
def top10_simliar(userID):
res = []
for userid in data.keys():
# 排除与自己计算相似度
if not userid == userID:
simliar = Euclidean(userID, userid)
res.append((userid,simliar))
res.sort(key=lambda val:val[1], reverse=True) # 按照元组中第二个元素进行排序,默认从小到大,降序需要使用reverse=True
return res[:10]
RES = top10_simliar('1')
print(RES)
# 用户之间的相似度结果: 0表示两位的影评几乎一样,1表示没有共同影评
![](https://img.haomeiwen.com/i20129852/ef46f3dbded3cbef.png)
# 根据相似度来推荐用户:
def recommend(user):
# 相似度最高的用户
top_sim_user = top10_simliar(user)[0][0]
# 相似度最高的用户的观影记录
items = data[top_sim_user]
recommendations = []
# 筛选出该用户未观看的电影并添加到列表中
for item in items.keys():
if item not in data[user].keys():
recommendations.append((item, items[item]))
recommendations.sort(key=lambda val:val[1], reverse=True) # 按照评分排序
# 返回评分最高的10部电影
return recommendations[:10]
Recommendations = recommend('1')
print(Recommendations)
![](https://img.haomeiwen.com/i20129852/8aaba4eaa509ecb5.png)
分割线:使用皮尔逊相关系数代替欧氏距离
有时我们会碰到因为两个用户之间的数据,由于数据膨胀,一方数据大,一方数据小,但是两者之间呈现明显的线性关系。
我们引入Pearson相关系数来衡量两个变量之间的线性相关性。
Pearson系数:-1 ~ 1
皮尔逊相关系数
Python代码:
## 计算两用户之间的Pearson相关系数
def pearson_sim(user1,user2):
# 取出两位用户评论过的电影和评分
user1_data = data[user1]
user2_data = data[user2]
distance = 0
common = {}
# 找到两位用户都评论过的电影
for key in user1_data.keys():
if key in user2_data.keys():
common[key] = 1
if len(common) == 0:
return 0#如果没有共同评论过的电影,则返回0
n = len(common)#共同电影数目
print(n,common)
##计算评分和
sum1 = sum([float(user1_data[movie]) for movie in common])
sum2 = sum([float(user2_data[movie]) for movie in common])
##计算评分平方和
sum1Sq = sum([pow(float(user1_data[movie]),2) for movie in common])
sum2Sq = sum([pow(float(user2_data[movie]),2) for movie in common])
##计算乘积和
PSum = sum([float(user1_data[it])*float(user2_data[it]) for it in common])
##计算相关系数
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
R = pearson_sim('1','3')
print(R)
注意:通过Pearson系数得到用户的相似度和通过欧式距离得到结果可能不一样~
网友评论