Skip to content

Latest commit

 

History

History
545 lines (351 loc) · 29 KB

File metadata and controls

545 lines (351 loc) · 29 KB

二十四、使用决策树预测体育获胜者

在本章中,我们将研究使用不同类型的分类算法:决策树来预测体育比赛的获胜者。 与其他算法相比,这些算法具有许多优势。 主要优点之一是它们可以被人类读取。 这样,决策树可用于学习过程,然后可以将其提供给人工执行(如果需要)。 另一个优点是它们可以使用多种功能,我们将在本章中看到这些功能。

我们将在本章介绍以下主题:

  • 使用 pandas 库加载和处理数据
  • 决策树
  • 随机森林
  • 在数据挖掘中使用真实数据集
  • 创建新功能并在强大的框架中对其进行测试

加载数据集

在本章中,我们将预测国家篮球协会NBA)比赛的获胜者。 NBA 中的比赛通常很接近,可以在最后一刻决定,这很难预测获胜者。 许多运动都有此特征,因此预期冠军可能会在适当的时候被另一支球队击败。

有关预测获胜者的各种研究表明,运动结果预测的准确性可能存在上限,视运动而定,其准确性介于 70%到 80%之间。 通常通过数据挖掘或基于统计的方法,对运动预测进行了大量研究。

收集数据

我们将使用的数据是 2013-2014 赛季 NBA 的比赛历史数据,包含从 NBA 和其他联赛收集的大量资源和统计信息。 要下载数据集,请执行以下步骤:

  1. 在您的网络浏览器中导航到这个页面
  2. 单击常规季节标题旁边的导出按钮。
  3. 将文件下载到您的数据文件夹并记下路径。

这将下载 CSV逗号分隔值的缩写)文件,其中包含 NBA 常规赛季 1,230 场比赛的结果。

CSV 文件是简单的文本文件,其中每行包含一个新行,并且每个值都用逗号分隔(因此而得名)。 只需输入文本编辑器并以.csv扩展名保存,即可手动创建 CSV 文件。 它们也可以在任何可以读取文本文件的程序中打开,但是也可以在 Excel 中作为电子表格打开。

我们将使用 pandasPython 数据分析的缩写)库加载文件,该库对于处理数据非常有用。 Python 还包含一个名为csv的内置库,该库支持读写 CSV 文件。 但是,我们将使用 pandas,它提供了更强大的功能,我们将在本章的后面部分使用它们来创建新功能。

注意

对于本章,您将需要安装 Pandas。 最简单的安装方法是使用pip3,就像在第 1 章 “数据挖掘入门”中所做的那样,安装scikit-learn

$pip3 install pandas

如果您在安装[Pandas]时遇到困难,请访问Pandas 网站 并阅读系统的安装说明。

使用 Pandas 加载数据集

pandas 库是用于加载,管理和处理数据的库。 它处理后台数据结构并支持分析方法,例如计算均值。

在进行多个数据挖掘实验时,您会发现您一次又一次地编写了许多相同的功能,例如读取文件和提取功能。 每次重新实现时,您都有引入错误的风险。 使用诸如 pandas 之类的高级库可以显着减少执行这些功能所需的工作量,还可以使您对使用经过良好测试的代码更有信心。

在整个模块中,我们将大量使用 Pandas,并在此过程中介绍用例。

我们可以使用read_csv函数加载数据集:

import pandas as pd
dataset = pd.read_csv(data_filename)

结果是 Pandas数据帧,它具有一些有用的功能,稍后我们将使用它们。 查看结果数据集,我们可以看到一些问题。 键入以下内容并运行代码以查看数据集的前五行:

dataset.ix[:5]

这是输出:

Using pandas to load the dataset

这实际上是一个可用的数据集,但是它包含一些问题,我们将尽快解决。

清理数据集

在查看输出后,我们可以看到许多问题:

  • 日期只是一个字符串,而不是日期对象
  • 第一行是空白
  • 通过目视检查结果,标题不完整或不正确

这些问题来自数据,我们可以通过更改数据本身来解决此问题。 但是,这样做时,我们可能会忘记采取的步骤或错误地使用了这些步骤; 也就是说,我们无法复制结果。 与上一节中使用管道跟踪对数据集所做的转换一样,我们将使用 pandas 将转换应用于原始数据本身。

pandas.read_csv函数具有修复每个问题的参数,我们可以在加载文件时指定这些参数。 加载文件后,我们还可以更改标题,如以下代码所示:

