足球运动员分析
背景信息
当前,足球运动是最受欢迎的运动之一(也可以说没有之一)。
任务说明
我们的任务,就是在众多的足球运动员中,发现统计一些关于足球运动员的共性,或某些潜在的规律。
数据集描述
数据集包含的是2017年所有活跃的足球运动员。
- Name 姓名
- Nationality 国籍
- National_Position 国家队位置
- National_Kit 国家队号码
- Club 所在俱乐部
- Club_Position 所在俱乐部位置
- Club_Kit 俱乐部号码
- Club_Joining 加入俱乐部时间
- Contract_Expiry 合同到期时间
- Rating 评分
- Height 身高
- Weight 体重
- Preffered_Foot 擅长左(右)脚
- Birth_Date 出生日期
- Age 年龄
- Preffered_Position 擅长位置
- Work_Rate 工作效率
- Weak_foot 非惯用脚使用频率
- Skill_Moves 技术等级
- Ball_Control 控球技术
- Dribbling 盘球(带球)能力
- Marking 盯人能力
- Sliding_Tackle 铲球
- Standing_Tackle 逼抢能力
- Aggression 攻击能力
- Reactions 反击
- Attacking_Position 攻击性跑位
- Interceptions 抢断
- Vision 视野
- Composure 镇静
- Crossing 下底传中
- Short_Pass 短传
- Long_Pass 长传
- Acceleration 加速度
- Speed 速度
- Stamina 体力
- Strength 强壮
- Balance 平衡
- Agility 敏捷度
- Jumping 跳跃
- Heading 投球
- Shot_Power 射门力量
- Finishing 射门
- Long_Shots 远射
- Curve 弧线
- Freekick_Accuracy 任意球精准度
- Penalties 点球
- Volleys 凌空能力
- GK_Positioning 门将位置感
- GK_Diving 扑救能力
- GK_Kicking 门将踢球能力
- GK_Handling 扑球脱手几率
- GK_Reflexes 门将反应度
程序实现
导入相关的库
导入需要的库,同时,进行一些初始化的设置。
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
# 支持中文显示。
mpl.rcParams["font.family"] = "SimHei"
mpl.rcParams["axes.unicode_minus"] = False
加载相关的数据集
- 加载相关的数据集(注意原数据集中是否存在标题),并查看数据的大致情况。
- 可以使用head / tail,也可以使用sample。
- 列没有显式完整,我们需要进行设置。(pd.set_option)
data = pd.read_csv("FullData.csv")
# data.head(10)
# data.sample(3)
pd.set_option("display.max_columns", 55)
data.head()
数据探索与清洗
缺失值处理
- 通过info查看缺失值信息(以及每列的类型信息)。
- 可以通过isnull, any, dropna,fillna等方法结合使用,对缺失值进行处理。
# data.info()
# 查看存在空值的记录。
# data[data["Club_Position"].isnull()]
# data.isnull().sum(axis=0)
# data["Club_Position"].notnull()
# 过滤空值记录。
# data = data[data["Club_Position"].notnull()]
# data.info()
异常值处理
- 通过describe查看数值信息。
- 可配合箱线图辅助。
# data.describe()
# data["Rating"].plot(kind="box")
重复值处理
- 使用duplicate检查重复值。可配合keep参数进行调整。
- 使用drop_duplicate删除重复值。
# np.sum(data.duplicated())
# data[data.duplicated()]
# data.drop_duplicates()
将身高与体重处理成数值类型,便于分析。
# 对数据列进行转换。可以使用apply或map。
# data["Height"] =
# data["Height"] = data["Height"].apply(lambda item: item.replace("cm", "")).astype(np.int32)
# data["Weight"] = data["Weight"].apply(lambda item: item.replace("kg", "")).astype(np.int32)
# 也可以使用map实现同样的转换。
#data["Height"].map(lambda item: item.replace("cm", "")).astype(np.int32)
# 使用字符串的矢量化运算完成转换。
data["Height"] = data["Height"].str.replace("cm", "").astype(np.int32)
data["Weight"] = data["Weight"].str.replace("kg", "").astype(np.int32)
运动员的身高,体重,评分信息分布。
# data["Height"].plot(kind="kde")
# data["Weight"].plot(kind="kde")
# data["Rating"].plot(kind="kde")
data[["Height", "Weight", "Rating"]].plot(kind="kde")
左脚与右脚选手在数量上是否存在偏差?
# 第一种方式
# g = data.groupby("Preffered_Foot")
# g["Preffered_Foot"].count()
# 第二种方式
# g = data.groupby("Preffered_Foot")
# g.size()
# 第三种方式
data["Preffered_Foot"].value_counts().plot(kind="bar")
从球员平均评分上考虑,拥有top10评分能力的俱乐部 / 国家。【超过20人】
# 俱乐部
# g = data.groupby("Club")
# g["Rating"].mean().sort_values(ascending=False)
# t = g["Rating"].agg(["mean", "count"])
# t = t[t["count"] > 20]
# t = t.sort_values(by="mean", ascending=False).head(10)
# t["mean"].plot(kind="bar")
# 国家队
g = data.groupby("Nationality")
# g["Rating"].mean().sort_values(ascending=False)
t = g["Rating"].agg(["mean", "count"])
t = t[t["count"] > 20]
t = t.sort_values(by="mean", ascending=False).head(10)
t
# t["mean"].plot(kind="bar")
哪个俱乐部拥有更多忠心的球员(5年及以上)?
t = data["Club_Joining"].apply(lambda item: int(item.split("/")[-1]))
# 计算球员的效力时间。
year = 2017 - t
# 对数据集进行过滤,只保留效力时间达到或超过5年的球员。
t = data[(year >= 5) & (data["Club"] != "Free Agents")]
# t
t["Club"].value_counts()
足球运动员是否是出生年月相关?
- 全体运动员
- 知名运动员(80分及以上)
# 全体运行员
# expand 默认为False。如果设置为True,就是展开成为一个DataFrame。
t = data["Birth_Date"].str.split("/", expand=True)
# t[2].value_counts().plot(kind="bar")
# t[0].value_counts().plot(kind="bar")
# t[1].value_counts().plot(kind="bar")
# 80分以上的运动员
t = data[data["Rating"] >= 80]
t = t["Birth_Date"].str.split("/", expand=True)
# t[2].value_counts().plot(kind="bar")
# t[0].value_counts().plot(kind="bar")
# t[1].value_counts().plot(kind="bar")
Out[74]:
<matplotlib.axes._subplots.AxesSubplot at 0x108f5550>
足球运动员号码是否与位置相关?
# 去掉替补与预备队的球员
t = data[(data["Club_Position"] != "Res") & (data["Club_Position"] != "Sub")]
# g = data.groupby(["Club_Position", "Club_Kit"])
g = t.groupby(["Club_Kit", "Club_Position"])
g.size()
Out[78]:
Club_Kit Club_Position
1.0 GK 333
2.0 CAM 1
CB 3
GK 1
LB 26
LCB 36
LCM 3
LDM 3
LM 5
LW 1
LWB 1
RB 148
RCB 41
RCM 4
RDM 3
RM 3
RWB 11
3.0 CAM 1
CB 5
CDM 1
CM 2
LB 103
LCB 58
LCM 2
LDM 6
LM 3
LW 1
LWB 12
RB 19
RCB 75
...
93.0 LM 2
LS 1
LW 1
RCM 1
ST 1
94.0 CAM 2
RB 1
RCB 1
RCM 1
RM 1
RS 1
RW 1
95.0 LB 1
LM 2
RB 1
RM 1
96.0 LB 1
97.0 CAM 1
CM 1
ST 1
98.0 LB 1
LCM 1
RDM 1
99.0 CAM 1
GK 4
LM 1
LS 4
LW 1
RS 2
ST 8
Length: 954, dtype: int64
身高与体重是否具有相关性?
# data.plot(kind="scatter", x="Height", y="Weight")
# data.plot(kind="scatter", x="Height", y="Rating")
Out[80]:
<matplotlib.axes._subplots.AxesSubplot at 0x11159b70>
哪些指标对评分的影响最大?
data.corr()
Out[82]:
National_Kit | Club_Kit | Contract_Expiry | Rating | Height | Weight | Age | Weak_foot | Skill_Moves | Ball_Control | Dribbling | Marking | Sliding_Tackle | Standing_Tackle | Aggression | Reactions | Attacking_Position | Interceptions | Vision | Composure | Crossing | Short_Pass | Long_Pass | Acceleration | Speed | Stamina | Strength | Balance | Agility | Jumping | Heading | Shot_Power | Finishing | Long_Shots | Curve | Freekick_Accuracy | Penalties | Volleys | GK_Positioning | GK_Diving | GK_Kicking | GK_Handling | GK_Reflexes | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
National_Kit | 1.000000 | 0.055408 | -0.027211 | -0.084289 | -0.101229 | -0.093795 | -0.103583 | 0.027268 | 0.105903 | 0.046644 | 0.093836 | -0.162083 | -0.152251 | -0.148329 | -0.073288 | -0.05 |