集合(set)属于Python无序可变序列,使用一对大括号作为定界符,元素之间使用逗号分隔,同一个集合内的每个元素都是唯一的,元素之间不允许重复。
集合中只能包含数字、字符串、元组等不可变类型(或者说可哈希)的数据,而不能包含列表、字典、集合等可变类型的数据。
集合对象的创建与删除
直接将集合赋值给变量即可创建一个集合对象。
>>> 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()
集合操作与运算
(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()用于从集合中删除一个特定元素,如果元素不在集合中则忽略该操作;
clear()方法清空集合删除所有元素。
>>> s.discard(5) #删除元素,不存在则忽略该操作
>>> s
{1, 2, 3, 4}
>>> s.remove(5) #删除元素,不存在就抛出异常
KeyError: 5
>>> s.pop() #删除并返回一个元素
1
(2)集合运算
>>> 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.union(b_set) #并集
{0, 1, 2, 3, 7, 8, 9, 10, 11, 12, 13}
>>> a_set & b_set #交集
{8}
>>> a_set.intersection(b_set) #交集
{8}
>>> a_set.difference(b_set) #差集
{9, 10, 11, 12, 13}
>>> a_set - b_set
{9, 10, 11, 12, 13}
>>> a_set.symmetric_difference(b_set) #对称差集:去掉中间重叠部分
{0, 1, 2, 3, 7, 9, 10, 11, 12, 13}
>>> a_set ^ b_set
{0, 1, 2, 3, 7, 9, 10, 11, 12, 13}
>>> x = {1, 2, 3}
>>> y = {1, 2, 5}
>>> z = {1, 2, 3, 4}
>>> x < y #比较集合大小/包含关系
False
>>> x < z #真子集
True
>>> y < z
False
>>> {1, 2, 3} <= {1, 2, 3} #子集
True
集合应用案例(4个)
例1 使用集合快速提取序列中单一元素,即提取出序列中所有不重复元素。如果使用传统方式的话,需要编写下面的代码:
>>> import random
#生成100个介于0到9999之间的随机数
>>> listRandom = [random.choice(range(10000)) for i in range(100)]
>>> newSet = set(listRandom)
>>> print(newSet)
例2 返回指定范围内一定数量的不重复数字。
import random
def randomNumbers(number, start, end):
'''使用集合来生成number个介于start和end之间的不重复随机数'''
data = set()
while len(data)<number:
element = random.randint(start, end)
data.add(element)
return data
data = randomNumbers(10, 1, 100)
print(data)
例3 测试指定列表中是否包含非法数据。
import random
lstColor = ('red', 'green', 'blue')
colors = [random.choice(lstColor) for i in range(10000)]
for item in colors: #遍历列表中的元素并逐个判断
if item not in lstColor:
print('error:', item)
break
if (set(colors)-set(lstColor)): #转换为集合之后再比较
print('error')
例4 电影评分与推荐。
问题描述:假设已有大量用户对若干电影的评分数据,现有某用户,也看过一些电影并进行过评分,要求根据已有打分数据为该用户进行推荐。
基本思路:用基于用户的协同过滤算法,也就是根据用户喜好来确定与当前用户最相似的用户,然后再根据最相似用户的喜好为当前用户进行推荐。本例采用字典来存放打分数据,格式为{用户1:{电影名称1:打分1, 电影名称2:打分2,…}, 用户2:{…}},首先在已有数据中查找与当前用户共同打分电影(使用集合的交集运算)数量最多的用户,如果有多个这样的用户就再从中选择打分最接近(打分的差距最小)的用户。代码中使用到了random模块中的randrange()函数,用来生成指定范围内的一个随机数。
from random import randrange
#历史电影打分数据,一共10个用户,每个用户对3到9个电影进行评分
#每个电影的评分最低1分最高5分,这里是字典推导式和集合推导式的用法
data = {'user'+str(i):{'film'+str(randrange(1, 15)):randrange(1, 6)
for j in range(randrange(3, 10))}
for i in range(10)}
#模拟当前用户打分数据,为5部随机电影打分
user = {'film'+str(randrange(1, 15)):randrange(1,6) for i in range(5)}
#最相似的用户及其对电影打分情况
#两个用户共同打分的电影最多
#并且所有电影打分差值的平方和最小
f = lambda item:(-len(item[1].keys()&user),
sum(((item[1].get(film)-user.get(film))**2
for film in user.keys()&item[1].keys())))
similarUser, films = min(data.items(), key=f)
#在输出结果中,第一列表示两个人共同打分的电影的数量
#第二列表示二人打分之间的相似度,数字越小表示越相似
#然后是该用户对电影的打分数据
print('known data'.center(50, '='))
for item in data.items():
print(len(item[1].keys()&user.keys()),
sum(((item[1].get(film)-user.get(film))**2
for film in user.keys()&item[1].keys())),
item,
sep=':')
print('current user'.center(50, '='))
print(user)
print('most similar user and his films'.center(50, '='))
print(similarUser, films, sep=':')
print('recommended film'.center(50, '='))
#在当前用户没看过的电影中选择打分最高的进行推荐
print(max(films.keys()-user.keys(), key=lambda film: films[film]))