前几篇文章所用到的数据集都是用特征来描述的。 虽然的用到的数据集为交易类型的数据,形式
稍有不同,但归根结底还是一种用特征来描述的数据集。
除此之外,还有很多其他类型的数据集,比如文本、图像、声音、视频甚至是真实的物体。
然而,大多数数据挖掘算法都依赖于数值或类别型特征。 这表明在使用数据挖掘算法处理它们之
前,需要找到一种表示它们的方法。
本文所讨论的是如何从数据集中抽取数值和类别型特征,并选出最佳特征,前提是数据集确
实包含这些特征。 我们还会介绍特征抽取的常用模式和技巧。
本文主要介绍以下几个概念。
  • 从数据集中抽取特征
  • 创建新特征
  • 选取好特征
  • 创建转换器,处理数据集

特征抽取

特征抽取是数据挖掘任务最为重要的一个环节,一般而言,它对最终结果的影响要高过数据挖掘算法本身。 不幸的是,关于怎样选取好的特征,还没有严格、快捷的规则可循,其实这也是数据挖掘科学更像是一门艺术的所在。 创建好的规则离不开直觉,还需要专业领域知识和数据挖掘经验,光有这些还不够,还得不停地尝试、摸索,在试错中前进,有时多少还要靠点运气。

1.在模型中表示事实

不是所有的数据集都是用特征来表示的。 数据集可以是一位作家所写的全部书籍,也可以是
1979年以来所有电影的胶片,还可以是馆藏的一批历史文物。 5.1 特征抽取 63
1
我们可能想对以上数据集做数据挖掘。 对于作家的作品,我们想知道这位作家都写过哪些主题;对于电影,我们想知道女性形象是怎么塑造的;对于文物,我们想知道它们是何方产物。 我们没办法把实物直接塞进决策树来得到结果。
只有先把现实用特征表示出来,才能借助数据挖掘的力量找到问题的答案。 特征可用于建模, 模型以机器挖掘算法能够理解的近似的方式来表示现实。 因此可以说,模型描述了客观世界中对象的某些方面,是它的一个简化版本。 举例来说,象棋就是对过往战事简化后得到的一种模型。
特征选择的另一个优点在于:降低真实世界的复杂度,模型比现实更容易操纵。 想象一下,向一个没有任何背景知识的人介绍一种新事物,要给出恰如其分、精确又不失全面的描述需要提供多少信息。 我们可能需要介绍其尺寸、重量、质地、成分、年龄、瑕疵、用途、产地等信息。
实物的复杂性对目前的算法而言过于复杂,我们退而求其次,使用更为简洁的模型来表示实物。
简化要以数据挖掘应用的目标为核心。 本书后面会讲到聚类,对这类应用而言,特征选取至关重要,如果随意选取特征,分簇结果因选择的特征而异,呈现出很强的随机性。
降低复杂性有好处,但也有不足,简化会忽略很多细节,甚至会抛弃很多对数据挖掘算法能起到帮助作用的信息。
我们应该始终关注怎么用模型来表示现实,要多考虑数据挖掘的目标,而不是轻率地用我们过去用过的特征。 要经常想想我们的目标是什么。 第3章创建特征时充分考虑目标(预测赢家), 使用篮球比赛领域相关知识,找出哪些因素与比赛结果密切相关,据此再创建新特征。
用Adult数据集讲解如何借助特征为复杂的现实世界建模再好不过。 我们这里数据挖掘的目
标是,预测一个人是否年收入多于五万美元。 数据集下载地址为http://archive.ics.uci.edu/ml/
datasets/Adult,点击Data Folder链接。 下载adult.data和adult.names文件,在数据集文件夹(主目录下Data文件夹)中创建Adult文件夹,将这两个文件放到里面。
数据集用特征描述了一个个活生生的人及其他们所处的环境、背景和生活状况。
打开IPython Notebook,新建一个笔记本文件用于本章实验,导入pandas模块,指定数据集文
件的路径,用pandas加载数据集文件。
import os
import pandas as pd
data_folder = os.path.join(os.path.expanduser("~"), "Data","Adult")
adult_filename = os.path.join(data_folder, "adult.data")
Using pandas as before, we load the file with read_csv:
adult = pd.read_csv(adult_filename, header=None, names=["Age", "Work-Class", "fnlwgt", "Education", "Education-Num", "Marital-Status", "Occupation", "Relationship", "Race", "Sex", "Capital-gain", "Capital-loss", "Hours-per-week", "Native-Country", "Earnings-Raw"])

大部分代码前几篇文章都见过。

