为了巩固本章所学,安排了3个与字典有关的示例,给出了Excel VBA和Python两个版本的代码。
字典示例1-多行数据中唯一值出现次数汇总
图8-5所示工作表中B列至H列列出了多行人员姓名,其中有很多姓名是重复出现的。现在要求计算每个姓名出现的次数。
图8-5 多行数据中唯一值出现次数汇总
问题处理思路是创建一个字典,遍历所有姓名,如果字典的键中没有当前姓名,在字典中添加该姓名作为键,1作为值的键值对;如果已经有当前姓名,则该姓名作为键对应的值加1。
【Excel VBA】
示例文件的存放路径为Samples\ch08\Excel VBA\多行数据中唯一值出现次数汇总.xlsm。
Sub Test()
Dim arr '保存原始数据
Dim intI As Integer
Dim intJ As Integer
Dim d As Dictionary '字典对象
Set d = New Dictionary
On Error Resume Next
'获取原始数据
arr = ActiveSheet.Range("B2:H10").Value
For intI = 1 To UBound(arr, 1)
For intJ = 1 To UBound(arr, 2)
'遍历每个原始数据,如果不为空
If arr(intI, intJ) <> "" Then
'如果当前数据作为字典的键已经存在,个数加1
If d.Exists(arr(intI, intJ)) Then
d(arr(intI, intJ)) = d(arr(intI, intJ)) + 1
Else '如果不存在,添加键值对,个数为1
d.Add arr(intI, intJ), 1
End If
End If
Next
Next
'输出所有键,即唯一姓名及其对应的出现个数
ActiveSheet.Range("J2").Resize(d.Count, 1).Value = _
Application.WorksheetFunction.Transpose(d.Keys)
ActiveSheet.Range("K2").Resize(d.Count, 1).Value = _
Application.WorksheetFunction.Transpose(d.Items)
End Sub
运行程序,输出结果如图8-5所示工作表中J列和K列所示。
【Python】
本示例的数据文件存放路径为Samples\ch08\Python\多行数据中唯一值出现次数汇总.xlsx,py文件保存在相同目录下,文件名为sam08-01.py。
import xlwings as xw #导入xlwings包
#从constants类中导入Direction
from xlwings.constants import Direction
import os #导入os包
#获取本py文件的当前路径
root = os.getcwd()
#创建Excel应用,可见,没有工作簿
app=xw.App(visible=True, add_book=False)
#打开数据文件,可写
bk=app.books.open(fullname=root+\
r'\多行数据中唯一值出现次数汇总.xlsx',read_only=False)
sht=bk.sheets(1) #获取第1个工作表
d={}
arr=sht.range('B2:H10';).value #获取数据
for i in range(len(arr)): #行
for j in range(len(arr[0])): #列
#遍历每个原始数据,如果不为空
if arr[i][j] is not None:
if arr[i][j] in d: #如果在字典中已经存在
d[arr[i][j]]=d[arr[i][j]]+1 #个数加1
else: #如果不存在
d[arr[i][j]]=1 #添加键值对,值为1
#输出所有键,即唯一姓名及其对应的出现个数
sht.range('J2').options(transpose=True).value=list(d.keys())
sht.range('K2').options(transpose=True).value=list(d.values())
运行脚本,输出结果如图8-5所示工作表中J列和K列所示。
字典示例2-球员奖项汇总
图8-6所示工作表中A-C列列出了金球奖、最佳球员和金靴奖的得奖球员名单,现在要求根据该数据对每个球员获奖的奖项进行汇总。
图8-6 球员奖项汇总
问题处理思路是创建一个字典,遍历所有球员姓名,如果字典的键中没有当前姓名,在字典中添加该姓名作为键,当前奖项作为值的键值对;如果已经有当前姓名,则该姓名作为键对应的值添加当前奖项。
【Excel VBA】
示例文件的存放路径为Samples\ch08\Excel VBA\球员奖项汇总.xlsm。
Sub Test()
Dim intI As Long
Dim intR As Long
Dim d As Dictionary '字典对象
Set d = New Dictionary
Dim sht As Object
Set sht = ActiveSheet
On Error Resume Next
intR = sht.Range("A1").End(xlDown).Row '第1列数据行数
For intI = 2 To intR
'把第1列数据添加到字典,球员名为键,值为“金球奖”
d.Add sht.Cells(intI, 1).Value, "金球奖"
Next
intR = sht.Range("B1").End(xlDown).Row '第2列数据行数
For intI = 2 To intR
'判断第2列球员名在字典中是否已经存在
If d.Exists(sht.Cells(intI, 2).Value) Then
'如果已经存在,则对应键的值追加“,最佳球员”字符串
d(sht.Cells(intI, 2).Value) = _
d(sht.Cells(intI, 2).Value) & ",最佳球员"
Else
'如果不存在,则添加新的键值对
d.Add sht.Cells(intI, 2).Value, "最佳球员"
End If
Next
intR = sht.Range("C1").End(xlDown).Row '第3列数据行数
For intI = 2 To intR
'判断第3列球员名在字典中是否已经存在
If d.Exists(sht.Cells(intI, 3).Value) Then
'如果已经存在,则对应键的值追加“,金靴奖”字符串
d(sht.Cells(intI, 3).Value) = _
d(sht.Cells(intI, 3).Value) & ",金靴奖"
Else
'如果不存在,则添加新的键值对
d.Add sht.Cells(intI, 3).Value, "金靴奖"
End If
Next
'输出字典数据,第1列为球员名,第2列为对应奖项
sht.Range("E1").Resize(d.Count, 1).Value = _
Application.WorksheetFunction.Transpose(d.Keys)
sht.Range("F1").Resize(d.Count, 1).Value = _
Application.WorksheetFunction.Transpose(d.Items)
End Sub
运行程序,输出结果如图8-6所示工作表中E列和F列所示。
【Python】
本示例的数据文件存放路径为Samples\ch08\Python\球员奖项汇总.xlsx,py文件保存在相同目录下,文件名为sam08-02.py。
import xlwings as xw #导入xlwings包
#从constants类中导入Direction
from xlwings.constants import Direction
import os #导入os包
#获取本py文件的当前路径
root = os.getcwd()
#创建Excel应用,可见,没有工作簿
app=xw.App(visible=True, add_book=False)
#打开数据文件,可写
bk=app.books.open(fullname=root+\
r'\球员奖项汇总.xlsx',read_only=False)
sht=bk.sheets(1) #获取第1个工作表
#工作表中第1列数据最大行号
row_num_1=sht.api.Range('A1').End(Direction.xlDown).Row
#工作表中第2列数据最大行号
row_num_2=sht.api.Range('B1').End(Direction.xlDown).Row
#工作表中第3列数据最大行号
row_num_3=sht.api.Range('C1').End(Direction.xlDown).Row
d={}
#第1列数据添加进字典,球员名为键,奖项为值
for i in range(2,row_num_1):
d[sht.cells(i,1).value]='金球奖'
#第2列数据添加进字典
for i in range(2,row_num_2):
#如果球员名已经存在,追加奖项;否则添加键值对
if sht.cells(i,2).value in d:
d[sht.cells(i,2).value]=d[sht.cells(i,2).value]+',最佳球员'
else:
d[sht.cells(i,2).value]='最佳球员'
#第3列数据添加进字典
for i in range(2,row_num_3):
#如果球员名已经存在,追加奖项;否则添加键值对
if sht.cells(i,3).value in d:
d[sht.cells(i,3).value]=d[sht.cells(i,3).value]+',金靴奖'
else:
d[sht.cells(i,3).value]='金靴奖'
#输出字典数据
sht.range('E1').options(transpose=True).value=list(d.keys())
sht.range('F1').options(transpose=True).value=list(d.values())
运行脚本,输出结果如图8-6所示工作表中E列和F列所示。
字典示例3-研究课题的子课题汇总
图8-7所示工作表中第1-2列为研究课题及其子课题数据,现在要求将每个课题的子课题汇总,将各子课题用 | 连接成字符串,并计算子课题的个数。
图8-7 研究课题的子课题汇总
很明显,本问题是8.5.1和8.5.2两个示例问题的综合。需要构造两个字典,1个用于汇总各子课题名称,另1个汇总子课题个数。方法请参见前面两个示例,不再赘述。
【Excel VBA】
示例文件的存放路径为Samples\ch08\Excel VBA\研究课题的子课题汇总.xlsm。
Sub Test()
Dim intI As Integer
Dim d1 As Dictionary '字典对象,处理子课题名称
Set d1 = New Dictionary
Dim d2 As Dictionary '字典对象,处理子课题个数
Set d2 = New Dictionary
Dim sht As Object
Set sht = ActiveSheet
Dim intR As Integer
Dim strD
On Error Resume Next
'获取原始数据
intR = sht.Range("A1").End(xlDown).Row
For intI = 2 To intR
strD = sht.Cells(intI, 1).Value
'如果当前数据作为字典的键已经存在,
'追加新子课题名,个数加1
If d1.Exists(strD) Then
d1(strD) = d1(strD) & "|" & sht.Cells(intI, 2).Value
d2(strD) = d2(strD) + 1
Else '如果不存在,添加键值对,个数为1
d1.Add strD, sht.Cells(intI, 2).Value
d2.Add strD, 1
End If
Next
'输出课题名称
sht.Range("D2").Resize(d1.Count, 1).Value = _
Application.WorksheetFunction.Transpose(d1.Keys)
'输出课题对应的子课题名称
sht.Range("E2").Resize(d1.Count, 1).Value = _
Application.WorksheetFunction.Transpose(d1.Items)
'输出课题对应的子课题的个数
sht.Range("F2").Resize(d1.Count, 1).Value = _
Application.WorksheetFunction.Transpose(d2.Items)
End Sub
运行程序,输出结果如图8-7所示工作表中D-F列所示。
【Python】
本示例的数据文件存放路径为Samples\ch08\Python\研究课题的子课题汇总.xlsx,py文件保存在相同目录下,文件名为sam08-03.py。
import xlwings as xw #导入xlwings包
#从constants类中导入Direction
from xlwings.constants import Direction
import os #导入os包
#获取本py文件的当前路径
root = os.getcwd()
#创建Excel应用,可见,没有工作簿
app=xw.App(visible=True, add_book=False)
#打开数据文件,可写
bk=app.books.open(fullname=root+\
r'\研究课题的子课题汇总.xlsx',read_only=False)
sht=bk.sheets(1) #获取第1个工作表
#工作表中第1列数据最大行号
row_num=sht.api.Range('A1').End(Direction.xlDown).Row
d1={} #字典1,课题为键,子课题集合为值
d2={} #字典2,课题为键,子课题个数为值
for i in range(2,row_num):
it=sht.cells(i,1).value
#如果当前数据作为字典的键已经存在,
#追加新子课题名,个数加1
if it in d1:
d1[it]=d1[it]+'|'+sht.cells(i,2).value
d2[it]=d2[it]+1
else: #如果不存在,在字典中添加新的键值对
d1[it]=sht.cells(i,2).value
d2[it]=1
#输出课题名称
sht.range('D2').options(transpose=True).value=list(d1.keys())
#输出课题对应的子课题名称
sht.range('E2').options(transpose=True).value=list(d1.values())
#输出课题对应的子课题的个数
sht.range('F2').options(transpose=True).value=list(d2.values())
运行脚本,输出结果如图8-7所示工作表中D-F列所示。