场景描述
“2022年全国名优茶品质大赛”(也称为斗茶大赛) 于6月18日在山城重庆举行,来自全国50多家茶叶生产厂商参赛观摩。本届茶叶品质评审委员会是从国家专家库中随机抽取的8名茶叶审评专家组成。大赛将产生全国名优质茶金奖1名,银奖2名,铜奖3名。具体的比赛规则如下:
评审指标
评审指标共计9个。其中干评指标有:嫩度、色泽、条索、整碎、 净度等5个;湿评指标则是香气、 汤色、滋味和叶底4项指标。
指标分值
嫩度(10)、色泽(10)、条索(10)、整碎(10)、净度(10)、香气(15)、汤色(10)、滋味(15)、叶底(10),9项指标总分为100分。
指标权重
指标权重分配如下:嫩度(0.10)、色泽(0.05)、条索(0.05)、整碎(0.05)、净度(0.05)、香气(0.25)、汤色(0.10)、滋味(0.25)、叶底(0.10)
专家评审
每位专家需要对参评的20种茶叶的9项指标进行评分。表格样式如下所示:
综合得分
每种茶叶的综合得分按照以下公式计算:
$score =\sum^9_{n=1}(a_nb_n )$ 其中$n$取值$1-9$,代表$9$个评审指标编号;$a_n$代表指标$n$对应的权重;$b_n$代表所有专家对某种茶叶的指标$n$评分之和(需要去掉一个最高分和一个最低分),$score$代表某种茶叶的综合得分。 下面我们将使用Python编程的解决方案,实现评审数据采集,茶叶综合得分计算以及生成茶叶评比的排行榜。
编程思路
评审打分
采用Excel文件作为专家评分表,每位专家均要为参赛的20个茶样进行评分,每个茶样需要对9个指标逐一进行评分。
指标权重
本届茶叶品质大赛设立了9个评审指标,每个指标的权重设置如下所示。我们采用文本文件weight.txt方式保存,具体内容包括:
嫩度 0.10 色泽 0.05 条索 0.05 整碎 0.05 净度 0.05 香气 0.25 汤色 0.10 滋味 0.25 叶底 0.10
其中:指标名称与权重值之间是以’\t’制表符进行分隔。 在Windows环境下,我使用notepad记事本程序来创建文本文件weight.txt,在此需要特别说明的是保存文件要指定文件格式UTF-8,以便与本案例中的读取权重文件的函数配套使用。
数据采集
Python提供了多种方式可以访问和读取微软的Excel文件,本案例选用pandas模块读取专家Excel文件的评分表,将评审表内容以dataframe形式存放,供后续处理。
计算综合得分
我们需要读取所有评审专家的评分表,针对每个参赛茶样,汇总合计每一个指标的专家评审分值,同时在这个过程中需要去除一个最高分和一个最低分,根据综合得分计算公式,求出该茶叶品种的综合得分。
生成排行榜
根据所有茶叶品种的综合得分进行排名,显示或打印“2022年全国名优茶排行榜”和“2022年全国名优茶获奖名单”。
代码实现
程序代码
"""
tea_competition.py : 打望全国名优茶品质大赛
"""
import os
import pandas as pd
def main():
weight = get_weight('data/weight.txt')
all_xls = get_xls('data')
total_df = concat_df(all_xls)
rank = get_rank(total_df, weight)
print_rank(rank)
print_prizes(rank)
def get_weight(file):
"""
获取评审指标权重因子
"""
indicators = {}
with open(file, encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
name, weight = line.strip().split('\t') # ①
indicators[name] = float(weight)
return indicators
def get_xls(dirname):
"""
获取评审表文件名的列表
"""
all_xls = os.listdir(dirname)
all_xls = [dirname+'/'+f for f in all_xls if f.endswith('xlsx')] # ②
return all_xls
def concat_df(all_xls):
"""
功能:多个 dataframe 拼接
"""
for i, xls in enumerate(all_xls):
df = pd.read_excel(xls, header=3, index_col=0) # ③
df = df.iloc[:-1]
if i == 0:
total = df
else:
total = pd.concat([total, df]) # ④
return total
def get_rank(total_df, weight):
"""
功能:按照茶叶种类,分别计算综合得分
"""
indicators = list(total_df.columns) # ⑤
tea_type = set(total_df.index) # ⑥
rank = []
for tea in tea_type:
scores = 0
df = total_df.loc[tea] # ⑦
for indicator in indicators:
score_lst = list(df[indicator]) # ⑧
score_lst.sort()
score_lst = score_lst[1:-1] # ⑨
score = sum(score_lst)
scores += score * weight[indicator]
rank += [(tea, round(scores, 1))]
rank.sort(key=lambda e: e[1], reverse=True) # ⑩
return rank
def print_rank(rank):
"""
功能: 打印评审结果排行榜
"""
print('--- 2022全国名优茶排行榜 ---')
for i, (tea, score) in enumerate(rank):
print(str(i+1).ljust(3), tea, '\t', str(score).rjust(10))
def print_prizes(rank):
"""
功能:打印获奖名单
"""
print('\n*** 2022全国名优茶获奖名单 ***\n')
print('金奖:' + rank[0][0])
print('银奖:' + rank[1][0] + '\t' + rank[2][0])
print('铜奖:', end='')
for tea, score in rank[3:6]:
print(tea, end='\t')
print()
if __name__ == '__main__':
main()
主要函数
函数get_weight():读取文本文件weight.txt,获取评审指标的权重因子,返回权重因子字典。 函数get_xls():获取所有的专家评审电子表格文件名,返回值为包含文件名的列表。 函数concat_df():读入所有专家评审表Excel文件,实现表格数据拼接。其返回值是一个pandas的dataframe数据结构,其中包含了所有专家的评审表数据。 函数get_rank():这是最重要的功能实现。按照茶叶品名分类,分别计算其综合得分。返回值按照综合得分降序排列的排行榜列表,而列表中的每一个元素又是一个元组,包含茶叶品名和综合得分。 函数print_rank():打印评审结果的排行榜。 函数print_prizes():打印获奖名单。 函数main():主程序,整个代码的主入口。
重要语句
语句①解包元组,分离指标名和权重值。 语句②使用列表推导式生成评审表文件名列表。 语句③读入Excel文件内容,生成dataframe数据结构存储评审表数据。 语句④实现多个dataframe数据结构的连接(拼接)操作,简单明了。 语句⑤获取dataframe结构列名,强制转换为列表,其中包含了9个评审指标名称。 语句⑥获取茶叶品名的集合。通过使用集合set,实现除去重复值的功能。 语句⑦按照茶叶品名,获取所有专家的指标评分,它是一个8X9的矩阵数据,8代表8位评审专家,9代表9项指标。 语句⑧获取所有评审专家对1个茶叶品名和1个指标的评分值,这是生成了一个列表。 语句⑨去除一个最高分和一个最低分,代码极其简洁。 语句⑩实现列表排序。由于列表的每个元素是一个元组,排序的键是元组的第2个元素,代表茶叶品名对应的综合得分,通过函数参数key=lambda e: e[1] 指定排序键,参数reverse=True代表按降序排序。
运行效果
环境安装
本程序运行环境需要安装pandas和openpyxl模块。可以在Windows的命令行窗口中执行以下命令完成模块安装。
pip install pandas -i http://pypi.douban.com/simple --trusted-host pypi.douban.com pip install openpyxl -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
目录结构
本程序由代码文件tea_competition.py和数据文件组成。数据文件存放在子目录data下面,weight.txt为指标权重文件,余下的是专家评审的Excel电子表格文件,这些数据均为模拟数据。
D:\cases\斗茶
tea_competition.py
D:\cases\斗茶\data
weight.txt
万沐春.xlsx
吕静成.xlsx
张冲之.xlsx
李博闻.xlsx
李拓.xlsx
柳湘莲.xlsx
周立可.xlsx
邱荷.xlsx
程序执行
程序优化
截止目前,我们所编程的程序已经完美地实现了业务需求,程序代码简洁,实现逻辑清晰,功能非常强的。如果要使用其他编程语言实现相同的功能,其代码编程量是目前编程量的3~5倍。可见Python的编程效率是相当的高效。 在本案例程序的实现中,为了获取正确的评审结果,有一个相当重要的前提是假定专家评审数据是正确的。如果某位专家对某茶样的“嫩度”指标的评分为13分,这是一个无效分,因为该指标的高分为10分。为了正确程序的容错能力,我们需要对专家的评分表进行数据有效性检测。根据评分规则,每一个指标设定了最高分,那么最低分是否就是零,这一点需要明确;当然也可以结合实际情况,是否需要设置每一个指标的起评分,这也是一种解决方案。 好了,我们已经了解了每一个评审指标的最高分和最低分范围,就可以轻松地编写数据有效性检测程序。这个程序有点小挑战,有兴趣的读者可以自行编写。
场景扩展
本案例场景解决方案还适用于:企业绩效考核指标评分与统计、蔬菜水果质量评审、饮料品质评审、白酒/红酒品质评审、咖啡品鉴会等应用场景。