无论您现在消费何种媒介的内容(播客、文章、推文等),您都可能会遇到一些关于数据的引用。无论是为了支持某个论点,还是从宏观角度看待数据如何无处不在,数据及其分析都处于高度需求之中。
作为一名程序员,我发现数据科学更像是巫术而不是精确科学。我渴望获得原始数据并从中收集有用的、具体的东西的能力。多么有用的才能!
这让我开始思考数据科学家和程序员之间的区别。数据科学家不就是会编码的统计学家吗?环顾四周,您会看到许多旨在帮助开发人员成为数据科学家的工具。AWS 有一个完整的 机器学习课程,专门针对将开发人员转变为专家。Visual Studio 具有内置的 Python 项目,只需单击一个按钮,即可为分类问题创建一个完整的模板。并且有大量的程序员正在编写旨在使任何人都能更容易地掌握数据科学的工具。
我想我会倾向于招募程序员加入数据(或黑暗)阵营的明确信息,并尝试一个有趣的项目:训练一个机器学习模型来预测使用国家橄榄球联盟 (NFL) 数据集的比赛。
设置环境
在我深入研究数据之前,我需要设置我的 虚拟环境。这很重要,因为如果没有环境,我就无处工作。幸运的是,Opensource.com 有 一些很棒的资源 用于安装和配置设置。
您在这里看到的任何代码,我都可以通过现有文档查找。如果说程序员对什么最熟悉,那就是浏览陌生的(有时非常稀疏的)文档。
获取数据
与任何现代问题一样,第一步是确保您拥有高质量的数据。幸运的是,我偶然发现了一组来自 2017 年的 NFL 跟踪数据,这些数据用于 NFL Big Data Bowl。即使 NFL 也在尽最大努力吸引数据领域的杰出人才。
关于模式我需要知道的一切都在 README 中。此练习将训练一个机器学习模型,以使用 plays.csv 数据文件 预测跑动(持球者保持足球并向下场跑动)和传球(球传给接球球员)比赛。在此练习中我不会使用球员跟踪数据,但以后探索它可能会很有趣。
首先,我需要通过将数据导入到数据帧来访问我的数据。Pandas 库是一个开源 Python 库,它提供了用于轻松分析数据结构的算法。示例 NFL 数据中的结构恰好是一个二维数组(或更简单的说,一个表格),数据科学家通常将其称为数据帧。处理数据帧的 Pandas 函数是 pandas.DataFrame。我还将导入其他几个我稍后将使用的库。
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import xgboost as xgb
from sklearn import metrics
df = pd.read_csv('data/plays.csv')
print(len(df))
print(df.head())
格式化数据
NFL 数据转储未明确指出哪些比赛是跑动(也称为冲球),哪些是传球。因此,我必须通过一些足球知识和推理来对进攻比赛类型进行分类。
立即,我可以摆脱 isSTPLAY 列中的特殊球队比赛。特殊球队既不是进攻也不是防守,因此与我的目标无关。
#drop st plays
df = df[~df['isSTPlay']]
print(len(df))
浏览 playDescription 列,我看到一些四分卫跪下的比赛,这实际上结束了一次进攻。这通常被称为“胜利阵型”,因为目的是耗尽时间。这些与正常的跑动比赛有很大不同,所以我也可以删除它们。
#drop kneels
df = df[~df['playDescription'].str.contains("kneels")]
print (len(df))
数据以正常比赛的节数(以及每节比赛时钟上的时间)报告时间。就尝试预测序列而言,这是最直观的吗?回答这个问题的一种方法是考虑比赛在时间分割之间有何不同。
当一支球队在第一节还剩一分钟时拥有球权时,它的表现是否会与它在第二节还剩一分钟时拥有球权时相同?可能不会。在上下半场结束时还剩一分钟时,它的表现是否相同?在所有其他条件相同的情况下,在大多数情况下,答案可能是肯定的。
我会将 quarter 和 GameClock 列从节转换为半场,以秒而不是分钟表示。我还会从 quarter 值创建一个 half 列。有一些第五节的值,我将其视为加时赛。由于加时赛规则与正常比赛不同,我可以删除它们。
#drop overtime
df = df[~(df['quarter'] == 5)]
print(len(df))
#convert time/quarters
def translate_game_clock(row):
raw_game_clock = row['GameClock']
quarter = row['quarter']
minutes, seconds_raw = raw_game_clock.partition(':')[::2]
seconds = seconds_raw.partition(':')[0]
total_seconds_left_in_quarter = int(seconds) + (int(minutes) * 60)
if quarter == 3 or quarter == 1:
return total_seconds_left_in_quarter + 900
elif quarter == 4 or quarter == 2:
return total_seconds_left_in_quarter
if 'GameClock' in list (df.columns):
df['secondsLeftInHalf'] = df.apply(translate_game_clock, axis=1)
if 'quarter' in list(df.columns):
df['half'] = df['quarter'].map(lambda q: 2 if q > 2 else 1)
yardlineNumber 列也需要转换。数据当前将码线列为从 1 到 50 的值。同样,这没有帮助,因为球队在自己的 20 码线和对手的 20 码线上的表现不会相同。我会将其转换为表示从 1 到 99 的值,其中 1 码线最靠近拥有球权的球队的端区,而 99 码线最靠近对手的端区。
def yards_to_endzone(row):
if row['possessionTeam'] == row['yardlineSide']:
return 100 - row['yardlineNumber']
else :
return row['yardlineNumber']
df['yardsToEndzone'] = df.apply(yards_to_endzone, axis = 1)
如果我可以将人员数据转换为机器学习算法可以接受的格式,那么人员数据将非常有用。人员确定了给定时间场上不同类型的技术位置。personnel.offense 中当前显示的字符串值不利于输入,因此我会将每个人员位置转换为其自己的列,以指示比赛期间场上的人数。稍后包含防守人员可能会很有趣,看看它是否对预测有任何影响。目前,我只关注进攻。
def transform_off_personnel(row):
rb_count = 0
te_count = 0
wr_count = 0
ol_count = 0
dl_count = 0
db_count = 0
if not pd.isna(row['personnel.offense']):
personnel = row['personnel.offense'].split(', ')
for p in personnel:
if p[2:4] == 'RB':
rb_count = int(p[0])
elif p[2:4] == 'TE':
te_count = int(p[0])
elif p[2:4] == 'WR':
wr_count = int(p[0])
elif p[2:4] == 'OL':
ol_count = int(p[0])
elif p[2:4] == 'DL':
dl_count = int(p[0])
elif p[2:4] == 'DB':
db_count = int(p[0])
return pd.Series([rb_count,te_count,wr_count,ol_count,dl_count, db_count])
df[['rb_count','te_count','wr_count','ol_count','dl_count', 'db_count']] = df.apply(transform_off_personnel, axis=1)
现在,进攻人员值由单独的列表示。

