足球运动员分析
背景信息
当前,足球运动是最受欢迎的运动之一(也可以说没有之一)。
任务说明
我们的任务,就是在众多的足球运动员中,发现统计一些关于足球运动员的共性,或某些潜在的规律。
数据集描述
数据集包含的是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")
display(data.head(5))
# display(data.sample())
# 当列数较多时,默认情况下列不会显示完整。我们可以通过set_option方法来进行设置。
pd.set_option("display.max_columns", 100) # 100>53,所以全部显示
display(data.head(5))
数据探索与清洗
缺失值处理
- 通过inof查看缺失值信息(以及每列的类型信息)。
- 可以通过isnull, any, dropna,fillna等方法结合使用,对缺失值进行处理。
# 查看缺失值信息,发现国家队的位置和国家队的号码缺失比较多
# 究其原因:运动员很多,但是能进国家队的就比较少了
display(data.info())
# 俱乐部不缺失,但是俱乐部中的位置和编号缺失,就不正常了
display(data[data["Club_Position"].isnull()])
# 发现俱乐部位置和编号的缺失都在这里,所以直接将这一条记录过滤掉
data=data[data["Club_Position"].notnull()] # 过滤掉后的数据
# 查看俱乐部位置是否还有缺失值
print(data["Club_Position"].isnull().any())
异常值处理
- 通过describe查看数值信息。
- 可配合箱线图辅助。
display(data.describe())
data[["Age", "Rating"]].plot(kind="box")
重复值处理
- 使用duplicate检查重复值。可配合keep参数进行调整。
- 使用drop_duplicate删除重复值。
# 没有重复值。
display(data.duplicated().any()) # False没有重复值
# 如果存在重复值,则删除重复值。
data.drop_duplicates(inplace=True)
将身高与体重处理成数值类型,便于分析。
方法1:
# 观察一下身高和体重的信息
display(data.sample())
display(data.info())
# 注意:身高与体重是字符串类型,替换掉cm或kg之后,依然还是字符串类型,不会自动变成数值类型。
# 将cm和kg去掉,并转换成int型
t = data.copy()
t["Height"] = t["Height"].apply(lambda x: x.replace("cm", "")).astype(np.int32)
t["Weight"] = t["Weight"].apply(lambda x: x.replace("kg", "")).astype(np.int32)
display(t.head())
display(t.info())
方法2:
# 也可以使用字符串矢量化运算来解决。
data["Height"] = data["Height"].str.replace("cm", "").astype(np.int32)
data["Weight"] = data["Weight"].str.replace("kg", "").astype(np.int32)
data.info()
运动员的身高,体重,评分信息分布。
# 直方图是离散的统计,核密度图是连续的统计。身高,体重,与评分服从正态分布。
display(data[["Height", "Weight", "Rating"]].plot(kind="hist"))
display(data[["Height", "Weight", "Rating"]].plot(kind="kde"))
左脚与右脚选手在数量上是否存在偏差?
# 解决方法不止一种。
# 1 通过分组,然后聚合统计。
g = data.groupby("Preffered_Foot")
display(g["Preffered_Foot"].count())
display(g["Preffered_Foot"].count().plot(kind='bar'))
# 2 通过分组对象的size方法。
display(g["Preffered_Foot"].size())
display(g.size().plot(kind="bar"))
# 3 使用value_counts
# value_counts 默认根据值数量,按降序来进行排列。
# 我们可以通过ascending参数来控制升序还是降序。
data["Preffered_Foot"].value_counts(ascending=True).plot(kind="bar")
从球员平均评分上考虑,拥有top10评分能力的俱乐部 / 国家。【超过20人】
# 对俱乐部的分数的平均分统计
g = data.groupby("Club")
display(g["Rating"].mean().sort_values(ascending=False))
v = g["Rating"].agg(["mean", "count"])
v = v[v["count"] > 20]
display(v)
v.sort_values("mean", ascending=False).head(10).plot(kind="bar")
# 对国家队统计
g = data.groupby("Nationality")
display(g["Rating"].mean().sort_values(ascending=False))
v = g["Rating"].agg(["mean", "count"])
v = v[v["count"] > 20]
display(v.head(10))
v.sort_values("mean", ascending=False).head(10).plot(kind="bar")
哪个俱乐部拥有更多忠心的球员(5年及以上)?
x='07/01/2009'
a=x.split("/")
print(a)
print(a[-1])
# 获取球员加入俱乐部的年份。
display(data["Club_Joining"].head(2))
year = data["Club_Joining"].map(lambda x: x.split("/")[-1]).astype(np.int)
display(year.head(2))
# 用2017(获取数据的时间)-加入的时间>=5(同时,去掉自由身的球员)
t = data[(2017 - year >= 5) & (data["Club"] != "Free Agents")]
display(t.head(2))
# t.groupby("Club").size()
display(t["Club"].value_counts().head(5)) # 俱乐部超过5年球员的数量
t["Club"].value_counts().head(10).plot(kind="bar")
足球运动员是否是出生年月相关?
- 全体运动员
- 知名运动员(80分及以上)
display(data["Birth_Date"].head(2))
t = data["Birth_Date"].str.split("/", expand=True) # 矢量化张开成dateframe格式
display(t.head(2))
# display(t[2].value_counts().plot(kind="bar")) #年
display(t[0].value_counts().plot(kind="bar"))
# display(t[1].value_counts().plot(kind="bar"))
足球运动员号码是否与位置相关?
# 去掉替补球员与预备队球员。
display(data["Club_Position"].head(2))
display(data["Club_Kit"].head(2))
t = data[(data["Club_Position"] != "Sub") & (data["Club_Position"] != "Res")]
display(t.groupby(["Club_Kit", "Club_Position"]).size())
t.groupby(["Club_Kit", "Club_Position"]).size().sort_values(ascending=False).head(10).plot(kind="bar")
身高与体重是否具有相关性?
# 身高与体重息息相关。
display(data.plot(kind="scatter", x="Height", y="Weight")) # 相关
# 身高与评分关联性不大。
display(data.plot(kind="scatter", x="Height", y="Rating")) # 不相关
哪些指标对评分的影响最大?
data.corr() # 越大越相关
假设我们不清楚后2列的具体含义是什么,分析该标题可能的含义。
display(data.head(2))
data.groupby("Club_Position")["GK_Handling"].mean().sort_values(ascending=False).plot(kind="bar")
年龄与评分具有怎样的关系?
display(data.plot(kind="scatter", x="Age", y="Rating")) # 不明显
# 将连续的值切割成离散的形式。
# 注意:cut切割的桶,区间范围与直方图有些不同。cut的区间是前开后闭的形式。(]
# cut方法返回的值默认为我们桶的区间范围值,我们也可以通过labels属性来显式指定具体的标签。
display(pd.cut(data["Age"], bins=5))
display(pd.cut(data["Age"], bins=5, labels=["1区间", "2区间", "3区间", "4区间", "5区间"]).head(5))
t = data.copy()
# cut方法,如果bins是整数类型,则区间(桶)进行等分设置,如果区间不等分,则我们可以显式设置区间的取值范围。
# # 对bins参数传递一个数组,数组来指定区间的具体值。
t["Age2"] = pd.cut(t["Age"], bins=[1, 20, 30, 40, 100], labels=["初出茅庐", "快速成长", "当打之年", "衰老之年"])
display(t.head(5))
t.groupby("Age2")["Rating"].mean().plot(marker="o", xticks=[0, 1, 2, 3])