- Python程序设计基础与应用
- 董付国
- 2224字
- 2022-06-01 16:55:56
3.5 集合
集合(set)属于Python无序可变序列,使用一对大括号作为定界符,元素之间使用逗号分隔,同一个集合内的每个元素都是唯一的,不允许重复。另外,集合中只能包含数字、字符串、元组等不可变类型的数据,而不能包含列表、字典、集合等可变类型的数据。
3.5.1 集合对象的创建与删除
直接将集合赋值给变量即可创建一个集合对象。
> > > a = {3, 5} #创建集合对象
也可以使用set()函数将列表、元组、字符串、range对象等其他可迭代对象转换为集合,如果原来的数据中存在重复元素,则在转换为集合的时候只保留一个;如果原序列或迭代对象中有不可散列的值,无法转换成为集合,抛出异常。
> > > a_set = set(range(8, 14)) #把range对象转换为集合 > > > a_set {8, 9, 10, 11, 12, 13} > > > b_set = set([0, 1, 2, 3, 0, 1, 2, 3, 7, 8]) #转换时自动去掉重复元素 > > > b_set {0, 1, 2, 3, 7, 8} > > > x = set() #空集合
当不再使用某个集合时,可以使用del命令删除整个集合。
3.5.2 集合操作与运算
(1)集合元素增加与删除
使用集合对象的add()方法可以增加新元素,如果该元素已存在则忽略该操作,不会抛出异常;update()方法用于合并另外一个集合中的元素到当前集合中,并自动去除重复元素。例如:
> > > s = {1, 2, 3} > > > s.add(3) #添加元素,重复元素自动忽略 > > > s {1, 2, 3} > > > s.update({3,4}) #更新当前字典,自动忽略重复的元素 > > > s {1, 2, 3, 4}
集合对象的pop()方法用于随机删除并返回集合中的一个元素,如果集合为空则抛出异常;remove()方法用于删除集合中的元素,如果指定元素不存在则抛出异常;discard()用于从集合中删除一个特定元素,如果元素不在集合中则忽略该操作。
> > > s.discard(5) #删除元素,不存在则忽略该操作 > > > s {1, 2, 3, 4} > > > s.remove(5) #删除元素,不存在就抛出异常 KeyError: 5 > > > s.pop() #删除并返回一个元素 1
(2)集合运算
内置函数len()、max()、min()、sum()、sorted()、map()、filter()、enumerate()等也适用于集合。另外,Python集合还支持数学意义上的交集、并集、差集等运算。例如:
> > > a_set = set([8, 9, 10, 11, 12, 13]) > > > b_set = {0, 1, 2, 3, 7, 8} > > > a_set | b_set #并集 {0, 1, 2, 3, 7, 8, 9, 10, 11, 12, 13} > > > a_set & b_set #交集 {8} > > > a_set - b_set #差集 {9, 10, 11, 12, 13} > > > a_set ^ b_set #对称差集 {0, 1, 2, 3, 7, 9, 10, 11, 12, 13}
需要注意的是,关系运算符>、>=、<、<=作用于集合时表示集合之间的包含关系,而不是集合中元素的大小关系。例如,两个集合A和B,如果A<B不成立,不代表A>=B就一定成立。
> > > {1, 2, 3} < {1, 2, 3, 4} #真子集 True > > > {1, 2, 3} <= {1, 2, 3} #子集 True
3.5.3 集合应用案例
例3-5 使用集合快速提取序列中的唯一元素。
问题描述:所谓唯一元素,这里是指不重复的元素。也就是说,如果原序列中某个元素出现多次,那么只保留一个。
基本思路:首先使用列表推导式生成一个包含100个10000以内随机数的列表,然后把列表转换为集合,自动去除重复元素。
> > > import random #生成100个10000以内的随机数 > > > listRandom = [random.choice(range(10000)) for i in range(100)] > > > newSet = set(listRandom) > > > print(newSet)
运行结果如下:
{1538, 5130, 9746, 6162, …} #略去更多结果
例3-6 获取指定范围内一定数量的不重复数字。
基本思路:主要利用集合元素不重复的特点,使用random模块中的randint()函数生成一个随机数,然后使用集合的add()方法将该随机数放入集合。如果集合中已经存在该数字则会自动忽略,如果不存在才会放入。
1. import random 2. 3. def randomNumbers(number, start, end): 4. ''’使用集合来生成number个介于start和end之间的不重复随机数’'' 5. data = set() 6. while len(data)<number: 7. element = random.randint(start, end) 8. data.add(element) 9. 10. return data 11. data = randomNumbers(10, 1, 100) 12. print(data)
某次运行结果如下:
{67, 68, 35, 3, 71, 10, 81, 21, 24, 62}
当然,如果在项目中需要这样一个功能的时候,还是直接使用random模块的sample()函数更好一些。
> > > import random > > > random.sample(range(1000), 20) #在指定分布中选取不重复元素
运行结果如下:
[61, 538, 873, 815, 708, 609, 995, 64, 7, 719, 922, 859, 807, 464, 789,651, 31, 702, 504, 25]
例3-7 测试指定列表中是否包含非法数据。
问题描述:这里所谓非法数据,是指不允许出现的数据。
基本思路:代码中假设lstColor是允许出现的合法数据,然后使用使用列表推导式生成一些随机数据,最后利用集合的差集运算来测试colors中是否只包含lstColor中的数据。
1. import random 2. 3. lstColor = ('red', 'green', 'blue') 4. colors = [random.choice(lstColor) for i in range(10000)] 5. 6. if set(colors)-set(lstColor): #转换为集合之后再比较 7. print('error')
例3-8 电影评分与推荐。
问题描述:假设已有大量用户对若干电影的评分数据,现有某用户,也看过一些电影并进行过评分,要求根据已有打分数据为该用户进行推荐。
基本思路:用基于用户的协同过滤算法,也就是根据用户喜好来确定与当前用户最相似的用户,然后再根据最相似用户的喜好为当前用户进行推荐。本例采用字典来存储打分数据,格式为{用户1:{电影名称1:打分1, 电影名称2:打分2,…}, 用户2:{…}},首先在已有数据中查找与当前用户共同打分电影(使用集合的交集运算)数量最多的用户,如果有多个这样的用户就再从中选择打分最接近(打分的差距最小)的用户。代码中使用到了random模块中的randrange()函数,用来生成指定范围内的一个随机数。
1. from random import randrange 2. 3. #历史电影打分数据,一共10个用户,每个用户对3~9个电影进行评分 4. #每个电影的评分最低1分、最高5分,这里是字典推导式和集合推导式的用法 5. data = {'user'+str(i):{'film'+str(randrange(1, 15)):randrange(1, 6) 6. for j in range(randrange(3, 10))} 7. for i in range(10)} 8. 9. #模拟当前用户打分数据,为5部随机电影打分 10. user = {'film'+str(randrange(1, 15)):randrange(1,6) for i in range(5)} 11. #最相似的用户及其对电影打分情况 12. #两个用户共同打分的电影最多 13. #并且所有电影打分差值的平方和最小 14. f = lambda item:(-len(item[1].keys()&user), 15. sum(((item[1].get(film)-user.get(film))**2 16. for film in user.keys()&item[1].keys()))) 17. similarUser, films = min(data.items(), key=f) 18. 19. #在输出结果中,第一列表示两个人共同打分的电影的数量 20. #第二列表示二人打分之间的相似度,数字越小表示越相似 21. #然后是该用户对电影的打分数据 22. print('known data'.center(50, '=')) 23. for item in data.items(): 24. print(len(item[1].keys()&user.keys()), 25. sum(((item[1].get(film)-user.get(film))**2 26. for film in user.keys()&item[1].keys())), 27. item, 28. sep=':') 29. print('current user'.center(50, '=')) 30. print(user) 31. print('most similar user and his films'.center(50, '=')) 32. print(similarUser, films, sep=':') 33. print('recommended film'.center(50, '=')) 34. #在当前用户没看过的电影中选择打分最高的进行推荐 35. print(max(films.keys()-user.keys(), key=lambda film: films[film]))
某次运行结果如图3-3所示,在所有已知用户中,user7和user9都与当前用户共同打分的电影数量最多,都是3。但是,user7与当前用户打分的距离是9,而user9的距离是20,所以user7与当前用户更接近一些,最终选择该用户进行推荐。
图3-3 运行结果