dataset = pd.read_csv(data_filename, parse_dates=["Date"], skiprows=[0,])
dataset.columns = ["Date", "Score Type", "Visitor Team", "VisitorPts", "Home Team", "HomePts", "OT?", "Notes"]

结果有了显着改善,因为我们可以看到是否打印出了结果数据帧:

dataset.ix[:5]

输出如下:

Cleaning up the dataset

即使在诸如此类的经过良好编译的数据源中,您也需要进行一些调整。 不同的系统具有不同的细微差别,导致数据文件彼此之间不太兼容。

现在我们有了数据集,我们可以计算基线了。 基线是一种准确度,表示获得良好准确度的简便方法。 任何数据挖掘解决方案都应该胜过这一点。

在每场比赛中,我们都有两支球队:主队和客队。 一个明显的基线(称为机会率)为 50%。 (随时间推移)随机选择将导致 50%的准确性。

提取新功能

现在,我们可以通过合并和比较现有数据从此数据集中提取特征。 首先,我们需要指定我们的类值,这将为我们的分类算法提供一些参考,以比较其预测是否正确。 这可以用多种方式进行编码。 但是,对于此应用,如果主队获胜,我们将类别指定为 1,如果访问者队获胜,则将类别指定为 0。 在篮球比赛中,得分最高的球队获胜。 因此,尽管数据集未指定谁获胜,但我们可以轻松地进行计算。

我们可以通过以下方式指定数据集:

dataset["HomeWin"] = dataset["VisitorPts"] < dataset["HomePts"]

然后,我们将这些值复制到NumPy数组中,以供以后用于scikit-learn分类器。 Pandas 和 scikit-learn 之间目前尚无干净的集成,但是它们可以通过使用NumPy数组很好地协同工作。 虽然我们将使用 Pandas 提取特征,但我们需要提取值以将其与 scikit-learn 结合使用:

y_true = dataset["HomeWin"].values

现在,前面的数组以 scikit-learn 可以读取的格式保存我们的类值。

我们还可以开始创建一些可用于数据挖掘的功能。 尽管有时我们只是将原始数据扔进分类器中,但我们经常需要推导连续的数值或分类特征。

我们要创建的前两个功能可以帮助我们预测哪支球队会获胜,这就是这两支球队中的哪支球队是否赢得了最后一场比赛。 这大致可以估算出哪支球队表现出色。

我们将通过依次遍历各行并记录获胜团队来计算此功能。 当我们进入新的一行时,我们将检查团队是否在上次看到他们时赢了。

我们首先创建一个(默认)字典来存储团队的最后结果:

from collections import defaultdict 
won_last = defaultdict(int)

这本词典的关键是团队,价值在于他们是否赢得了上一场比赛。 然后,我们可以遍历所有行,并使用团队的最后结果更新当前行:

for index, row in dataset.iterrows():
    home_team = row["Home Team"]
    visitor_team = row["Visitor Team"]
    row["HomeLastWin"] = won_last[home_team]
    row["VisitorLastWin"] = won_last[visitor_team]
    dataset.ix[index] = row

请注意,前面的代码依赖于我们按时间顺序排列的数据集。 我们的数据集是有序的; 但是,如果使用的数据集不按顺序排列,则需要将dataset.iterrows()替换为dataset.sort("Date").iterrows()

然后,在下次看到这些团队时,使用每个团队的结果(在此行中)设置字典。 代码如下:

    won_last[home_team] = row["HomeWin"]
    won_last[visitor_team] = not row["HomeWin"]

在前面的代码运行之后,我们将具有两个新功能:HomeLastWinVisitorLastWin。 我们可以看一下数据集。 不过,看一下前五款游戏并没有多大意义。 由于我们的代码运行方式,当时我们没有用于它们的数据。 因此,直到一支球队第二个赛季,我们才知道他们目前的状态。 我们可以改为查看列表中的其他位置。 以下代码将显示本赛季第 20 到 25 场比赛:

dataset.ix[20:25]

这是输出:

Extracting new features

您可以更改这些索引以查看数据的其他部分,因为我们的数据集中有 1000 多个游戏!

当前,这给所有团队(包括前一年的冠军!)初次见到的时候都是虚假的价值。 我们可以使用上一年的数据来改进此功能,但本章将不做。

决策树

决策树是一类监督学习算法,例如由一系列节点组成的流程图,其中样本值用于对要进入的下一个节点进行决策。

Decision trees

