关于python的容器,印象最深刻的就是字典,dict和java的map极为相似,但是在用法上面有一些不同,现在将我做过的练习Po出来,作为一个记录

问题提出

图片.png

题目来自董付国老师的python小屋ppt,侵删


总结一下这个问题的关键——如何给一个用户推荐一部电影

  • 找到和这个被测用户口味相同的其他用户,也就是说,其他用户必须和这个用户看过的电影的交集尽可能大,当然这种情况很可能挑出不止一个用户
  • 在上面找到的这些用户里面,选出和这个被测用户,打分最相似的。也就是说,最后挑选出来的用户,不仅要看过被测用户看过的电影,喜爱程度(打分)还要相近。
  • 最后挑出一个最相近的用户,在这个用户看过,但是被测用户没看过的集合里面,挑选出一部电影来给这个用户

到此,已经明白了我们需要做什么

  • 要设计算法,求出看过电影交集的最大的用户的集合
  • 设计算法,求出打分最相近的用户的集合
  • 设计算法,在最相似的用户没看过的电影里面挑出没看过,但是打分最高的电影

知识点大纲

字典是什么

  1. 可变容器,存储任意可变对象
  2. key唯一,value不唯一,思维从java的map迁移即可
  3. 使用items()方法将字典每一项转化tuple,可用下标去访问。
  4. 使用keys()方法作用在dict上,可以获取这个dict的所有key

怎么求字典交集

如下,使用 & 操作符即可

  1. data1 = {'movie1':9,'movie3':10,'movie5':4}
  2. data2 = {'movie1':4,'movie2':3,'movie5':4}
  3. print(data1.keys()&data2.keys())

在python3里面,你甚至可以第三行其中任意一个写成print(data1&data2.keys()),也是允许的,这里求取的就是两个字典的key的交集。最后输出

{'movie1', 'movie5'}

怎么求最相似的打分

首先观察原始的数据结构

  1. data1 = {'movie1':9,'movie3':10,'movie5':4}
  2. data2 = {'movie1':4,'movie2':3,'movie5':4}

这种样子的数据结构,和n维空间的向量非常相似,这种比较向量的距离来作为向量的相似度的做法非常常见。

我们可以使用求两个向量之间的方差,方差越小,说明两者的电影口味相似度高。

  1. f = lambda item : sum((item.get(i)-data2.get(i))**2 for i in item.keys() & data2.keys())
  2. num = f(data1)

比如上面代码,就可求出上面两组数据,相同key之间(movie1和movie5)的方差,为25


怎么求差集

当我们知道哪个用户和被测用户最相似之后,我们需要在这个用户看过的其他电影中选取分数最高的。

提取关键字,我们可以提炼出两条信息

  • 这个用户看过的其他电影,也即是这个用户和被测用户的差集
  • 分数最高

到这里,我们可以自然的想到做法

max函数,但是自定义一个rule,将key的规则变成 获取这个dict的value,也就是dict[key]


其他重要知识点

lambda函数

匿名函数、lambda关键字后面跟着的是参数,:后面跟着的是想要返回的东西,可能是整数,可能是容器…

常和 filter以及max/min函数配合服用。

filter函数

过滤器,我理解为剔除器,将列表,以及保留规则(函数或者lambda函数)传入,即可处理一个容器集合。将符合保留条件的保留下来。

max函数

求最大值,这里要讲的不是简单意义上的max函数,而是自定义的max函数。

自定义max函数需要补充key属性,一般是个函数或者lambda。自定义max函数传的两个参数中:第一个是集合,也就是返回的是该集合中的元素;第二个我理解为元素映射的值的指示函数,自定义max函数会根据这个key里面的函数,获取前面第一个参数中元素对应的值,这样才能比较大小。

在字典里面,我们通常需要返回key,但是只能比较value。上面这种自定义的max函数完美的符合这种需求


董教授的代码

图片.png

董教授的代码真的很pythonic,尤其是红线画出来的地方。

将求最多相似电影和最小方差评分在一个lambda函数里,最后用一个min函数就完成了最相似用户的求取。将求最大值转化为求最小值加负号的思想真的很值得学习

但是个人觉得对于我这种正在学习阶段的人来说,这种代码在可理解性上差了点,小技巧以后自然会习得,但是当务之急是熟练这些高级函数和容器的使用。

思考和改进思路

那么,我将怎么组织我的代码?

  1. 生成用户数据,以及被测用户数据,全部随机生成,使用random库
  2. 先求最多相似的用户,将每个用户和被测用户的重复的key计数,求出最大重复数
  3. 使用filter,将用户数据里面符合最大重复数的用户保留,其余剔除
  4. 在新的用户数据里面求出最小的方差,将那个最小方差的用户提取出来
  5. 在这个提取出来的用户看过的电影中,找出被测用户没看过,并且这个用户评分最高的电影
  6. 结束

其中,2-3-4是我讲董教授的代码步骤进行拆分的结果

我的代码

  1. from random import randint,randrange
  2. user_data={}
  3. for i in range(10):
  4. user_data['user'+str(i)]={}
  5. # 接下来给每个用户随机生成电影20次,每次可能和之前生成的某次电影编号相似
  6. for movie in range(10):
  7. # 给user0-user9,每个人看过的每个电影打上1-10分
  8. user_data['user'+str(i)]['movie'+str(randrange(3,9))] = randrange(1,11)
  9. print('所有用户数据'.center(100,'='))
  10. for i in user_data.items():
  11. print(i)
  12. #########################################################################################
  13. print('测试用户数据'.center(100,'='))
  14. test_user = {'movie'+str(randrange(3,9)):randrange(1,11) for movie in range(5)}
  15. print(test_user)
  16. #########################################################################################
  17. same_movie = [len(i[1].keys() & test_user.keys())for i in user_data.items()]
  18. max_same_number = max(same_movie)
  19. print('='.center(100,'='))
  20. print("每个用户和被测用户看过一样的电影数量"+str(same_movie)+" 用户最多看过 "+str(max_same_number)+" 部和被测用户一样的电影")
  21. #########################################################################################
  22. print('最多电影相似用户(处理后数据)'.center(100,'='))
  23. flt_func = lambda item:len(item[1].keys()&test_user)==max_same_number
  24. flt_func1 = lambda new_user_data:sum(((new_user_data[1].get(movie)-test_user.get(movie))**2 for movie in new_user_data[1].keys()&test_user))
  25. new_dict = filter(flt_func,user_data.items())
  26. new_user_data = dict(new_dict)
  27. for i in new_user_data.items():
  28. print(i)
  29. #########################################################################################
  30. print('最小欧拉距离'.center(100,'='))
  31. most_sim_user = min(new_user_data.items(),key=flt_func1)
  32. print(most_sim_user)
  33. #########################################################################################
  34. print('最后推荐的电影是'.center(100,'='))
  35. print(max(most_sim_user[1].keys()-test_user.keys(),key = lambda item:most_sim_user[1][item]))

全部代码共计50行以内,写的比较啰嗦,并且变量和函数命名比较丑陋。

以后的学习会多注意多参考。