adult.data文件末尾有两处空行。 pandas默认把倒数第二行空行作为一条有效数据读入(只不
过各列的值为空)。 我们需要删除包含无效数字的行(设置inplace参数为真,表示改动当前数
据框,而不是新建一个)。
adult.dropna(how='all', inplace=True)
下面为所有保存在pandas的Index对象中的特征名称。
Index(['Age', 'Work-Class', 'fnlwgt', 'Education',
'Education-Num', 'Marital-Status', 'Occupation', 'Relationship',
'Race', 'Sex', 'Capital-gain', 'Capital-loss', 'Hours-per-week',
'Native-Country', 'Earnings-Raw'], dtype='object')

2.通用的特征创建模式

纵然有数不清的特征创建方法,但其中有一些是适用于不同学科的通用模式。然而,选择合适的特征是项技术活,需要考虑特征和最终结果之间的相互关系。正如谚语所说的,不要用封面来评价书的好坏——如果你对书的内容感兴趣,书的大小当然也不用考虑。
一些通用特征关注的是所研究对象的物理属性。
  • 空间属性,比如对象的长、宽、高
  • 重量和(或)密度
  • 年龄、使用年限或成分
  • 种类
  • 品质
另一些特征可能与对象的使用或历史相关。
  • 生产商、出版商或创造者
  • 生产时间
  • 使用方法
还有一些特征从组成成分角度描述对象。
  • 次级成分的频率,如一本书中某个单词的词频
  • 次级成分的数量和(或)次级成分种类的数量
  • 次级成分的平均大小,例如平均句长
序数特征可对其排序、分组。正如前几章所见到的,特征可以是数值或类别型特征。数值特
征通常被看作是有顺序的(ordinal)。例如,爱丽丝、鲍勃、查理三个人身高分别为1.5m、1.6m
和1.7m。我们可以说爱丽丝和鲍勃,比起爱丽丝和查理,在身高上更相似。
我们导入的数据集,包含连续特征、序数特征。例如,Hours-per-week特征表示一个人每周的工作时间。这个特征可用来计算平均工作时间、标准差、最大值和最小值。pandas提供了常见统计量的计算方法。
adult["Hours-per-week"].describe()
输出结果让我们对这个特征有了更多了解。
count        32561.000000
mean        40.437456
std            12.347429
min             1.000000
25%          40.000000
50%          40.000000
75%          45.000000
max          99.000000
dtype:        float64
这些统计方法对其他特征可能没有意义。例如,计算受教育程度的和就讲不通
还有些特征,它们虽然不是数值,但仍然属于序数值,比如Adult数据集中的Education(教
育)特征,学士学位比高中毕业,在受教育程度上,要高一个层次,而高中毕业又比没有完成高
中学业高一个层次。计算它俩的均值就没有意义,但是我们可以找到一种近似的表示方法。数据
集有个叫作Education-Num(受教育年限)的特征,它的取值基本上就是每个教育阶段的年限。
这样我们就能快速计算受教育年限的均值。
adult["Education-Num"].median()
结果为10,或者可以表述为刚好读完高一。如果没有受教育年限数据,我们为不同教育阶段
指定数字编号,也可以计算均值。

array([' State-gov', ' Self-emp-not-inc', ' Private', ' Federal-gov', ' Local-gov', ' ?', ' Self-emp-inc', ' Without-pay', ' Never-worked', nan], dtype=object)
上做类似的区分——它们要么是同
一类,要么不是。
类别型特征二值化后就变成了数值型特征,第3章中曾经用过。对于上述球的种类,可以创建三个二值特征:“是网球”“是棒球”和“是足球”。如果是网球的话,特征向量就是[1, 0, 0], 棒球[0, 1, 0],足球[0, 0, 1]。这些特征都只有两个取值,很多算法都可以把它们作为连续型特征使用。二值化的好处是,便于直接进行数字上的比较(例如计算个体之间的距离)。
Adult数据集包含一些类别型特征,例如Work-Class(工作),其中它的有些值可以进行量
级上的比较,比如不同的就业状况影响收入(例如有工作的人比没有工作的人收入高)。再比如,
州政府职员的工资不太可能比私企职工工资高。
用数据框的unique函数就能得到所有的工作情况。
from scipy.stats import pearsonr

输出结果如下:

array([' State-gov', ' Self-emp-not-inc', ' Private', ' Federal-gov',
' Local-gov', ' ?', ' Self-emp-inc', ' Without-pay',
' Never-worked', nan], dtype=object)
数据集部分数据缺失,但不会影响这里的计算。
同理,数值型特征也可以通过离散化过程转换为类别型特征,第4章中曾用过。例如,高于1.7m的人,我们称其为高个子,低于1.7m的叫作矮个子。这样就得到了类别型特征(虽然是序数值类型)。在这个过程中确实丢失了一些信息。例如,有两个人,身高分别为1.69m和1.71m,这两个个子差不多的将被分到两个截然不同的类别里。而身高仅为1.2m的将会被认为与身高1.69m的“差不多高”!细节的丢失是离散化不好的一面,也是建模时需要考虑解决的问题。
对于Adult数据集,我们可以创建LongHours(时长)特征,用它来表示一个人每周工作时长是否多于40小时。这样就把连续值(Hours-per-week,每周工作时长)转换为类别型特征。
adult["LongHours"] = adult["Hours-per-week"] > 40