与大多数分类算法一样,有两个组件:

  • 第一个是训练阶段,其中使用训练数据构建一棵树。 虽然上一章中最接近的邻居算法没有训练阶段,但决策树需要它。 这样,最近邻居算法是一个懒惰的学习者,仅在需要进行预测时才做任何工作。 相反,决策树像大多数分类方法一样,都是渴望学习的人,在训练阶段就开始工作。
  • 第二个是预测阶段,其中训练有素的树用于预测新样本的分类。 使用前面的示例树,数据点["is raining", "very windy"]将被分类为"bad weather"

创建决策树的算法很多。 这些算法很多都是迭代的。 它们从基础节点开始,并确定要用于第一个决策的最佳功能,然后转到每个节点并选择下一个最佳功能,依此类推。 当确定无法进一步扩展树时,该过程将在某个点停止。

scikit-learn程序包将 CART分类和回归树)算法实现为其默认决策树类,该算法可以同时使用分类和连续功能。

决策树中的参数

决策树的最重要的特征之一就是停止标准。 当构建一棵树时,最后的几个决策通常在某种程度上是任意的,并且仅依靠少量样本做出决策。 使用这样的特定节点可能会导致树大大超出训练数据。 相反,可以使用停止标准来确保决策树未达到此准确性。

除了使用停止条件外,还可以完整地创建树,然后进行修剪。 此修整过程将删除不会为整个过程提供太多信息的节点。 这被称为修剪。

scikit-learn 中的决策树实现提供了一种使用以下选项停止构建树的方法:

  • min_samples_split:这指定需要多少样本才能在决策树中创建一个新节点
  • min_samples_leaf:这指定从节点留下的样本必须为多少

第一个指示是否将创建决策节点,而第二个指示是否将保留决策节点。

决策树的另一个参数是创建决策的标准。 基尼杂质和信息增益是两个流行的:

  • 基尼杂质:这是衡量决策节点错误地预测样本类别的频率的方法
  • 信息增益:这使用基于信息论的熵来指示决策节点获得了多少额外信息

使用决策树

我们可以导入DecisionTreeClassifier类,并使用 scikit-learn 创建决策树:

from sklearn.tree import DecisionTreeClassifierclf = DecisionTreeClassifier(random_state=14)

提示

我们再次将 14 用于random_state,并将在大多数模块中使用。 使用相同的随机种子可以复制实验。 但是,在进行实验时,应混合使用随机状态,以确保算法的性能不受特定值的限制。

现在,我们需要从我们的 Pandas 数据框中提取数据集,以便将其与scikit-learn分类器一起使用。 为此,我们指定希望使用的列,并使用数据框视图的values参数。 以下代码使用我们的主队和客队的最后获胜值创建一个数据集:

X_previouswins = dataset[["HomeLastWin", "VisitorLastWin"]].values

决策树是估计器,如在第 2 章,“使用 scikit-learn 估计器进行分类”中,因此具有fitpredict方法。 我们还可以使用cross_val_score方法来获得平均分数(就像我们之前所做的那样):

