编译平台:PyCharm
使用语言:Python
版本:2020.1
一、前言
实验:现给出球员的赛场信息表 ,请分析该表,排名球员实力。
表资源下载链接:data.xlsx
二、代码
2.1 导入库
import re
import matplotlib.pyplot as plt
import pandas
from collections import Counter
有关上述库的理解如下:
Python库 | 描述 |
---|---|
re 库 | 提供字符串分割方法 |
matplotlib 库 | 提供图像可视化 |
pandas 库 | 提供获取Excel表等文件类型的数据手段 |
collections 库 | 提供统计数方法 |
2.2 准备Excel路径
# data.xlsx路径
FilePath = "C:\\Users\\Administrator\\Desktop\\pandas入门\\data.xlsx"
OutPath = "C:\\Users\\Administrator\\Desktop\\pandas入门\\NewData.xlsx"
如上图所示,为笔者的data.xlsx
本地路径。输出路径为后续将筛除的新数据进行导出的路径。特别注意:你需要将你的本地路径覆盖上图中的路径。
2.3 提取Excel数据
为方便理解,我们使用pandas
直接引用,不做缩写命名。
List = pandas.read_excel(FilePath)
pandas
库提供read_excel()
方法来读取Excel表中的数据。
关于更多了解该函数方法,请参考CSDN作者:leenuxcore — Pandas read_excel( )参数详解。
输出的部分结果如下图所示:
2.4 修正格式(可忽略)
# 解决数据输出时列名不对齐的问题
pd.set_option('display.unicode.east_asian_width', True)
2.5 提取关键数据
TeamRole = pandas.DataFrame(data=List, index=None, columns=['球员', '进球(点球)', '射门', '射正', '球队'])
出于数据类型多,我们只筛选出相对有用的列。pandas
库提供DataFrame()
方法允许我们从获取的数据集中,拿取指定列、行的数据集。
关于该函数方法,请参考CSDN作者:daydayup_668819 — python下的Pandas中DataFrame基本操作(一),基本函数整理。
提取数据部分图如下图所示:
2.6 数据清洗阶段
2.6.1 认识射门与射正
描述 | |
---|---|
射门 | 指用踢球、头顶球、铲球等技术将球射向对方球门。是进攻的最终目的,也是比赛胜负的关键。 |
射正(进攻) | 从进球的角度,在最后一次出球(射门)时,完成破门或对球门造成威胁。 |
射正(防守) | 从防守的角度,最后一次触球,踢向球门的中路,导致门将将球没收或将球扑出,浪费了一次进攻机会 |
笔者个人理解 射门 —— 向门的方向射球
射球 —— 射向门内区域(不包含门框)
2.6.2 正则表达式提取关键数据
由于每名球员中,存在 进球数(点球) 这一选项,我们需要使用re
库,即正则表达式对数据进行分割。
首先,我们通过len()
获取列长度。或许也有其他更好的方法直接获取列长,命名为RoleCount
(取名为"球员数量")。
RoleCount = len(TeamRole)
其次,受限于我们从Excel中获取的数据格式有int
与string
两个类型混合,我们决定使用re
库提供的split()
方法对数据进行提取。于是,我们准备了数组GoalMain
、GoalPoint
进行提取的数据存储。如下所示:
GoalMain = [] # 存储总进球数
GoalPoint = [] # 存储点射进球数
但是,re
库提供的split()
方法在非包含指定分割字符的前提下,返回报错的问题。原因是存在int
类型的数据。
于是,为解决此问题我们引入了isdigit()
方法来辨识字符串是否为数值。在此之前,我们需要将所有数据类型强转为string
类型,来满足isdigit()
的前提条件。
for index in range(0, RoleCount):
value = TeamRole['进球(点球)'][index] # 索引进球数据
value = str(value) # 强转str
# 如果数据为纯数字,直接赋值,否则进行数据提取
# 判断字符串内部是否为纯数字
if value.isdigit():
data = value # 直接赋值
GoalMain.append(data) # 添加进球总数
GoalPoint.append(0) # 添加点射进球数0
else:
data = re.split('[()]', value) # 提取数据
# print(len(data)) # 获取数据大小
# 输出数据 ['数据1', '数据2', 'null']
GoalMain.append(data[0]) # 添加进球总数
GoalPoint.append(data[1]) # 添加点射进球数
特别注意:
数据内容 17(2),在使用split()
分割后,其分割后的数据为Array
类型,即分割数据为['17', '2', '']
,故获取的重要数据分为别data[0]
、data[1]
。
数据内容 16,因为没有点球数,故计数为0。
处理后的数据集如下所示:
2.6.3 添加新列
如下所示,我们将获取到的数据分别命名为"总进球"
、"点进球"
,并将数组GoalMain
、GoalPoint
分别赋值。
TeamRole['总进球'] = GoalMain
TeamRole['点进球'] = GoalPoint
2.6.4 删除旧列
删除'进球(点球)'
列,因为我们从中提取到想要的数据集,后续将不再需要。
del TeamRole['进球(点球)']
2.6.5 提取的数据表
2.7 统计数据
由 2.6.1 认识射门与射正 提供的了解,新的数据计算如下所示:
算式 | 描述 | |
---|---|---|
框内命中率 | = 射正 / 射门 | 射进球框内区域方向的概率 |
进球率 | = 进球 / 射正 | 在射进球框内区域方向上的进球得分概率 |
于是,准备两个数组EntryRate
与GoalRate
作为存储数据组:
EntryRate = [] # 进门率
GoalRate = [] # 进球率
接着,遍历各列,获取数据,计算。将计算的结果添加至数据组中。其中round()
方法帮助我们控制数据的小数点后位数,以更直观的美化数据观感,如下图示:
for index in range(0, RoleCount):
value_SP = float(TeamRole['射正'][index])
value_SM = float(TeamRole['射门'][index])
value_go = float(TeamRole['总进球'][index])
# 射正/射门 = 框内命中率
valueA = value_SP / value_SM
# 进球/射正 = 进球率
valueB = value_go / value_SP
# 添加
EntryRate.append(round(valueA, 3))
GoalRate.append(round(valueB, 3))
特别注意:出于获取的内容为字符串string
类型,需要float()
强转数据类型,后续计算准备。
重复 2.6.3 添加新列 操作,将新数据组添加至TeamRole
中,如下图所示:
TeamRole['进门率'] = EntryRate
TeamRole['进球率'] = GoalRate
2.8 制图阶段
2.8.0 最终效果图展示
2.8.1 准备数据
如 2.8.0 最终效果图展示所示,x轴为球员,y+为进门率,y-为进球率。并附有两条平均水平线作参考。
分别获取TeamRole['球员']
、TeamRole['进球率']
、TeamRole['进门率']
。
x1 = TeamRole['球员']
y2 = TeamRole['进球率']
y3 = TeamRole['进门率']
由于笔者在Python中尚未找到average()
方法,可能有但就是完美的错过了。选择使用sum()
计算各数据列总和,来求均值。得到的均值为 各率参考线。
ave_Entry = sum(y2)/RoleCount
ave_Goal = sum(y3)/RoleCount
2.8.2 绘制关系图
plt.figure(figsize=(13, 8))
plt.bar(x1, y2, width=0.4, label='进球率')
plt.xticks(rotation=45)
plt.bar(x1, -y3, width=0.4, label='进门率')
2.8.3 绘制参考线
plt.axhline(y=ave_Entry, ls=":", c="red", label='平均进门率') # 添加水平直线
plt.axhline(y=-ave_Goal, ls=":", c="blue", label='平均进球率') # 添加水平直线
2.8.4 预防图像数据乱码
出于编码格式的缘由,一部分中文字体无法正常显示(即 乱码)。需要使用以下两行代码对输出文字格式进行修正。
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
2.8.5 其他数据内容补充
为X轴、Y轴添加标签说明,如下所示:
plt.xlabel('球员')
plt.ylabel('百分比 %')
将标签内容以微型图方式呈现于数据图右上方,如下所示:
plt.legend()
命名数据的标题内容,并呈现于数据图正上方。
plt.title('球员进球率(条形图)')
2.8.6 显示图
plt.show()
2.9 输出Excel表
result
为我们在 2.7 统计数据 完成后获取的最终数据。OutPath
为我们在 2.2 准备Excel路径 时已经完成。
result.to_excel(OutPath, sheet_name='NewData')
三、后记
如有错误,请评论指正,或联系作者进行更正。