集合的特点与数据要求
- 不允许出现重复元素
- 元素排列无特定顺序
- 只能包含不可变类型
a=['东风', '东风', '幺鸡', '红中', '发财', '白板', '东风']
a = set(a)
a
{'东风', '发财', '幺鸡', '白板', '红中'}
a = list(a)
a
['白板', '发财', '东风', '幺鸡', '红中']
a = {1, 'xyz', (25, 37)}
a = {1, 'xyz', (25, 37), [2, 3]}
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-321b2a8ee442> in <module>
----> 1 a = {1, 'xyz', (25, 37), [2, 3]}
TypeError: unhashable type: 'list'
列表属于可变类型的数据,不能作为集合的元素
可变对象列表
、集合
、字典
等类型的数据,其在内存中的内容可以随时修改、扩充
a = [['中国', 'CHN'], ['美国', 'USA'], ['英国', 'UK']]
s = set(a)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-b30da0cf6bfa> in <module>
1 a = [['中国', 'CHN'], ['美国', 'USA'], ['英国', 'UK']]
----> 2 s = set(a)
TypeError: unhashable type: 'list'
假如这个转换能够成功,得到的集合就是:
{[‘中国’, ‘CHN’], [‘美国’, ‘USA’], [‘英国’, ‘UK’]}
然而列表属于可变类型的数据,不能作为集合的元素
set类型与集合运算
交集、并集、差集
交集运算符:&
空字典:{}
空集合:set()
判断变量a是否为空集合:
- if a == set():
- if len(a) == 0:
if a:
print('a不是空集合')
else:
print('a是空集合')
a = {'A', 'B', 'C', 'D'}
b = {'B', 'E', 'D', 'F'}
c = a & b
c
{'B', 'D'}
并集运算符:|
a = {'A', 'B', 'C', 'D'}
b = {'B', 'E', 'D', 'F'}
c = a | b
c
{'A', 'B', 'C', 'D', 'E', 'F'}
差集运算符:-
a = {'A', 'B', 'C', 'D'}
b = {'B', 'E', 'D', 'F'}
c = a - b
c
{'A', 'C'}
射雕 = {'黄药师', '欧阳锋', '段智兴', '洪七公', '王重阳'}
神雕 = {'黄药师', '杨过', '段智兴', '郭靖', '周伯通'}
射雕 - 神雕 # 差集
{'欧阳锋', '洪七公', '王重阳'}
神雕 - 射雕 # 差集
{'周伯通', '杨过', '郭靖'}
射雕 & 神雕 # 交集
{'段智兴', '黄药师'}
射雕 | 神雕 # 并集
{'周伯通', '杨过', '欧阳锋', '段智兴', '洪七公', '王重阳', '郭靖', '黄药师'}
集合对象的方法
集合对象的各种方法可以方便实现添加、修改、删除、判断等操作。
a = {2, 3, 5}
b = {1, 2, 3, 4, 5, 6, 7}
a.issubset(b)
True
b.issuperset(a)
True
方法或运算符 | 作用 | 示例 |
---|---|---|
add | 向集合内添加元素 | s.add(x) |
remove | 从集合内删除指定数值的元素 | s.remove(x) |
pop | 让集合删除并返回一个元素 (由 Python自己决定删除哪个 元素,程序员不能指定) |
x = s.pop() |
clear | 从集合内移除所有元素 | |
issubset | 判断本集合是否为另一个集合 的子集,是则返回True |
a.issubset(b) |
<= | 同 issubset | a <= b |
issuperset | 判断本集合是否为另一个集合 的父集,是则返回True |
a.issuperset(b) |
>= | 同 issuperset | a >= b |
union | 返回本集合与其他集合的并集 | a.union(b, c) |
intersection | 返回本集合与其他集合的交集 | |
difference | 返回本集合与其他集合的差集 | |
isdisjoint | 判断本集合与其他集合的交集是否为空(即不相交) |
在岗员工统计
import xlwings as xw
app = xw.App()
wb = app.books.open('员工名单.xlsx')
ws = wb.sheets[0]
n1 = ws.range('A4:A16901').value
n2 = ws.range('B4:B16890').value
# 离职员工
ws.range('E2').value = list(set(n1) - set(n2))
# 新员工
ws.range('F2').value = list(set(n2) - set(n1))
# 老员工
ws.range('G2').value = list(set(n1) & set(n2))
wb.save('员工名单.xlsx')
wb.close()
app.quit()
【注意】
在xlwings中,将一维列表写入range,默认情况下是横向书写,即写到一行里。
如果要将一维列表纵向写入一列,应使用以下写法:
range().options(transpose=True).value = list(一维列表)
指定本次写入后用“转置”选项
使用 collections. Counter(计数器)实现频次统计
collections模块常用工具:
有序字典 OrderedDict,既能像字典样存放键值对,又能像列表一样按下标引用
双向列表 deque,类似列表,但是插入删除非常快
计数器 Counter,用法:Counter(可迭代对象),返回特殊的字典类型,用法与字典完全相同,额外支持加法、减法、交集、并集等运算
from collections import Counter
a = ['张三', '李四', '张三', '王五', '张三', '李四', '张三', '王五', '王五', '张三', '赵六', '赵六']
c = Counter(a)
c
Counter({'张三': 5, '李四': 2, '王五': 3, '赵六': 2})
c.values()
dict_values([5, 2, 3, 2])
day1 = ['李四', '张三', '李四', '李四', '王五', '王五', '田七', '张三', '刘八', '田七', '李四']
day2 = ['张三', '李四', '张三', '王五', '张三', '李四', '张三', '王五', '王五', '张三', '赵六', '赵六']
c1 = Counter(day1)
c1
Counter({'李四': 4, '张三': 2, '王五': 2, '田七': 2, '刘八': 1})
c2 = Counter(day2)
c2
Counter({'张三': 5, '李四': 2, '王五': 3, '赵六': 2})
c1 + c2
Counter({'李四': 6, '张三': 7, '王五': 5, '田七': 2, '刘八': 1, '赵六': 2})
Counter对象之间可以进行加减运算,按照“同类合并”的方式对各个键的频次进行加减。不过在减法运算中,如果某个元素相减后为负数或零,则不出现在结果集合中。此外Counter对象之间还可以进行交集和并集运算。
二维可迭代对象扁平化
应用场景:
max函数处理二维列表时,并不去查找最大元素,而是比较每个子列表的第一项返回第一项最大的子列表。
如果第一项都相同,则顺次比较第二项,如此类推……
如何使用max函数求取二维列表中的最大值?
a = [[1, 5], [3, 4], [2, 1, 3]]
max(a)
[3, 4]
列表生成式
a = [[1, 5], [3, 4], [2, 1, 3]]
b = [max(x) for x in a]
c = max(b)
c
5
使用 itertools.chain,将二维列表合并为一维列表
itertools:处理可迭代对象的工具
chain:串联参数
from itertools import chain
a = [1, 3, 5]
b = [2, 4, 6]
c = [3, 6, 9]
x = chain(a, b, c)
x
# chain函数返回的不是列表,而是Chain类型的可迭代对象
<itertools.chain at 0x1ceac581278>
x = list(x)
x
[1, 3, 5, 2, 4, 6, 3, 6, 9]
a = [[1, 5], [3, 4], [2, 1, 3]]
x = list(chain(a))
x
[[1, 5], [3, 4], [2, 1, 3]]
解包
x = list(chain(*a)) # chain(*a)相当于chain(a[0], a[1], a[2])
x
[1, 5, 3, 4, 2, 1, 3]
a = [[1, 5], [3, [4, 5, 6]], [2, [2, 3, [3, 5, 1]]]]
x = list(chain(*a))
x
[1, 5, 3, [4, 5, 6], 2, [2, 3, [3, 5, 1]]]
对于维度复杂的容器,不能使用chain方法,需要自己编写递归程序、或使用第三方模块。
练习
练习1:集合计算考勤名单
请先下载 某企业考勤记录文件 。该文件的“考勤统计”工作表中,B、C、D 三列分别记录了三周的出勤记录。因为每周有5天,所以一个员工在一周内可能出勤多次。
请编写程序,使用print输出以下统计结果:
第一周出过勤,但第二周完全缺勤的人员名单(即第二周从未出现过该名字)
第二周出过勤,但第一周完全缺勤的人员名单
从第一周到第三周全都出过勤、从未缺勤的人员名单
第一周和第二周全都出过勤、但第三周完全缺勤的人员名单
从第一周到第三周,所有曾经出勤(即使只在一周出过勤)的人员总名单
完成之后,请改写程序,将结果直接写入Excel工作表(自行指定行列位置)。
import xlwings as xw
app = xw.App()
wb = app.books.open('python02_19_01.xlsx')
ws = wb.sheets[0]
w1 = ws.range('B3:B30').value
w2 = ws.range('C3:C30').value
w3 = ws.range('D3:D30').value
at1ab2 = list(set(w1) - set(w2))
at2ab1 = list(set(w2) - set(w1))
at123 = list(set(w1) & set(w2) & set(w3))
at12ab3 = list((set(w1) | set(w2)) - set(w3))
atonce = list(set(w1) | set(w2) | set(w3))
ws.range('E2').options(transpose = True).value = at1ab2
ws.range('F2').options(transpose = True).value = at2ab1
ws.range('G2').options(transpose = True).value = at123
ws.range('H2').options(transpose = True).value = at12ab3
ws.range('I2').options(transpose = True).value = atonce
wb.save('python02_19_01_统计.xlsx')
wb.close()
app.quit()
at1ab2
['王五', '崔九']
at2ab1
['赵六']
at123
['李四', '刘八', '黄大', '田七', '王二', '顾十']
at12ab3
['张三']
atonce
['李四', '崔九', '王五', '黄大', None, '田七', '顾十', '刘八', '赵六', '王二', '张三']
- 用 xlwings 将 B3:B30 、C3:C30 和 D3:D30 读入三个列表;
- 用 set() 将三个列表转换为三个集合,比如 s1、s2、s3 ;
- 问题1 即 s1-s2 ,问题2 即 s2-s1 ,问题3 即 s1&s2&s3 ,问题4 即 s1&s2-s3 ,问题5 即 s1|s2|s3 。
作业2:Counter统计出勤次数
仍然使用上一作业中已经下载过的 某企业考勤记录文件 。请使用Counter完成以下统计工作,并将结果print到屏幕上:
统计第一周每个人的出勤次数,以字典形式打印在屏幕上;
统计刘八在第一周、第二周和第三周分别出勤几次,把三个数字打印在屏幕上;
统计每个人在三周里的总出勤次数,即每周出勤次数之和;
统计第三周比第二周出勤更多的人员名单,比如刘八在第三周出勤5次,而在第二周只出勤2次,所以应当显示在屏幕上。
from collections import Counter
count1 = Counter(w1)
countliu1 = count1['刘八']
count2 = Counter(w2)
countliu2 = count2['刘八']
count3 = Counter(w3)
countliu3 = count3['刘八']
count_all = count1 + count2 + count3
count3_more = count3 - count2
print(count1)
Counter({'田七': 5, '崔九': 4, '王二': 3, '张三': 3, '王五': 3, '黄大': 3, '李四': 3, '刘八': 2, '顾十': 2})
print(countliu1)
2
print(countliu2)
2
print(countliu3)
4
print(count_all)
Counter({'田七': 12, '王二': 9, '黄大': 9, '李四': 9, '刘八': 8, '崔九': 8, '顾十': 8, '赵六': 8, '张三': 7, '王五': 5, None: 1})
print(count3_more)
Counter({'崔九': 4, '顾十': 2, '刘八': 2, '王五': 2, None: 1})
本练习的目的是掌握Counter类型基本用法,以及Counter对象之间的加减法。具体思路如下:
用 xlwings 将 B3:B30 、C3:C30 和 D3:D30 读入三个列表,比如 s1 、s2 和 s3 ;
用 Counter(s1) 即可完成问题1的统计要求;
如果让变量 d=Counter(s1),那么 d[‘刘八’] 就是刘八在第一周的出勤次数,或者可以直接写为 Counter(s1)[‘刘八’] 。其他各周以此类推。
总出席次数其实就是 Counter(s1)+Counter(s2)+Counter(s3) 。
使用 Counter(s3)-Counter(s2) ,就可以得到第三周与第二周每个人的出勤次数之差,而且只保留正数结果(即第三周出勤更多的人)。
以上每个任务的最终结果(一个Counter类型的特殊字典对象),都可以用 for k:v in d.items() 的形式逐个扫描其姓名和次数,从而逐个打印在屏幕上或写入到Excel中。
练习3:chain实现扁平化
仍然使用上一作业中已经下载过的 某企业考勤记录文件 。请思考并试验:
如果使用 xlwings 一次性读入 range(‘B3:D30’),那么得到的就是一个二维列表。接下来能否直接从这个二维列表中得到每个人的三周总出勤次数(即第一周、第二周和第三周出勤次数之和)?
import xlwings as xw
app = xw.App()
wb = app.books.open('python02_19_01.xlsx')
ws = wb.sheets[0]
data = ws.range('B3:D30').value
from itertools import chain
data = list(chain(*data))
count_all = Counter(data)
print(count_all)
Counter({'田七': 12, '王二': 9, '黄大': 9, '李四': 9, '赵六': 8, '刘八': 8, '崔九': 8, '顾十': 8, '张三': 7, '王五': 5, None: 1})