本文概述
Python编程语言是数据科学和预测分析的理想选择, 因为它配备了多个软件包, 可满足你大多数数据分析的需求。对于Python中的机器学习, Scikit-learn(sklearn)是一个不错的选择, 它建立在NumPy, SciPy和Matplotlib(分别为N维数组, 科学计算和数据可视化)的基础上。
在本教程中, 你将看到如何使用sqlite3轻松地从数据库中加载数据, 如何使用pandas和matplotlib探索数据并改善其数据质量, 以及如何使用Scikit-Learn包提取数据。数据中的一些有效见解。
如果你想参加机器学习课程, 请查看srcmini的scikit-learn监督学习课程。
第1部分:预测美国职业棒球大联盟每个赛季的获胜次数
在这个项目中, 你将测试sklearn的几种机器学习模型, 以根据该赛季的球队统计数据和其他变量预测大联盟棒球队在该赛季赢得的比赛次数。如果我是一个赌博者(我当然是一个赌博者), 我可以使用前几个赛季的历史数据来建立模型, 以预测即将到来的那个赛季。考虑到数据的时间序列性质, 你可以生成指标(例如, 过去五年中的每年平均胜利数)以及其他类似因素, 以建立高度准确的模型。这超出了本教程的范围, 但是你将每行视为独立的。我们数据的每一行将由一个特定年份的团队组成。
肖恩·拉曼(Sean Lahman)在其网站上整理了这些数据, 并在此处将其转换为sqlite数据库。
汇入资料
你将通过使用sqlite3包查询sqlite数据库并使用pandas转换为DataFrame来读入数据。你的数据将被过滤, 仅包括当前活跃的现代团队, 以及仅包含该团队玩过150场或更多场比赛的年份。
首先, 下载文件” lahman2016.sqlite”(此处)。然后, 加载Pandas并重命名为pd以便提高效率。你可能还记得pd是Pandas的通用别名。最后, 加载sqlite3并连接到数据库, 如下所示:
# import `pandas` and `sqlite3`
import pandas as pd
import sqlite3
# Connecting to SQLite Database
conn = sqlite3.connect('lahman2016.sqlite')
接下来, 编写查询, 执行查询并获取结果。
# Querying Database for all seasons where a team played 150 or more games and is still active today.
query = '''select * from Teams
inner join TeamsFranchises
on Teams.franchID == TeamsFranchises.franchID
where Teams.G >= 150 and TeamsFranchises.active == 'Y';
'''
# Creating dataframe from query.
Teams = conn.execute(query).fetchall()
提示:如果你想了解有关将SQL与Python结合使用的更多信息, 请考虑阅读srcmini的Python数据库简介。
然后使用pandas将结果转换为DataFrame并使用head()方法打印前5行:
每个列均包含与特定团队和年份有关的数据。下面列出了一些更重要的变量。完整的变量列表可以在这里找到。
- yearID-年
- teamID-团队
- franchID-专营权(链接到TeamsFranchise表)
- G-玩过的游戏
- W-胜利
- LgWin-联赛冠军(是或否)
- WSWin-世界大赛冠军(是或否)
- R-得分得分
- AB-蝙蝠
- H-击球手命中
- HR-击球员本垒打
- BB-击球员
- SO-击球手的三振
- SB-被盗基地
- CS-被盗
- HBP-击球手被击球击中
- SF-牺牲苍蝇
- RA-对手得分
- ER-允许提前进行跑步
- ERA-赢得的跑步平均值
- CG-完整游戏
- 休假
- SV-保存
- IPOuts-间距输出(间距x 3)
- HA-允许点击
- GAME-允许全垒打
- BBA-允许步行
- SOA-投手的三振
- E-错误
- DP-双打
- FP-实地百分比
- 名称-团队的全名
对于那些可能不太熟悉棒球的人, 这里简要介绍一下游戏的运作方式, 其中包括一些变量。
棒球在两支球队之间进行比赛(你可以通过名称或teamID在数据中找到它们), 每支球队有九名球员。这两支球队轮流击球并得分。击球队尝试轮换击球, 以击打由守门员投手投掷的球, 然后逆时针围绕一系列四个垒:第一, 第二, 第三和本垒打来得分。守备队试图通过击打者或垒手以几种方式中的任意一种来阻止奔跑, 并且当玩家绕着垒前进并返回本垒板时, 会为奔跑(R)得分。击球团队中安全到达基地的玩家会在队友轮转击球时尝试前进到后续基地, 例如击中(H), 被盗基地(SB)或通过其他方式。
每当守门员团队记录三场比赛时, 他们便在击球和守门员之间切换。从访问队开始, 两支球队的一击都构成一局。一场比赛由9局组成, 比赛结束时奔跑次数更多的团队获胜。棒球没有比赛时间, 尽管大多数比赛都在第九局结束, 但是如果一场比赛在九局之后并列, 它将进入额外局, 并且将无限期延续, 直到一支球队在额外局结束时领先。
有关棒球比赛的详细说明, 请查看美国职业棒球大联盟的正式规则。
清理和准备数据
正如你在上面看到的, DataFrame没有列标题。你可以通过将标题列表传递给pandas的columns属性来添加标题。
len()函数可让你知道要处理多少行:2, 287不是要处理的数据点, 因此希望没有太多的空值。
在评估数据质量之前, 我们首先要消除不必要的列或从目标列派生出来的列(获胜者)。在这里, 你正在使用的数据知识变得非常有价值。如果你对正在使用的数据一无所知, 则对编码或统计信息的了解程度无关紧要。作为终身的棒球迷, 无疑对我帮助了这个项目。
如你在上面所读, 空值会影响数据质量, 这又可能导致机器学习算法出现问题。
因此, 你接下来将其删除。有几种消除空值的方法, 但是最好先显示每列的空值计数, 以便你决定如何最好地处理它们。
这是你要权衡的地方:你需要干净的数据, 但也没有大量的数据可以保留。其中两列的空值相对较少。 SO(淘汰)列中有110个空值, 而DP(Double Play)列中有22个空值。其中两列的数量相对较大。 CS(捕捉到的偷窃)列中有419个空值, HBP(命中率)列中有1777个空值。
如果你删除其中的列具有少量空值的行, 那么你丢失的数据将超过百分之五。由于你要预测获胜情况, 因此得分和允许的跑步次数与目标高度相关。你希望这些列中的数据非常准确。
罢工(SO)和双打(DP)并不那么重要。
我认为你最好保留行, 并使用fillna()方法将空值填充为每个列的中值。抢断(CS)和投篮命中(HBP)也不是很重要的变量。这些列中有很多空值, 因此最好一并消除。
探索和可视化数据
现在, 你已经清理了数据, 可以进行一些探索。通过一些简单的可视化效果, 你可以更好地了解数据集。 matplotlib是出色的数据可视化库。
导入matplotlib.pyplot并将其重命名为plt以提高效率。如果你使用的是Jupyter笔记本, 则需要使用%matplotlib内置魔术。
首先, 绘制目标列的直方图, 以便查看获胜的分布。
# import the pyplot module from matplotlib
import matplotlib.pyplot as plt
# matplotlib plots inline
%matplotlib inline
# Plotting distribution of wins
plt.hist(df['W'])
plt.xlabel('Wins')
plt.title('Distribution of Wins')
plt.show()
请注意, 如果你不使用Jupyter笔记本, 则必须使用plt.show()来显示绘图。
打印出每年的平均获胜数(W)。你可以为此使用mean()方法。
在浏览数据时为目标列创建bin很有用, 但是你需要确保在训练模型时不包括从目标列生成的任何功能。在训练集中包括从目标列生成的一列标签, 就像为模型提供测试的答案一样。
要创建胜利标签, 你将创建一个名为assign_win_bins的函数, 该函数将接收一个整数值(胜利), 并根据输入值返回1-5的整数。
接下来, 你将通过使用wins列上的apply()方法并传入assign_win_bins()函数来创建新列win_bins。
现在, 让我们制作一个散点图, x轴为年份, y轴为胜利, 并用颜色突出显示win_bins列。
# Plotting scatter graph of Year vs. Wins
plt.scatter(df['yearID'], df['W'], c=df['win_bins'])
plt.title('Wins Scatter Plot')
plt.xlabel('Year')
plt.ylabel('Wins')
plt.show()
正如你在上面的散点图中所看到的, 从1900年前开始只有很少的几个季节, 那时的比赛就大不相同了。因此, 从数据集中消除那些行是有意义的。
处理连续数据并创建线性模型时, 整数值(例如年份)可能会引起问题。数字1950与模型推断的其余数据不太可能具有相同的关系。
你可以通过创建基于yearID值标记数据的新变量来避免这些问题。
任何从事棒球比赛的人都知道, 随着美国职棒大联盟(MLB)的发展, 出现了不同的时代, 每场比赛的奔跑数量显着增加或减少。 1900年代初期的死球时代是低得分时代的一个例子, 而21世纪初的类固醇时代是高得分时代的一个例子。
让我们在下面制作一张图表, 指出每年的得分。
首先, 创建字典run_per_year和games_per_year。使用iterrows()方法循环遍历数据框。使用年份作为关键字填充runs_per_year词典, 并在该年中对多少次运行进行评分。使用年份作为键填充games_per_year词典, 并以该年的年份填充多少个游戏作为值。
接下来, 创建一个名为mlb_runs_per_game的字典。使用items()方法遍历games_per_year字典。使用年份作为关键字填充mlb_runs_per_game词典, 并以联盟范围内每场比赛得分的运行次数作为填充值。
最后, 通过在x轴上放置年份, 并在y轴上显示每个游戏的运行, 从mlb_runs_per_game字典中创建情节。
# Create lists from mlb_runs_per_game dictionary
lists = sorted(mlb_runs_per_game.items())
x, y = zip(*lists)
# Create line plot of Year vs. MLB runs per Game
plt.plot(x, y)
plt.title('MLB Yearly Runs per Game')
plt.xlabel('Year')
plt.ylabel('MLB Runs per Game')
plt.show()
增加新功能
现在, 你对得分趋势有了更好的了解, 你可以创建新的变量, 这些变量指示基于yearID的每一行数据所属的特定时代。创建win_bins列时, 将遵循与上述相同的过程。
但是, 这一次, 你将创建虚拟列。每个时代的新专栏。你可以为此使用get_dummies()方法。
由于你已经确定了每年每场MLB的运行次数, 因此请将该数据添加到数据集中。
现在, 你将通过为每个十年创建虚拟列将年份转换为几十年。然后, 你可以删除不再需要的列。
棒球比赛的底线是你得分多少次以及允许多少次奔跑。通过创建作为其他数据列的比率的列, 可以显着提高模型的准确性。每个游戏的运行次数和每个游戏允许的运行次数将是添加到我们的数据集中的强大功能。
通过将R列除以G列来创建R_per_game列, 在创建新列时, Pandas使其非常简单。
现在, 通过制作一对散点图, 看看两个新变量中的每一个与目标获胜栏的关系。在一个图形的x轴上绘制每个游戏的运行次数, 在另一个图形的x轴上绘制每个游戏的允许运行次数。在每个y轴上绘制W列。
# Create scatter plots for runs per game vs. wins and runs allowed per game vs. wins
fig = plt.figure(figsize=(12, 6))
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2)
ax1.scatter(df['R_per_game'], df['W'], c='blue')
ax1.set_title('Runs per Game vs. Wins')
ax1.set_ylabel('Wins')
ax1.set_xlabel('Runs per Game')
ax2.scatter(df['RA_per_game'], df['W'], c='red')
ax2.set_title('Runs Allowed per Game vs. Wins')
ax2.set_xlabel('Runs Allowed per Game')
plt.show()
在进入任何机器学习模型之前, 了解每个变量与目标变量之间的关系是很有用的。 Pandas使用corr()方法使此操作变得容易。
你可以添加到数据集中的另一个功能是从sklearn提供的K-means聚类算法得出的标签。 K均值是一种简单的聚类算法, 可根据你指定的k个质心数对数据进行分区。将每个数据点分配给一个群集, 基于该群集, 质心到数据点的欧几里得距离最小。
你可以在此处了解有关K-means聚类的更多信息。
首先, 创建一个不包含目标变量的DataFrame:
在使用模型之前, 你必须确定的K均值聚类的一方面是想要多少个聚类。使用sklearn的silhouette_score()函数, 你可以更好地了解理想的群集数量。此函数返回所有样本的平均轮廓系数。你需要更高的轮廓分数, 并且随着添加更多群集, 分数会降低。
现在你可以初始化模型。将群集数设置为6, 将随机状态设置为1。通过使用fit_transform()方法确定每个数据点的欧几里得距离, 然后使用散点图可视化群集。
# Create K-means model and determine euclidian distances for each data point
kmeans_model = KMeans(n_clusters=6, random_state=1)
distances = kmeans_model.fit_transform(data_attributes)
# Create scatter plot using labels from K-means model as color
labels = kmeans_model.labels_
plt.scatter(distances[:, 0], distances[:, 1], c=labels)
plt.title('Kmeans Clusters')
plt.show()
现在, 将群集中的标签作为新列添加到数据集中。还将字符串” labels”添加到属性列表, 以备后用。
在构建模型之前, 你需要将数据拆分为训练集和测试集。这样做是因为, 如果你决定在测试模型的相同数据上训练模型, 则模型很容易过拟合数据:模型将更多地记住数据而不是从中学习, 从而导致模型过于复杂。你的数据。这也解释了为什么当你尝试使用新数据进行预测时, 过拟合模型的性能会非常差。
但是, 现在不用担心, 有很多方法可以交叉验证模型。
这次, 你只需对火车数据集随机抽取75%的数据, 而将其余25%用于测试数据集。用你将在模型中使用的所有列创建一个列表numeric_cols。接下来, 从df DataFrame中创建一个新的DataFrame数据, 并带有numeric_cols列表中的列。然后, 还通过采样DataFrame数据来创建训练和测试数据集。
选择误差指标和模型
平均绝对误差(MAE)是用于确定模型准确性的指标。它衡量了预测与最终结果的接近程度。具体来说, 对于这些数据, 这意味着该误差度量将为你提供预测未达到其标记的平均绝对值。
这意味着, 如果平均而言, 如果你的预测未达到目标数5次, 则你的错误指标为5。
你将训练的第一个模型将是线性回归模型。你可以分别从sklearn.linear_model和sklearn.metrics导入LinearRegression和mean_absolute_error, 然后创建一个模型lr。接下来, 你将拟合模型, 进行预测并确定模型的平均绝对误差。
如果从上面回顾, 平均获胜次数约为79次。平均而言, 该模型仅以2.687次获胜。
现在尝试一个Ridge回归模型。从sklearn.linear_model导入RidgeCV并创建模型rrm。 RidgeCV模型允许你设置alpha参数, alpha参数是控制收缩量的复杂性参数(在此处了解更多)。模型将使用交叉验证来确定你提供的哪个alpha参数是理想的。
再次, 拟合模型, 进行预测并确定平均绝对误差。
该模型的性能稍好一些, 平均而言, 以2.673获胜。
体育分析和Scikit学习
至此, 本系列教程的第一部分结束了, 你已经了解了如何使用scikit-Learn分析体育数据。你从SQLite数据库导入了数据, 对其进行了清理, 可视化地探索了各个方面, 并设计了一些新功能。你学习了如何创建K-means聚类模型, 几个不同的线性回归模型, 以及如何使用平均绝对误差度量标准测试你的预测。
在第二部分中, 你将看到如何使用分类模型来预测哪些球员进入了MLB名人堂。
评论前必须登录!
注册