阵型描述了球员在场上的位置,这似乎也对预测比赛结果有价值。再次,我会将字符串值转换为整数。
df['offenseFormation'] = df['offenseFormation'].map(lambda f : 'EMPTY' if pd.isna(f) else f)
def formation(row):
form = row['offenseFormation'].strip()
if form == 'SHOTGUN':
return 0
elif form == 'SINGLEBACK':
return 1
elif form == 'EMPTY':
return 2
elif form == 'I_FORM':
return 3
elif form == 'PISTOL':
return 4
elif form == 'JUMBO':
return 5
elif form == 'WILDCAT':
return 6
elif form=='ACE':
return 7
else:
return -1
df['numericFormation'] = df.apply(formation, axis=1)
print(df.yardlineNumber.unique())
最后,是时候对比赛类型进行分类了。PassResult 列有四个不同的值:I、C、S 和 null,分别代表未完成的传球比赛、完成的传球比赛、擒杀(归类为传球比赛)和一个空值。由于我已经消除了所有特殊球队比赛,我可以假设空值是跑动比赛。因此,我会将比赛结果转换为一个名为 play_type 的单列,用 0 表示跑动,用 1 表示传球。这将是我希望我的算法预测的列(或数据科学家所说的标签)。
def play_type(row):
if row['PassResult'] == 'I' or row['PassResult'] == 'C' or row['PassResult'] == 'S':
return 'Passing'
else:
return 'Rushing'
df['play_type'] = df.apply(play_type, axis = 1)
df['numericPlayType'] = df['play_type'].map(lambda p: 1 if p == 'Passing' else 0)
休息一下
现在可以开始预测事情了吗?到目前为止,我的大部分工作都是试图理解数据以及它需要处于什么格式,然后我才开始预测任何事情。还有人需要休息一下吗?
在第二部分中,我将在将数据馈送到机器学习算法之前对其进行一些分析和可视化,然后我会对模型的结果进行评分,以查看它们的准确性。敬请期待!
评论已关闭。