3 创建好的特征

建模过程中,需要对真实世界中的对象进行简化,这会导致信息的丢失,这也就是为什么没有一套能够用于任何数据集的通用的数据挖掘方法。数据挖掘的行家里手需要拥有数据来源领域的知识,没有的话,要积极去掌握。他们弄清楚问题是什么,了解有哪些可用数据后,在此基础上,才能创建解决问题所需的模型。
例如,身高这个特征描述的是一个人外表的某个方面,但是不能说明这个人学习成绩如何,所以预测学习成绩时,我们没必要去测量每个人的身高。
这正是数据挖掘为什么变得更像是一门艺术而不是科学的原因所在。抽取好的特征难度不小,该课题具有重要研究意义,因此它获得了研究人员的持续关注。选择好的分类算法也可以提升数据挖掘应用的效果,但最好还是通过选用好的特征来达到同样的目的。
不论是设计什么数据挖掘应用,首先都应该确定大致的方向,然后再设计方法实现目标。知道自己的目标之后,就能确定所需要的特征类型和算法,对最终结果也做到心中有数。

4 特征选择

通常特征数量很多,但我们只想选用其中一小部分。有如下几个原因。

  • 降低复杂度:随着特征数量的增加,很多数据挖掘算法需要更多的时间和资源。减少特
  • 征数量,是提高算法运行速度,减少资源使用的好方法。
  • 降低噪音:增加额外特征并不总会提升算法的表现。额外特征可能扰乱算法的正常工作,
  • 这些额外特征间的相关性和模式没有实际应用价值(这种情况在小数据集上很常见)。只
  • 选择合适的特征有助于减少出现没有实际意义的相关性的几率。
  • 增加模型可读性:根据成千上万个特征创建的模型来解答一个问题,对计算机来说很容 模型对我们自己来说就晦涩无比。因此,使用更少的特征,创建我们自己可以理解的模型,就很有必要。
有些分类算法确实很强壮,能够处理噪音问题,特征再多也不在话下,但是选用干净的数据,选取更具描述性的特征,对算法效果提升很有帮助。
在开展数据挖掘工作前,有些基础性测试我们要做,比如确保特征值是不同的。如果特征值都相同,就跟没提供什么信息一样,挖掘就失去了意义。
scikit-learn中的VarianceThreshold转换器可用来删除特征值的方差达不到最低标准的特征。下面通过代码来讲下它的用法,首先用numpy创建一个简单的矩阵。
import numpy as np
X = np.arange(30).reshape((10, 3))
上述矩阵包含0到29,共30个数字,分为3列10行。可以把它看成一个有10个个体、3个特征的数据集。
array ([[ 0, 1, 2],
           [ 3, 4, 5],
           [ 6, 7, 8],
           [ 9, 10, 11],   
[12, 13,   14] 
[15, 16, 17],
           [18, 19, 20],
           [21, 22, 23],
           [24, 25, 26],
           [27, 28, 29]])  
接着,把所有第二列的数值都改为1。
X[:,1] = 1
第一、三列特征值方差很大,而第二列方差为0。
array                       ([[ 0, 1, 2],
                                 [ 3, 1, 5],
                                 [ 6 , 1, 8],
                                 [ 9, 1, 11],
                                 [12, 1, 14],
                                 [15, 1, 17],
                                 [18, 1, 20],
                                 [21, 1, 23],
                                 [24, 1, 26],
                                 [27, 1, 29]])
这时再来创建VarianceThreshold转换器,用它处理数据集。
from sklearn.feature_selection import VarianceThreshold
vt = VarianceThreshold()
Xt = vt.fit_transform(X)
输出Xt后,我们发现第二列消失了
array                              ([[ 0, 2],
                                        [ 3, 5],
                                        [ 6, 8],
                                        [ 9, 11],
                                        [12, 14],
                                        [15, 17],
                                       [18, 20],
                                       [21, 23],
                                       [24, 26],
                                       [27, 29]])