scores = cross_val_score(clf, X_previouswins, y_true, scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

得分 56.1%:我们比随机选择更好! 我们应该能够做得更好。 特征工程是数据挖掘中最困难的任务之一,选择好的特征是获得好的结果的关键-比选择正确的算法还重要!

Using decision trees

运动成绩预测

通过尝试其他功能,可能会做得更好。 我们有一种方法可以测试模型的准确性。 cross_val_score方法允许我们尝试新功能。

我们可以使用许多可能的功能,但是我们将尝试以下问题:

  • 一般认为哪个小组更好?
  • 哪支球队赢得了最后一场比赛?

我们还将尝试将原始团队放入算法中,以检查算法是否可以学习一个模型,该模型可以检查不同团队之间的对抗方式。

全部放在一起

对于第一个功能,我们将创建一个功能来告诉我们主队通常是否比访问者更好。 为此,我们将在上个赛季从 NBA 获得排名(在某些运动中也称为阶梯)。 如果一个团队在 2013 年排名高于其他团队,就会被认为会更好。

要获取排名数据,请执行以下步骤:

  1. 在您的网络浏览器中导航到这个页面
  2. 选择扩展排名以获取整个联赛的一个列表。
  3. 单击导出链接。
  4. 将下载的文件保存在您的数据文件夹中。

返回您的 IPython Notebook,在新单元格中输入以下行。 您需要确保文件已保存到data_folder变量指向的位置。 代码如下:

standings_filename = os.path.join(data_folder, "leagues_NBA_2013_standings_expanded-standings.csv")
standings = pd.read_csv(standings_filename, skiprows=[0,1])

您可以通过在新单元格中键入standings并运行代码来查看梯形图:

Standings

输出如下:

Putting it all together

接下来,我们使用与先前功能相似的模式创建一个新功能。 我们遍历行,查找主队和客队的排名。 代码如下:

dataset["HomeTeamRanksHigher"] = 0
for index, row in dataset.iterrows():
    home_team = row["Home Team"]
    visitor_team = row["Visitor Team"]

为了对数据进行重要调整,在 2013 和 2014 赛季之间改名了一支球队(但仍然是同一支球队)。 这是尝试集成数据时可能发生的许多不同事情之一的示例! 我们将需要调整团队查找,以确保获得正确的团队排名:

    if home_team == "New Orleans Pelicans":
        home_team = "New Orleans Hornets"
    elif visitor_team == "New Orleans Pelicans":
        visitor_team = "New Orleans Hornets"

现在我们可以获取每个团队的排名。 然后,我们将它们进行比较并更新该行中的功能:

  home_rank = standings[standings["Team"] == home_team]["Rk"].values[0]
        visitor_rank = standings[standings["Team"] == visitor_team]["Rk"].values[0]
        row["HomeTeamRanksHigher"] = int(home_rank > visitor_rank)
        dataset.ix[index] = row

接下来,我们使用cross_val_score功能测试结果。 首先,我们提取数据集:

X_homehigher =  dataset[["HomeLastWin", "VisitorLastWin", "HomeTeamRanksHigher"]].values

然后,我们创建一个新的DecisionTreeClassifier并运行评估:

clf = DecisionTreeClassifier(random_state=14)
scores = cross_val_score(clf, X_homehigher, y_true, scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

现在,该分数为 60.3%,甚至比我们之前的结果还要好。 我们可以做得更好吗?

接下来,让我们测试两支球队中的哪支赢得了最后一场比赛。 虽然排名可以暗示谁是赢家(排名较高的球队更有可能获胜),但有时球队与其他球队的比赛表现更好。 造成这种情况的原因很多,例如,某些团队可能制定了与其他团队非常有效的策略。 按照我们以前的模式,我们创建了一个字典来存储过去游戏的获胜者,并在数据框中创建一个新功能。 代码如下:

last_match_winner = defaultdict(int)
dataset["HomeTeamWonLast"] = 0

然后,我们遍历每一行并获得主队和访客队:

for index, row in dataset.iterrows():
    home_team = row["Home Team"]
    visitor_team = row["Visitor Team"]

我们想看看谁赢得了这两支球队之间的最后一场比赛,而与哪支球队在家中比赛无关。 因此,我们按字母顺序对团队名称进行排序,从而为这两个团队提供一致的密钥:

      teams = tuple(sorted([home_team, visitor_team]))

我们在字典中查询,看谁赢得了两支队伍之间的最后一场比赛。 然后,我们更新数据集数据框中的行:

      row["HomeTeamWonLast"] = 1 if last_match_winner[teams] == row["Home Team"] else 0
        dataset.ix[index] = row

最后,我们用该游戏的赢家更新了我们的词典,以便计算这两个团队下次见面时的功能:

    winner = row["Home Team"] if row["HomeWin"] else row["Visitor Team"]
    last_match_winner[teams] = winner

接下来,我们将仅创建具有两个功能的数据集。 您可以尝试不同的功能组合,以查看它们是否获得不同的结果。 代码如下:

X_lastwinner =  dataset[["HomeTeamRanksHigher", "HomeTeamWonLast"]].values
clf = DecisionTreeClassifier(random_state=14)
scores = cross_val_score(clf, X_lastwinner, y_true, scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

这占 60.6%。 我们的结果越来越好。

最后,我们将检查如果在决策树上投入大量数据会发生什么,并查看它是否仍然可以学习有效的模型。 我们将使团队进入树状结构,并检查决策树是否可以学习合并这些信息。

尽管决策树能够从分类特征中学习,但是 scikit-learn 中的实现要求首先对那些特征进行编码。 我们可以使用LabelEncoder转换器在基于字符串的团队名称之间转换为整数。 代码如下:

from sklearn.preprocessing import LabelEncoder
encoding = LabelEncoder()

我们将把此转换器安装到主队中,以便为每个队学习一个整数表示形式:

encoding.fit(dataset["Home Team"].values)

我们提取主队和访客队的所有标签,然后将它们加入(在NumPy中称为堆叠),以创建一个矩阵,对每个主队和访客队进行编码 游戏。 代码如下:

home_teams = encoding.transform(dataset["Home Team"].values)
visitor_teams = encoding.transform(dataset["Visitor Team"].values)
X_teams = np.vstack([home_teams, visitor_teams]).T

这些整数可以输入到决策树中,但是DecisionTreeClassifier仍会将它们解释为连续特征。 例如,可以为团队分配 0 到 16 的整数。算法将使团队 1 和 2 相似,而团队 4 和 10 则不同-但这对所有人都没有意义。 所有团队都互不相同-两个团队要么相同,要么不一样!

要解决此不一致的问题,我们使用OneHotEncoder转换器将这些整数编码为许多二进制特征。 每个二进制特征将是该特征的单个值。 例如,如果LabelEncoder将 NBA 芝加哥公牛队分配为整数 7,则如果该团队是芝加哥公牛队,则OneHotEncoder返回的第七个特征将为 1,而所有球队均为 0 其他团队。 针对每个可能的值执行此操作,从而导致更大的数据集。 代码如下:

from sklearn.preprocessing import OneHotEncoder
onehot = OneHotEncoder()

我们对同一个数据集进行拟合和变换,保存结果:

X_teams_expanded = onehot.fit_transform(X_teams).todense()

接下来,我们像以前一样在新数据集上运行决策树:

clf = DecisionTreeClassifier(random_state=14)
scores = cross_val_score(clf, X_teams_expanded, y_true, scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

得分准确性为 60%。 分数比基线好,但不及以前。 决策树可能无法正确处理大量特征。 因此,我们将尝试更改算法,看看是否有帮助。 数据挖掘可以是尝试新算法和功能的迭代过程。

随机森林

单个决策树可以学习非常复杂的功能。 但是,在许多方面,它很容易过拟合-仅适用于训练集的学习规则。 我们可以对此进行调整的方法之一是限制它学习的规则数量。 例如,我们可以将树的深度限制为仅三层。 这样的树将学习在全局范围内分割数据集的最佳规则,但不会学习将数据集分为高度精确的组的高度特定的规则。 这种权衡导致树可能具有良好的泛化性,但总体性能稍差。

为了弥补这一点,我们可以创建许多决策树,然后要求每个决策树预测类值。 我们可以进行多数表决,并将该答案用作我们的总体预测。 随机森林按照这一原则开展工作。

上述过程存在两个问题。 第一个问题是,构建决策树在很大程度上是确定性的-使用相同的输入将每次产生相同的输出。 我们只有一个训练数据集,这意味着如果我们尝试构建多棵树,我们的输入(以及输出)将是相同的。 我们可以通过选择数据集的随机子样本来解决此问题,从而有效地创建new训练集。 该处理称为套袋

的第二个问题是,用于树中前几个决策节点的功能将非常好。 即使我们选择训练数据的随机子样本,建立的决策树在很大程度上仍将是相同的。 为了弥补这一点,我们还选择了特征的随机子集来执行数据拆分。

然后,我们使用(几乎)随机选择的特征,使用随机选择的样本随机构建了树。 这是一个随机森林,也许是而不是,该算法在许多数据集上非常有效。

集成如何工作?

随机森林中固有的随机性可能使我们似乎将算法的结果留给了机会。 但是,我们将平均的好处应用于几乎随机建立的决策树,从而产生了一种可以减少结果差异的算法。

方差是算法中训练数据集变化引起的误差。 训练数据集的变化会极大地影响方差较大的算法(例如决策树)。 这导致模型存在过度拟合的问题。

注意

与相反,偏差是算法中的假设所引入的误差,而不是与数据集有关的误差,也就是说,如果我们有一个算法假定所有特征都是正态分布的,那么我们 如果功能不具备,则算法可能会有很高的错误。 通过分析数据以查看分类器的数据模型是否与实际数据相匹配,可以减少偏差带来的负面影响。

通过平均大量决策树,可以大大减少这种差异。 这导致模型具有更高的整体精度。

总的来说,合奏是基于这样的假设,即预测中的误差实际上是随机的,并且各个分类器之间的误差完全不同。 通过对许多模型的结果求平均值,可以消除这些随机误差,从而留下真实的预测结果。 在本书的其余部分中,我们将看到更多的合奏。

随机森林中的参数

scikit-learn 中的随机森林实现称为RandomForestClassifier,它具有许多参数。 由于随机森林使用的许多实例,因此它们共享许多相同的参数,例如标准(基尼杂质或熵/信息增益),max_featuresmin_samples_split

此外,合奏过程中使用了一些新参数:

  • n_estimators:这指示应构建多少个决策树。 较高的值将花费更长的时间运行,但(可能)会导致较高的精度。
  • oob_score:如果为 true,则使用不在用于训练决策树的随机子样本中的样本对方法进行测试。
  • n_jobs:此指定在并行训练决策树时要使用的核心数。

scikit-learn程序包使用名为Joblib的库进行内置并行化。 此参数决定要使用多少个内核。 默认情况下,仅使用单个核心-如果您具有更多核心,则可以增加该核心,或将其设置为-1 以使用所有核心。

应用随机森林

scikit-learn 中的随机森林使用 estimator 接口,使我们可以使用几乎与之前完全相同的代码来进行交叉折叠验证:

from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier(random_state=14)
scores = cross_val_score(clf, X_teams, y_true, scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

通过交换分类器,可以立即带来 60.6%的收益,提高了 0.6 个百分点。

使用特征子集的随机森林应该能够比正常决策树更有效地学习更多特征。 我们可以通过在算法上添加更多功能并查看其运行方式来进行测试:

X_all = np.hstack([X_home_higher, X_teams])
clf = RandomForestClassifier(random_state=14)
scores = cross_val_score(clf, X_all, y_true, scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

结果是 61.1%,甚至更好! 我们还可以使用类尝试其他一些参数,如我们在第 2 章,“使用 scikit-learn 估计器进行分类”中介绍的那样:

parameter_space = {
  "max_features": [2, 10, 'auto'],
  "n_estimators": [100,],
  "criterion": ["gini", "entropy"],
  "min_samples_leaf": [2, 4, 6],
}
clf = RandomForestClassifier(random_state=14)
grid = GridSearchCV(clf, parameter_space)
grid.fit(X_all, y_true)
print("Accuracy: {0:.1f}%".format(grid.best_score_ * 100))

准确率高达 64.2%!

如果我们想查看所使用的参数,我们可以打印出在网格搜索中找到的最佳模型。 代码如下:

print(grid.best_estimator_)

结果显示了最佳评分模型中使用的参数:

RandomForestClassifier(bootstrap=True, compute_importances=None,criterion='entropy', max_depth=None, max_features=2,max_leaf_nodes=None, min_density=None, min_samples_leaf=6,min_samples_split=2, n_estimators=100, n_jobs=1,oob_score=False, random_state=14, verbose=0)

工程新功能

在的前面几个示例中,我们看到更改功能可能会对算法的性能产生很大影响。 通过少量的测试,我们仅在功能上就有超过 10%的差异。

您可以通过执行以下操作来创建来自 Pandas 中的简单功能的功能:

dataset["New Feature"] = feature_creator()

feature_creator函数必须返回数据集中每个样本的特征值列表。 一种常见的模式是将数据集用作参数:

dataset["New Feature"] = feature_creator(dataset)

您可以通过将所有值设置为单个“默认”值来更直接地创建这些功能,例如下一行中的 0:

dataset["My New Feature"] = 0

然后,您可以遍历数据集,随时进行特征计算。 在本章中,我们使用了这种格式来创建许多功能:

  for index, row in dataset.iterrows():
    home_team = row["Home Team"]
    visitor_team = row["Visitor Team"]
    # Some calculation here to alter row dataset.ix[index] = row

请记住,这种模式不是很有效。 如果要执行此操作,请立即尝试所有功能。 常见的“最佳实践”是尽可能少地触摸每个样本,最好只接触一次。

您可以尝试实现的一些示例功能如下:

  • 自每支球队上一场比赛以来已有多少天? 如果团队在短时间内玩太多游戏,可能会感到疲倦。
  • 每支球队在最近五场比赛中赢了几场? 这将为我们较早提取的HomeLastWinVisitorLastWin特征提供更稳定的形式(并且可以非常相似的方式提取)。
  • 拜访某些其他团队时,团队是否有良好记录? 例如,即使是访客,一支球队也可能在特定的体育场打得很好。

如果在提取这些类型的功能时遇到麻烦,请查看这个页面上的 Pandas 文档。 或者,您可以尝试使用在线论坛(例如 Stack Overflow)寻求帮助。

更极端的例子可能是使用球员数据来估算每支球队的实力来预测谁获胜。 赌徒和体育博彩公司每天都使用这些类型的复杂功能,通过预测体育比赛的结果来尝试获利。

Engineering new features

Engineering new features

Engineering new features

Engineering new features