输出每一列的方差。
print(vt.variances_)
下面输出结果表明第一、三列包含有价值信息,第二列方差为0,不包含具有区别意义的信息。
array([ 74.25, 0. , 74.25])
无论什么时候,拿到数据后,先做下类似简单、直接的分析,对数据集的特点做到心中有数。方差为0的特征不但对数据挖掘没有丝毫用处,相反还会拖慢算法的运行速度。
特征很多的情况下,怎么选出最佳的几个,可有点难度。它与解决数据挖掘问题自身相关,计算量很大。正如第4章讲到的,随着特征数量的增加,寻找子集的任务复杂度呈指数级增长。寻找最佳特征组合的时间复杂度同样是指数级增长的。
其中一个变通方法是不要找表现好的子集,而只是去找表现好的单个特征(单变量),依据是它们各自所能达到的精确度。分类任务通常是这么做的,我们一般只要测量变量和目标类别之间的某种相关性就行。
scikit-learn提供了几个用于选择单变量特征的转换器,其中SelectKBest返回k个最佳特征,SelectPercentile返回表现最佳的前r%个特征。这两个转换器都提供计算特征表现的一系列方法。
单个特征和某一类别之间相关性的计算方法有很多。最常用的有卡方检验(χ2 )。其他方法还有互信息和信息熵。
我们可以测试单个特征在Adult数据集上的表现。首先,选取下述特征,从pandas数据框中抽取一部分数据。
X = adult[["Age", "Education-Num", "Capital-gain", "Capital-loss", "Hours-per-week"]].values
接着,判断Earnings-Raw(税前收入)是否达到五万美元,创建目标类别列表。如果达到,类别为True,否则,类别为False。代码如下:
y = (adult["Earnings-Raw"] == ' >50K').values
再使用SelectKBest转换器类,用卡方函数打分,初始化转换器。
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
transformer = SelectKBest(score_func=chi2, k=3)
调用fit_transform方法,对相同的数据集进行预处理和转换。结果为分类效果较好的三个特征。代码如下:
Xt_chi2 = transformer.fit_transform(X, y)
生成的矩阵只包含三个特征。我们还可以得到每一列的相关性,这样就可以知道都使用了哪些特征。还是看下代码:
print(transformer.scores_)
输出结果如下:
[ 8.60061182e+03 2.40142178e+03 8.21924671e+07 1.37214589e+06
6.47640900e+03]
相关性最好的分别是第一、三、四列,分别对应着Age(年龄)、Capital-Gain(资本收益)和Capital-Loss(资本损失)三个特征。从单变量特征选取角度来说,这些就是最佳特征。
还可以用其他方法计算相关性,比如皮尔逊(Pearson)①相关系数。用于科学计算的SciPy
库(scikit-learn在它基础上开发)实现了该方法。
首先,从SciPy导入pearsonr函数。
from scipy.stats import pearsonr

当前例子中为x和y)作为参数,返回两个数组:每个特征的皮尔逊相关系数和p值(p-value)。
前面用到的chi2函数使用的接口符合要求,因此我们直接把它传入到SelectKBest函数中。 SciPy的pearsonr函数参数为两个数组,但要注意的是第一个参数x为一维数组。我们来实现一个包装器函数,这样就能像前面那样处理多维数组。函数声明如下:
def multivariate_pearsonr(X, y):

创建scores和pvalues数组,遍历数据集的每一列
scores, pvalues = [], []
for column in range(X.shape[1]):

只计算该列的皮尔逊相关系数和p值,并将其存储到相应数组中。
cur_score, cur_p = pearsonr(X[:,column], y)
scores.append(abs(cur_score))
pvalues.append(cur_p)

函数最后返回包含皮尔逊相关系数和p值的元组
return (np.array(scores), np.array(pvalues))

现在,我们就可以像之前那样使用转换器类,根据皮尔逊相关系数对特征进行排序。
transformer = SelectKBest(score_func=multivariate_pearsonr, k=3)
Xt_pearson = transformer.fit_transform(X, y)
print(transformer.scores_)

返回的特征跟用卡方检验计算相关性得到的特征不一样!这回得到的是第一、二、五列:Age、

Education和Hours-per-week。这表明哪些特征是最好的这个问题没有标准答案——取决于度
量标准。
我们在分类器中看看哪个特征集合效果更好。请注意实验结果也只表明对于特定分类器和(或)特征子集效果更好——在数据挖掘领域,一种方法在任何情况下都比另一种方法好的情况
几乎没有!实验代码如下:
from sklearn.tree import DecisionTreeClassifier
from sklearn.cross_validation import cross_val_score
clf = DecisionTreeClassifier(random_state=14)
scores_chi2 = cross_val_score(clf, Xt_chi2, y, scoring='accuracy')
scores_pearson = cross_val_score(clf, Xt_pearson, y, scoing='accuracy')

chi2方法的平均正确率为0.83,而皮尔逊相关系数正确率为0.77。用卡方检验得到的特征组合效果更好!
别忘了本文数据挖掘任务目标是预测收入。使用特征选择技术,找到好的特征组合,只用三个特征,就能达到83%的正确率!
有时,仅仅选择已有特征不够用。这时,我们就可以用不同的方法从已有特征中发掘新特征,比如前面用到的一位有效编码(one-hot encoding),我们可以用它把有A、B、C三个选项的类别型特征转换为三个新的特征:“是A吗?”“是B吗?”和“是C吗?”。
创建新特征看上去没必要,也没什么好处——毕竟,这些信息原本就在数据集里,我们只是要用而已。然而,特征之间相关性很强,或者特征冗余,会增加算法处理难度。
出于这个原因,创建特征就很有必要,很多方法也应运而生。
接下来,我们要加载一个新数据集,正好也该创建一个新的IPython Notebook笔记本文件了。从http://archive.ics.uci.edu/ml/datasets/Internet+Advertisements下载Advertisements(广告)数据集, 保存到自己主目录下的Data文件夹中。
接着,用pandas加载数据集。我们还是先指定文件的路径。
import os
import numpy as np
import pandas as pd
data_folder = os.path.join(os.path.expanduser("~"), "Data")
data_filename = os.path.join(data_folder, "Ads", "ad.data")
数据集存在几个问题,加载过程需要我们做些处理。问题一,前几个特征是数值,但是pandas
会把它们当成字符串。要修复这个问题,我们需要编写将字符串转换为数字的函数,该函数能够
把只包含数字的字符串转换为数字,把其余的转化为“NaN”(“Not a Number”,不是一个数字), 表示参数值无法转换为数字,该值类似于其他编程语言中的none或null。
问题二,数据集中有些值缺失,缺失的值用“?”表示。幸运的是,问号不会被转换为浮点型数据,因此,我们也可以把它们转换为“NaN”。后续章节会讲到其他处理缺失值的方法。
转换函数声明如下:
def convert_number(x):

先试着把字符串转换为数字,如果失败就要处理异常,所以我们这里使用try/except结构,捕获ValueError异常(字符串无法转换为数字时,抛出异常)。
try:
return float(x)
except ValueError

在函数的最后,如果转换失败,返回numpy库中的“NaN”类型。
return np.nan

我们来创建一个字典存储所有特征及其转换结果,我们想把所有的特征值转换为浮点型。
converters = defaultdict(convert_number)

我们还想把最后一列(编号为#1558)的值转化为0或1,该列表示每条数据的类别。在Adult数据集中,我们专门创建了一列表示类别。对于当前实验,导入数据时,顺便把类别这一列各个类别值由字符串转换为数值。
converters[1558] = lambda x: 1 if x.strip() == "ad." else 0

可以用read_csv加载数据集了,在参数中指定我们刚创建的转换函数。
ads = pd.read_csv(data_filename, header=None, converters=converters)

数据集很大,有1559列,2000多条数据。先来看下前五条数据,在笔记本的新格子中输入并运行ads[:5]。

数据集所描述的是网上的图像,目标是确定图像是不是广告。

从数据集表头中无法获知每列数据的含义。你下载的另外两个文件ad.DOCUMENTATION和 ad.names有更多信息。前三 个特征分别指图像的高度、宽度和宽高比。最后一列是数据的类别,
1表示是广告,0表示不是广告。
其余特征取值为1的话,表示图像的URL、alt属性值或图像标题中包含某个特定的可以帮助判断是不是广告的词语,比如像sponsor(赞助商)。很多特征重合度很高,因为它们组合在一起恰好是其他特征。因此,数据集包含大量冗余信息。
将数据集加载到pandas之后,我们再来抽取用于分类算法的x矩阵和y数组,x矩阵为数据框 除去最后一列的所有列,y数组包含数据框的最后一列,也就是列编号为#1558的列。代码如下。

X = ads.drop(1558, axis=1).values
y = ads[1558]

5 主成分分析

有些数据集,特征之间联系紧密。例如,只有1个档的微型单座赛车,速度和油耗两者关系就极为密切。特征间的相关性信息在某些场合会很有用,但是数据挖掘算法通常不需要这些冗余信息。
Advertisements数据集中有些特征相关性特别大,因为很多关键词在图像的alt属性值及图像的标题中都会使用。
主成分分析算法(Principal Component Analysis,PCA)的目的是找到能用较少信息描述数据集的特征组合。它意在发现彼此之间没有相关性、能够描述数据集的特征,确切说这些特征的跟整体方差没有多大差距,这样的特征也被称为主成分。这也就意味着,借助这种方法,就能通过更少的特征捕获到数据集的大部分信息。
PCA跟其他转换器用法类似。它只有主成分数量这一个参数。它默认会返回数据集中的所有特征。然而,PCA会对返回结果根据方差大小进行排序,返回的第一个特征方差最大,第二个特征方差稍小,以此类推。因此,前几个特征往往就能够解释数据集的大部分信息。请见代码:
from sklearn.decomposition import PCA
pca = PCA(n_components=5)
Xd = pca.fit_transform(X)

返回的结果Xd矩阵只有五个特征,但是不容小觑,我们看一下每个特征的方差。

np.set_printoptions(precision=3, suppress=True)
pca.explained_variance_ratio_
结果array([ 0.854, 0.145, 0.001, 0. , 0. ])表明第一个特征的方差对数据集总
体方差的贡献率为85.4%。第二个为14.5%。第四个特征方差贡献率不可能高于1‰,后面1553个
特征则更少。
用PCA算法处理数据一个不好的地方在于,得到的主成分往往是其他几个特征的复杂组合,例如,上述第一个特征就是通过为原始数据集的1558个特征(虽然很多特征值为0)分别乘以不同权重得到的,前三个特征的权重依次为0.092、0.995和0.024。经过某种组合得到的特征,如果没有丰富的研究经验,理解起来很困难。
用PCA算法得到的数据创建模型,不仅能够近似地表示原始数据集,还能提升分类任务的正
确率。
clf = DecisionTreeClassifier(random_state=14)
scores_reduced = cross_val_score(clf, Xd, y, scoring='accuracy')

正确率为0.9356,比起只用所有的原始特征效果(稍)好。PCA算法不是说都能带来效果上的提升,但多数情况是有好处的。
PCA算法的另一个优点是,你可以把抽象难懂的数据集绘制成图形。例如,把PCA返回的前两个特征做成图形。
首先,告诉IPython在当前笔记本作图,导入pyplot。
matplotlib inline
from matplotlib import pyplot as plt

接着,获取数据集中类别的所有取值(只有两个:是广告和不是广告)。
classes = set(y)

指定在图形中用什么颜色表示这两个类别
colors = ['red', 'green']

用zip函数将这两个列表组合起来,同时遍历
for cur_class, color in zip(classes, colors):

为属于当前类别的所有个体创建遮罩层。mask = (y == cur_class).values
使用pyplot的scatter函数显示它们的位置。图中的x和y的值为前两个特征。
plt.scatter(Xd[mask,0], Xd[mask,1], marker='o', color=color, label=int(cur_class))
最后,在循环的外面,创建图示,显示图像,从中就能看到分属两个类别的个体。
plt.legend()
plt.show()

6 创建自己的转换器

随着数据集复杂程度的提高和类型的变化,你可能发现,现成的特征抽取转换器不能满足需求了。比如,第7章从图这种数据结构里抽取特征,就需要自己编写转换器。

转换器像极了转换函数。它接收一种形式的数据,输出另外一种形式。转换器可以用训练集训练,训练得到的参数可以用来转换测试数据集。

转换器的API(应用编程接口)很简单。它接收一种特定格式的数据,输出一种格式的数据(可能与输入数据的格式相同,也可能不同)。别的就没有什么特殊要求了。

7 转换器 API

转换器有两个关键函数。

  • fit():接收训练数据,设置内部参数。
  • transform():转换过程。接收训练数据集或相同格式的新数据集。
fit()和transform()函数的输入应该为同一种数据类型,但是transform()可以返回不同的数据类型。
我们现在就通过实现一个简单的转换器,来说明下API的使用。转换器接收numpy数组作为输入,根据均值将其离散化。任何高于均值的特征值(训练集中)替换为1,小于或等于均值的替换为0。
我们曾经用pandas对Adult数据集做过类似的转换:用Hours-per-week特征创建了LongHours特征,后者表示每周工作时长是否在40小时以上。即将编写的转换器有两个不同点。第一,接口与scikit-learn接口一致,便于在流水线中使用。第二,使用均值而不是固定的值(在LongHours例子中使用40作为将特征二值化的阈值)。

8 实现细节

打开分析Adult数据集时用到的笔记本文件。点击Cell 菜单,选择Run All,运行所有单元格。

首先,导入用来设置API 的TransformerMixin类。Python语言(相对于Java之类的语言) 没有严格的接口规范,使用类似mixin这样的类,scikit-learn就会把我们封装的类当作是转 换器。我们还需要导入用来判断输入类型是否合法的函数,稍后就会用到。

导入所需模块。
from sklearn.base import TransformerMixin
from sklearn.utils import as_float_array
接着,创建继承自mixin的类。
class MeanDiscrete(TransformerMixin):

我们需要定义接口规范与API一致的fit和transform函数。在fit函数中,计算出数据集的均值,用内部变量保存该值。函数声明如下:
def fit(self, X):

fit函数中,要确保X数据集可以处理,我们用到了as_float_array函数(尝试对X进行转换,例如X是浮点数列表时就可以对其进行转换)。
X = as_float_array(X)

计算数组的均值,用内部变量保存该值。如果X是多变量数组,数组self.mean保存的是每个特征的均值。
self.mean = X.mean(axis=0)

fit函数需要返回它本身

return self

确保在转换器中能够进行链式调用(例如调用ransformer.fit(X).transform(X))。函数返回语句如下:

接着,新建transform函数,参数类型与fit函数相同,检查下输入是否合法。
def transform(self, X):
X = as_float_array(X)

输入必须是numpy数组(或等价的数据结构),除此之外,还需要检查输入的数据列数是否一致。X中的特征数应该与训练集中的特征数一致。
assert X.shape[1] == self.mean.shape[0]

函数最后返回X中大于均值的数据。
return X > self.mean

初始化一个实例,用它来对X数组进行转换。

mean_discrete = MeanDiscrete()
X_mean = mean_discrete.fit_transform(X)

9 单元测试

创建完函数和类后,最好是做下单元测试。单元测试,顾名思义,就是对代码中完成一个功
能的代码单元进行测试。在这里,测试下转换器的功能是否符合预期。
充分的测试应当在独立于现有代码的前提下进行。为保证测试的有效性,最好使用另一种编程语言或方法来进行相同的计算。我们这里使用Excel创建数据集,再计算每一列的均值,接下来跟程序计算结果相比较。
单元测试应该短小、运行速度快。因此,我们选用少量数据进行测试。本例用于测试的数据集保存在前面创建的Xt变量中,在测试中会重新创建这个数据集。该数据集两个特征的均值分别
为13.5和15.5。
为了创建单元测试,我们从numpy的testing模块中导入assert_array_equal函数,它用来检测两个数组是否相等。

from numpy.testing import assert_array_equal
接下来,创建测试函数,注意文件名以“test_”开头,这种命名方法便于工具自动查找并运行测试。代码如下:
def test_meandiscrete():
X_test = np.array([[ 0, 2],
[ 3, 5],
[ 6, 8],
[ 9, 11],
[12, 14],
[15, 17],
[18, 20],
[21, 23],
[24, 26],
[27, 29]])
创建转换函数实例,用测试数据进行训练。
mean_discrete = MeanDiscrete()
mean_discrete.fit(X_test)

通过与在Excel中得到的结果比较,检查内部均值参数是否正确设置。
assert_array_equal(mean_discrete.mean, np.array([13.5, 15.5]))

运行转换函数,创建被转换过后的数据集。准备好测试数据(在Excel中创建的)。
X_transformed = mean_discrete.transform(X_test)
X_expected = np.array([[ 0, 0],
[ 0, 0],
[ 0, 0],
[ 0, 0],
[ 0, 0],
[ 1, 1],
[ 1, 1],
[ 1, 1],
[ 1, 1],
[ 1, 1]])
最后,测试转换器返回结果是否与期望结果一致。
assert_array_equal(mean_discrete.mean, np.array([13.5, 15.5]))

调用函数,开始测试!
test_meandiscrete()

如果没弄错,上述代码应该没有问题!可以改动测试数据,用几个错误值,看看是否测试失败。要让测试结果通过,记得再改回正确的值。
如果要进行多个测试,最好使用nose测试框架来运行测试。

10 组装起来

现在,我们有了一个经过测试的转换器,拿出来用用吧。用上我们之前学到的知识,创建一个流水线,第一步使用MeanDiscrete转换器,第二步使用决策树分类器,然后进行交叉检验,输出结果。来看下代码:
from sklearn.pipeline import Pipeline
pipeline = Pipeline([('mean_discrete', MeanDiscrete()), ('classifier', DecisionTreeClassifier(random_state=14))]) scores_mean_discrete = cross_val_score(pipeline, X, y, scoring='accuracy') print("Mean Discrete performance:
{0:.3f}".format(scores_mean_discrete.mean()))
正确率为0.803,结果没有之前高,对于简单的二值特征而言还算不错。

小结

本文重点为特征和转换器,以及如何将其用到数据挖掘流水线中。我们探讨了什么是好特征,以及如何用算法从数据集中选取最佳特征。然而,创建好的特征比起科学更像是一门艺术,通常需要领域相关的知识和经验。

【实用算法教学】——教你如何用转换器抽取电影,音乐甚至是比赛等特征相关推荐

  1. 【实用算法教学】——Apriori算法,教你使用亲和性分析方法推荐电影

    本文学习如何用亲和性分析方法找出在什么情况下两个对象经常一起出现.通俗来讲,这也 叫"购物篮分析",因为曾有人用它找出哪些商品经常一起出售. 前一篇文章关注的对象为球队,并用特征描 ...

  2. 【实用算法教学】——教你使用决策树算法预测NBA获胜球队

    本文介绍一种分类算法--决策树,用它预测NBA篮球赛的获胜球队.比起其他算法,决 策树有很多优点,其中最主要的一个优点是决策过程是机器和人都能看懂的,我们使用机器学习 到的模型就能完成预测任务.正如我 ...

  3. 手机计算机dj音乐,教你如何用手机制作电子音乐

    随着手机性能的提高,在手机上制作电子音乐并不是什么难事,接下来就为大家介绍一些可以在手机上使用的合成器应用. Cakewalk Z3TA + Cakewalk Z3TA +与它的Mac和Windows ...

  4. 用visio画用例图小人_教你如何用 Python 打飞机 ?

    前言:python 除了生孩子 ,啥都会 .包括打飞机 !今天就来教你如何用 python 打飞机 ! 简述 相信你是一个单纯的孩子说的打飞机是指啥意思 ,对吧 ?嗯 ,没毛病 .就是 pygame ...

  5. 怎么用python制作简单的程序-神级程序员教你如何用python制作一个牛逼的外挂!...

    玩过电脑游戏的同学对于外挂肯定不陌生,但是你在用外挂的时候有没有想过如何做一个外挂呢?(当然用外挂不是那么道义哈,呵呵),那我们就来看一下如何用python来制作一个外挂.... 我打开了4399小游 ...

  6. 不到 200 行代码,教你如何用 Keras 搭建生成对抗网络(GAN)

     不到 200 行代码,教你如何用 Keras 搭建生成对抗网络(GAN) 生成对抗网络(Generative Adversarial Networks,GAN)最早由 Ian Goodfello ...

  7. 贪吃蛇博弈算法python_算法应用实践:如何用Python写一个贪吃蛇AI

    原标题:算法应用实践:如何用Python写一个贪吃蛇AI 前言 这两天在网上看到一张让人涨姿势的图片,图片中展示的是贪吃蛇游戏, 估计大部分人都玩过.但如果仅仅是贪吃蛇游戏,那么它就没有什么让人涨姿势 ...

  8. 零元学Expression Design 4 - Chapter 5 教你如何用自制笔刷在5分钟内做出设计感效果...

    零元学Expression Design 4 - Chapter 5 教你如何用自制笔刷在5分钟内做出设计感效果 原文:零元学Expression Design 4 - Chapter 5 教你如何用 ...

  9. 教你如何用VB做控件*.ocx

    教你如何用VB做控件*.ocx 一个控件有很多事件如:Click.MouseDown.MouseUp.MouseMove等.要触发这些事件都需要你加入代码.在控件的声明处加入Public Event ...

  10. python换脸教程_教你如何用200行Python代码“换脸”教程

    原标题:教你如何用200行Python代码"换脸"教程 本文将介绍如何编写一个只有200行的Python脚本,为两张肖像照上人物的"换脸". 这个过程可分为四步 ...

最新文章

  1. 作业03:格式化输出
  2. android从放弃到精通 第九天 勿忘初心
  3. SSL服务器配置评级指南
  4. 使用Docker-容器命令案例1
  5. b+树阶怎么确定_B站公布年度弹幕,这个排名我不太服气
  6. Js中Symbol对象
  7. 2017/4/12 afternoon
  8. 如何不用MDI方式在Form1上显示Form2
  9. golang,break跳出循环的例子以及随机数生成
  10. Todoist Chrome:待办事项列表及任务管理
  11. Html常用正则表达式
  12. oracle查询备份存储路径,Oracle数据库备份和恢复(导出,导入)
  13. 副业做淘宝可以么?淘宝可以当做副业来做吗?
  14. Linux上用于Json数据处理并导入Mysql的几个有用Python脚本
  15. RecyclerView 删除元素后,点击报 IndexOutOfBoundsException 解决方法
  16. 前端SVG实现各式图片和动画
  17. 深度学习模型轻量化(上)
  18. IST改进算法之Two-Step Iterative Shrinkage/Thresholding(TwIST)
  19. pycharm报错warning: iCCP: known incorrect sRGB profile
  20. OpenNI 2.0

热门文章

  1. 如何免费自动将邮件发送到多个邮件地址 - 自动邮件发送器使用教程
  2. 电子邮件服务器的ip地址_EDM电子邮件营销,你真的了解么?
  3. 美国华盛顿州立大学计算机排名,2019上海软科世界一流学科排名计算机科学与工程专业排名华盛顿州立大学排名第301-400...
  4. “我是技术总监,我确实答不出那么多技术细节”
  5. html和js制作个人所得税表格,H5编写个税计算器(JS代码编写).doc
  6. input: kMAX dimensions in profile 0 are [2,3,128,128] but input has static dimensions [1,3,128,128]
  7. “New”一个完美对象,再来好好面向对象
  8. 【图形学】计算网格中两点连成的直线所经过的格子
  9. 2021-03-23美团面试
  10. mumu模拟器切换android,MuMu模拟器“键位设置”功能详解!如有问题请联系在线客服~...