《数据分析实战-托马兹.卓巴斯》读书笔记第5章-降维技巧

 

python学习笔记-目录索引html

 

第5章 展现了不少降维的技巧,从最知名的主成分分析出发,经由其核版本与随机化版本,一直讲到线性判别分析。python

本章会介绍一些下降数据维度的技术。将学习如下主题:
·建立三维散点图,显示主成分
·使用核PCA降维
·用主成分分析找到关键因素
·使用随机PCA在数据中寻找主成分
·使用线性判别分析提取有用的维度
·用kNN分类模型给电话分类时使用多种降维技巧

5.1导论

现在数据的冗余很恼人:数据集的增加不只体如今更多的观测值上,还体如今更丰富的元数据上。
本章将展示一系列技巧,帮助你从数据中提取最重要的特征,并用于建模。建模时使用主要成分替代原始特征的缺点是,解释清楚模型的系数——即理解预测或分类的驱动因素——几乎是不可能的。
若是你的目标是作一个高准确度的预测者,或者项目的焦点不是理解驱动因素,那么本章接下来要介绍的方法多是你感兴趣的。web

5.2建立三维散点图,显示主成分

主成分不过是能够用来转换数据的多维向量。经过找到数据的主要维度,能够发现数据的另外一番风景。
准备:须要带MPL工具包的Matplotlib。若是你安装的是Anaconda发行版Python,这些应该已经装好了。

使用.plot_components(...)方法绘制三维数据(helper.py文件):算法

 1   def plot_components(z, y, color_marker, **f_params):
 2     '''
 3         Produce and save the chart presenting 3 principal
 4         components
 5     '''
 6     # import necessary modules
 7     import matplotlib.pyplot as plt
 8     from mpl_toolkits.mplot3d import Axes3D
 9 
10     # convert the dependent into a Numpy array
11     # this is done so z and y are in the same format
12     y_np = y
13 
14     # do it only, however, if y is not NumPy array
15     if type(y_np) != np.array:
16         y_np = np.array(y_np)
17 
18     # create a figure
19     fig = plt.figure()
20     ax = fig.add_subplot(111, projection='3d')
21 
22     # plot the dots
23     for i, j in enumerate(np.unique(y_np)):
24         ax.scatter(
25             z[y_np == j, 0],
26             z[y_np == j, 1],
27             z[y_np == j, 2],
28             c=color_marker[i][0],
29             marker=color_marker[i][1])
30 
31     ax.set_xlabel('First component')
32     ax.set_ylabel('Second component')
33     ax.set_zlabel('Third component')
34 
35     # save the figure
36     plt.savefig(**f_params)

原理:plot_components(...)方法接受多个参数。z参数是转换到三维空间的数据集(即,只有最上面三个主成分)。y参数是类别的向量:咱们用的是和前两章相同的数据集(限定了特征数),因此这就是表明着银行职员这通电话是否带来了消费者的信用卡业务。color_marker是一系列“(颜色,标记)”形式构成的元组,每一个元素的第一个位置是颜色,第二个位置是标记,例如,('red','o');在这个例子中,用红色画小圈圈。
最后一个参数在本书中只是简单涉及:**f_params。**f_params参数(或者更常见的**kwargs)能让你向函数传入可变数量的关键词参数。例如,假设有个方法:
邀月工做室

像下面这样调用方法,会有不一样的输出:
邀月工做室
也能够经过*args参数传入可变长度的非关键词参数。
在plot_components(...)方法中,先导入须要的模块:pyplot和mplot3d中的Axes3D;后者容许咱们生成三维图形。而后,若是y向量不是NumPy数组,能够将其转变成NumPy数组。
接下来,开始绘图。第一步,建立一个图,并调用add_subplot(...)。111意味着你将图线加到第一行(第一个1)和第一列(第二个1),而且它是图表第一个也是惟一一个图线(第三个1)。
传入211会往第一行第一列加入一个子图,而212会往一样的图表上加第二个子图。换句话说,第一个数字是说图表中有多少行,第二个数字是说有多少列,最后一个数字是放子图的单元;单元是从左往右从上往下数的。
projection参数告诉add_subplot(...)咱们要传三维的数据。
for循环遍历y中的类,给图线加上散点。对每一个类,选取三个主成分;你应该已经熟悉z[y_np==j,0]这种写法,只选取z当中其一样索引在y_np中对应的元素与类标签j相等的元素,而且只选取第一列(数组中索引为0)。
散点的c参数定义了用来画这些点的颜色,marker决定了用什么样的标记。
查阅http://matplotlib.org/api/markers_api.html,看看你能用在图表上的各类标记。
顾名思义,set_{x,y,z}label(...)方法会给图表的坐标轴加上描述。
最后,使用Matplotlib的.savefig(...)方法绘图。至少要传入文件名参数。因为要保存为PNG格式,所以也要指定dpi(Dots Per Inch)参数为300,因此若是你想的话,能够输出这个图表;若是用于Web展现,100 dpi就足够了(有人用的是72 dpi)。
结果相似这样:
邀月工做室

5.3使用核PCA降维

PCA(Principal Components Analysis,主成分分析)将相关联的一组变量转换为主成分:线性不相关的(正交的)变量。PCA能够产生和变量同样多的主成分,不过一般来说仍是会下降数据的维度。第一个主成分选用的是数据中对方差贡献最大的特征,接下来的主成分是以对方差贡献降序排列的,而且保持主成分之间是正交的(无关的)。
准备:需安装好pandas、NumPy和MLPY。绘图时须要MPL工具包中的Matplotlib。

相似以前的技巧,咱们将构建模型的程序包了一层,这样能够用timeit装饰器给它计时(reduce_pca.py文件):编程

 1 def reduce_PCA(x):
 2     '''
 3         Reduce the dimensions using Principal Component
 4         Analysis
 5         使用主成分分析降维
 6     '''
 7     # create the PCA object建立PCA对象
 8     pca = ml.PCA(whiten=True)
 9 
10     # learn the principal components from all the features
11     #从全部的特征中学习主成分
12     pca.learn(x)
13 
14     # return the object返回3个主成分
15     return pca

原理:首先,读入数据。和其余技巧同样,使用pandas的DataFrame装载数据:api

# the file name of the dataset 数据集结文件名
r_filename = '../../Data/Chapter05/bank_contacts.csv'

# read the data
csv_read = pd.read_csv(r_filename)

而后从csv_read对象中提取出自变量和因变量,在PCA中只使用自变量:数组

# split into independent and dependent features
x = csv_read[csv_read.columns[:-1]]
y = csv_read[csv_read.columns[-1]]

最后,调用reduce_PCA(...)方法。只须要一个参数x:app

# reduce the dimensionality
z = reduce_PCA(x)

在这个方法中,首先建立了PCA(...)对象。whiten参数设为True,使得PCA(...)对象缩放数据,使得每一个特征的标准差都是1。
前一章介绍了漂洗。参考本书4.3节。
也能够指定MLPY中实现的PCA(...)算法要使用那种方法:'svd'仍是'cov'。
若是你倾向数学上的解释,好奇PCA(以及它与奇异值分解的关系),推荐你阅读这篇论文:https://www.cs.princeton.edu/picasso/mats/PCA-Tutorial-Intuition_jp.pdf
而后,让pca对象从x数据中学习(.learn(...))主成分。在个人机器上,这真的很快,(针对这个数据集)一般花0.26s左右:
注意,数据集越大(更多的行,更多的列),这个过程越慢。若是真的太慢了,MLPY提供了另外一个寻找主成分的方法:FastPCA(...)。参考:http://mlpy.sourceforge.net/docs/3.5/dim_red.html#fast-principal-component-analysis-pcafast。本章后续会介绍随机化PCA方法的。
最后,返回pca,把它保存在z对象中。
如今是画出成分的时候了:dom

# plot and save the chart
# to vary the colors and markers for the points
#使用不一样的颜色和标记绘图并保存
color_marker = [('r','o'),('g','.')]

file_save_params = {
    'filename': '../../Data/Chapter05/charts/pca_3d.png',
    'dpi': 300
} 
/* The method reduce_PCA took 0.75 sec to run.
Traceback (most recent call last):
  File "D:\Java2018\practicalDataAnalysis\Codes\Chapter05\reduce_pca.py", line 52, in <module>
    color_marker, **file_save_params)
  File "D:\Java2018\practicalDataAnalysis\helper.py", line 265, in plot_components
    plt.savefig(**f_params)
  File "D:\tools\Python37\lib\site-packages\matplotlib\pyplot.py", line 722, in savefig
    res = fig.savefig(*args, **kwargs)
TypeError: savefig() missing 1 required positional argument: 'fname'
 */

Tips:注意将file_save_params中的'filename'改成'fname',便可。ide


color_marker列表指定了咱们用的标记:若是电话没带来信用卡的办理,咱们用小的红色圈圈表示;若是带来了,就用绿色的点表示。file_save_params方法为.savefig(...)方法保存了全部的关键词参数,.savefig(...)方法会在.plot_components(...)方法中调用。
最后,调用.plot_components(...)方法。.transform(...)方法返回头三个主成分的列表(k=3参数)。
Scikit也有找到数据主成分的方法:

 1 import sklearn.decomposition as dc
 2 
 3 /* @hlp.timeit
 4 def reduce_PCA(x):
 5     '''
 6         Reduce the dimensions using Principal Component
 7         Analysis
 8     '''
 9     # create the PCA object
10     pca = dc.PCA(n_components=3, whiten=True)
11 
12     # learn the principal components from all the features
13     return pca.fit(x)
14  */

和一般同样,建立pca对象,应用数据,即学习主成分。和MLPY不一样,Scikit的.PCA(...)方法容许你指定想学习出的主成分个数;不指定这个参数就找出全部可能的主成分(最大值是数据集中特征的个数)。
Scikit的.PCA(...)方法与MLPY的另外一点不一样在于,它能够知道每一个特定的主成分贡献了多少方差。这样展现出来:

# how much variance each component explains?每一个成分贡献了多少方差?
print(z.explained_variance_ratio_)

# and total variance accounted for 整体方差
print(np.sum(z.explained_variance_ratio_))

找出来的成分与MLPY的.PCA(...)方法找出来的略有不一样:

/*
The method reduce_PCA took 0.29 sec to run.
[0.12686333 0.07761521 0.07516711]
0.2796456484006652
 */


参考:查看PCA的可视化:http://setosa.io/ev/principal-component-analysis/

5.4用主成分分析找到关键因素

与前面介绍的PCA方法不一样,核PCA使用用户定义的核函数,将n维的数据映射到m维的特征空间。PCA映射时用了线性函数,至关于一个使用了线性核的核PCA。
当数据不是线性可分时,核PCA就大有可为了,它可使用多种非线性核将数据映射到更高的维度。
准备:需安装好pandas和Scikit。
一样,咱们给模型包了一层方法,以便追踪模型到收敛时花了多久。使用核PCA,你应该内心有数,估算要更久(reduce_kernelPCA.py文件):

 1 @hlp.timeit
 2 def reduce_KernelPCA(x, **kwd_params):
 3     '''
 4         Reduce the dimensions using Principal Component
 5         Analysis with different kernels
 6     '''
 7     # create the PCA object
 8     pca = dc.KernelPCA(**kwd_params)
 9 
10     # learn the principal components from all the features
11     return pca.fit(x)

原理:和之前同样,先读入数据集,把它拆成自变量x和因变量y。使用关键词**kwd_params参数,能够轻松测试多种核模型;本例中,使用以前介绍过的RBF函数:

# reduce the dimensionality
kwd_params = {
        'kernel': 'rbf',
        'gamma': 0.33,
        'n_components': 3,
        'max_iter': 1,
        'tol': 0.9,
        'eigen_solver': 'arpack'
    }

kernel参数容许咱们选用多种核:可使用linear、poly、rbf、sigmoid和cosine,或者预先计算你本身的核。n_components参数控制着找到多少个主成分。
KernelPCA(...)方法循环寻找数据中的主成分。使用arpack找到协方差矩阵的特征向量。
要学习arpack的更多内容,参考http://www.caam.rice.edu/software/ARPACK或http://docs.scipy.org/doc/scipy/reference/tutorial/arpack.html
max_iter参数控制的是arpack中止估算前的最大循环次数。这是为了不估算陷在局部极值点。
tol参数指定了容忍度;也控制着循环。若是循环之间的提高低于这个值,循环就会终止,认为找到了特征值。
gamma参数是poly核和rbf核的系数,无疑是最难选择的参数。要具象化gamma参数对RBF核的影响,参考这个数据集(展现的是XOR函数):
   邀月工做室
在这个二维的例子中,要找到数据的最佳分割。咱们用的是RBF核PCA,这里展现的是不一样gamma参数下获得的不一样结果。在不一样gamma参数的图示后面,都跟着第一个主成分的图,做为视觉上的辅助,帮咱们检验数据是否分割:
邀月工做室


从图中能够看出,gamma参数影响了数据集转换后的形状和密度。另外,也能够看出XOR函数不易分割,即使使用了RBF函数也是这样。最接近分割的gamma参数的值是5。
使用RBF核的reduce_KernelPCA(...)方法在做者的机器上通常花10min左右进行估算。

邀月工做室

 

邀月机器内存16G过小,没法计算!!!!!!,把原数据4万行缩减为2万行,也须要多60倍的时间(18.38 sec VS 0.29秒 )

/*
如下方式试过无效:
Run configuration:
-Xms1024m -Xmx8192m -XX:MaxNewSize=10000m -XX:MaxPermSize=10000m
-Xms512m -Xmx8192m -XX:PermSize=512M -XX:MaxPermSize=8192m -XX:ReservedCodeCacheSize=96m
*/

前面提过,可使用其余函数做为核。本技巧中,以XOR函数数据集为例,展现如何用多种核函数估算核PCA,以避免等得像前一个例子那么久(reduce_kernelPCA_alternative.py文件)。
首先,准备数据集:

 1 def produce_XOR(sampleSize):
 2     import sklearn.datasets as dt
 3 
 4     # centers of the blobs
 5     centers = [(0,0),(3,0),(3,3),(0,3)]
 6 
 7     # create the sample
 8     x, y = dt.make_blobs(n_samples=sampleSize, n_features=2,
 9         cluster_std=0.8, centers=centers, shuffle=False
10     )
11 
12     # and make it XOR like
13     y[y == 2] = 0
14     y[y == 3] = 1
15 
16     return x, y
 1 def plot_components_2d(z, y, color_marker, **f_params):
 2     '''
 3         Produce and save the chart presenting 3 principal
 4         components
 5     '''
 6     # import necessary modules
 7     import matplotlib.pyplot as plt
 8     from mpl_toolkits.mplot3d import Axes3D
 9 
10     # convert the dependent into a Numpy array
11     # this is done so z and y are in the same format
12     y_np = y
13 
14     # do it only, however, if y is not NumPy array
15     if type(y_np) != np.array:
16         y_np = np.array(y_np)
17 
18     # create a figure
19     fig = plt.figure()
20     ax = fig.add_subplot(111)
21 
22     # plot the dots
23     for i in np.unique(y_np):
24         ax.scatter(
25             z[y_np == i, 0],
26             z[y_np == i, 1],
27             c=color_marker[i][0],
28             marker=color_marker[i][1])
29 
30     ax.set_xlabel('First component')
31     ax.set_ylabel('Second component')
32 
33     # save the figure
34     plt.savefig(**f_params)

 

首先,导入一个辅助模块sklearn.datasets。要在正方形的四角建立四个数据块,因此在centers列表存了四个角的坐标。使用.make_blobs(...)方法建立数据集:5000个数据点(n_samples参数),两个特征(咱们要的是二维的数据集,n_features)。数据块密度是均匀的;这由cluster_std参数控制。用centers参数控制数据块的中心点。最后一个参数shuffle控制着数据是否要打乱,这样会带来更高的变异性。
y对象保存数据的标签列表。因为传入了四个中心,所以会获得四个不一样的标签,XOR数据集应该只有两个标签:对角线上的数据块,其标签相同。所以,咱们会改变两个数据块的标签。
而后咱们准备全部想测试的核的列表:

 1 # reduce the dimensionality
 2 kwd_params = [{ 'kernel': 'linear',
 3         'n_components': 2,'max_iter': 3,
 4         'tol': 1.0, 'eigen_solver': 'arpack'
 5     }, { 'kernel': 'poly',
 6         'degree': 2,'n_components': 2,'max_iter': 3,
 7         'tol': 1.0, 'eigen_solver': 'arpack'
 8     }, { 'kernel': 'sigmoid',
 9         'n_components': 2,'max_iter': 3,
10         'tol': 1.0, 'eigen_solver': 'arpack'
11     }, { 'kernel': 'cosine',
12         'degree': 2,'n_components': 2,'max_iter': 3,
13         'tol': 1.0, 'eigen_solver': 'arpack'}
14 ] 

遍历全部列表,估算模型并保存图表:

color_marker = [('r','^'),('g','o')]

for params in kwd_params:
    z = reduce_KernelPCA(x, **params)

    # plot and save the chart
    # vary the colors and markers for the points
    file_save_params = {
        'filename':
            '../../Data/Chapter05/charts/kernel_pca_3d_{0}.png'\
            .format(params['kernel']),
        'dpi': 300
    }

    hlp.plot_components_2d(z.transform(x), y, color_marker,
        **file_save_params)

       
线性核等价于寻常的PCA模型。用在XOR数据集上,因为它不是线性可分的,因此并不指望它的表现能有多好。多项式和s函数核分割的结果某种程度上要更好。虽然从余弦函数的结果中能够辨别出模式,但仍是会在绿点中包括红点。在余弦(及其余方法)的例子中,没法仅仅用一个维度区分数据。全部的图表在Data/Chapter5/charts/文件夹下。

/*
The method reduce_KernelPCA took 0.69 sec to run.
The method reduce_KernelPCA took 0.74 sec to run.
The method reduce_KernelPCA took 1.19 sec to run.
The method reduce_KernelPCA took 0.67 sec to run.
 */

 

邀月工做室
参考:
Sebastian Raschka给出了一些核PCA的好例子:http://sebastianraschka.com/Articles/2014_kernel_pca.html

5.5使用随机PCA在数据中寻找主成分
PCA(和核PCA)都用低阶矩阵近似来估算主成分。低阶矩阵近似最小化损失函数,这个损失函数表明矩阵及其近似之间的偏差。
这种作法对大规模数据集来讲会很耗时。而随机化输入数据集中奇异值的分解,能够显著提高估算的速度。
准备:需安装好NumPy、Scikit和Matplotlib。
和之前同样,建立一个包装方法来估算模型(reduce_randomizedPCA.py文件):

 1 def reduce_randomizedPCA(x):
 2     '''
 3         Reduce the dimensions using Randomized PCA algorithm
 4     '''
 5     # create the CCA object
 6     randomPCA = dc.RandomizedPCA(n_components=2, whiten=True,
 7         copy=False)
 8 
 9     # learn the principal components from all the features
10     #从全部特征中学习主成分
11     return randomPCA.fit(x)

原理:这个方法的内部看上去几乎和以前的模型包装程序如出一辙。首先,咱们建立了模型对象,应用数据后返回。Scikit在其全部的分解方法中,保持了统一的API(Application Programming Interface,应用编程接口),因此咱们换模型不须要改太多代码。
在本技巧中,咱们将聚焦于,当数据的特征数和样本容量都增加时,随机PCA与PCA相比,速度上能有多大的提高。这里,咱们先定义参数:

# prepare the sample
sampleSizes = np.arange(1000, 50000, 3000)
featureSpace = np.arange(100, 1000, 100)

NumPy的.arange(<start>,<end>,<step>)方法建立了一个范围,自<start>始,以<end>(或左右)终,步长为<step>。
在helper.py文件中,咱们加一个相似于.timeit装饰器的方法:

def timeExecution(method, *args, **kwargs):
    '''
        A method to measure time of execution of a method
    '''
    start = time.time()
    result = method(*args, **kwargs)
    end = time.time()

    return result, end-start 

timeExecution(...)方法的第一个参数是方法名,将传入的这个方法的参数做为后续参数(*args参数和变长参数**kwargs)。方法内部和timeit装饰器的模式相似,可是不只返回方法的结果,也返回执行的时间。
咱们将使用timeExecution(...)方法衡量PCA和随机PCA执行的时间,后面还会画出图;Z是记载执行时间的字典:

# object to hold the results
Z = {'randomPCA': [], 'PCA': []}

脚本的主循环以下:

 1   for features in featureSpace:
 2     inner_z_randomPCA = []
 3     inner_z_PCA = []
 4 
 5     for sampleSize in sampleSizes:
 6         # get the sample
 7         x, y = hlp.produce_sample(
 8             sampleSize=sampleSize, features=features)
 9 
10         print(
11             'Processing: sample size {0} and {1} features'\
12             .format(sampleSize, features))
13 
14         # reduce the dimensionality
15         z_r, time_r     = hlp.timeExecution(
16             reduce_randomizedPCA, x)
17         z_pca, time_pca = hlp.timeExecution(
18             reduce_PCA, x)
19 
20         inner_z_randomPCA.append(time_r)
21         inner_z_PCA.append(time_pca)
22 
23     Z['randomPCA'].append(inner_z_randomPCA)
24     Z['PCA'].append(inner_z_PCA) 

咱们比较两个维度的执行时间,要遍历两个循环。这样作最终是为了找出哪一个对执行时间的影响更大:是特征数仍是样本容量。inner_z_...列表记录了执行时间。
在第二个循环的第一步,咱们使用produce_sample(...)方法。这是咱们要加到helper.py文件的另外一个方法:

def produce_sample(sampleSize, features):
    import sklearn.datasets as dt

    # create the sample
    x, y = dt.make_sparse_uncorrelated(
        n_samples=sampleSize, n_features=features)

    return x, y

这个方法生成一个样本,容量sampleSize和特征数由Scikit的make_sparse_uncorrelated方法预先指定了。
有了建立的这个样本,咱们如今能够为reduce_randomizedPCA(...)和reduce_PCA(...)方法的执行计时了:后者是从本书5.3节的技巧中引入。
以后,咱们将时间附到记载执行时间的列表。循环以附加上列表的Z字典结束。
咱们如今画出执行时间:

 1 def saveSurfacePlot(X_in,Y_in,Z,**f_params):
 2     from mpl_toolkits.mplot3d import Axes3D
 3     import matplotlib.pyplot as plt
 4     import matplotlib as mt
 5 
 6     # adjust the font 调整字体
 7     font = {'size': 8}
 8     mt.rc('font', **font)
 9 
10     # create a mesh 建立风格
11     X, Y = np.meshgrid(X_in, Y_in)
12 
13     # create figure and add axes 建立图,加上坐标
14     fig = plt.figure()
15     ax = fig.gca(projection='3d')
16 
17     # plot the surface   绘制表面
18     surf = ax.plot_surface(X, Y, Z,
19         rstride=1, cstride=1,
20         cmap=mt.cm.seismic,
21         linewidth=0, antialiased=True)
22 
23     # set the limits on the z-axis z轴设置界限
24     ax.set_zlim(0, 7)
25 
26     # add labels to axes 给轴加上标签
27     ax.set_xlabel('Sample size')
28     ax.set_ylabel('Feature space')
29     ax.set_zlabel('Time to estimate (s)')
30 
31     # rotate the chart 旋转图表
32     ax.view_init(30, 130)
33 
34     # and save the figure
35     fig.savefig(**f_params)
36     
37     # filename params for the randomized PCA
38 f_params = {
39     'filename':
40         '../../Data/Chapter05/charts/time_r_pca_surf.png',
41     'dpi': 300
42 }
43 
44 # prepare and save the plot
45 saveSurfacePlot(sampleSizes, featureSpace,
46     Z['randomPCA'], **f_params)

saveSurfacePlot(...)方法先引入须要的模块。咱们减少字体,使图表更可读。
NumPy的meshgrid(...)方法建立n x m个度量X和Y,X_in的大小是m,Y_in的大小是n;X_in在X中重复n次(行),Y_in在Y中重复m次(列)。咱们遍历每一个矩阵的全部m x n个元素时,就覆盖了全部特征和样本容量的组合。
接下来,咱们建立一个图,加上三维坐标,画出表面。X、Y和Z是点的坐标。rstride和cstride参数说明,咱们要从x轴和y轴的每一个点,沿着表面分别绘制水平线和垂直线;这样就在表面铺好瓦砖。linewidth参数控制线的宽度,cmap参数定义了色彩图(或温度图)——z的值越高,图线越红。
你能够在这里找到全部可用的色彩图:http://matplotlib.org/examples/color/colormaps_reference.html。
antialiased参数在图表上建立更平滑的线。
咱们在z轴上设了限制,以便在比较两个不一样方法的图时,咱们能够当即看出执行时间的差异。加标签永远是为了帮助理解图表的意义。
你也许须要调整.set_zlim(...)参数,由于你机器上的执行时间可能会不一样。
最后,咱们旋转图表并保存。.view_init(...)方法将海拔设为30度,并在x-y平面顺时针旋转130度:
邀月工做室
PCA的图看上去和前一个相似,不过随机化版本的图应该相似这样:
邀月工做室

Tips:

/* Processing: sample size 49000 and 900 features
Traceback (most recent call last):
  File "D:\Java2018\practicalDataAnalysis\Codes\Chapter05\reduce_randomizedPCA.py", line 119, in <module>
    Z['PCA'], **f_params)
  File "D:\Java2018\practicalDataAnalysis\Codes\Chapter05\reduce_randomizedPCA.py", line 57, in saveSurfacePlot
    linewidth=0, antialiased=True)
  File "D:\tools\Python37\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 1614, in plot_surface
    if Z.ndim != 2:
AttributeError: 'list' object has no attribute 'ndim'
 */

解决方案:
跟踪打印Z,注意到
AttributeError: 'list' object has no attribute 'shape'

/* #===============================================================================
# print('************************************')
# print(Z['PCA'])
# print(np.array(Z['PCA']))
#===============================================================================

#===============================================================================
# [[0.00498652458190918, 0.018980026245117188, 0.037740230560302734], [0.0076868534088134766, 0.02914142608642578, 0.05567455291748047]]
# [[0.00498652 0.01898003 0.03774023]
#  [0.00768685 0.02914143 0.05567455]]
#=============================================================================== */
/*
#Z为List,必须转化为Array,注意List对象没有shape属性
    Z=np.array(Z) */

如你所见,当样本容量和特征空间增加时,PCA算法的执行时间增加得很是快;不过只有一个维度增加时不怎么受影响。一样的参数配置下,随机化版本的PCA算法,执行时间快10倍。不过要注意,若是数据集很小(特征数<100),随机化PCA方法可能更慢。

5.6使用线性判别分析提取有用的维度

如今咱们理解了降维的机制(和取舍),咱们用它来分类吧。
本技巧介绍LDA(Linear Discriminant Analysis,线性判别分析)。与本章以前介绍的方法不一样,LDA的目标是将因变量用不少其余特征的一个线性函数表示;在这个意义上,这和回归(下一章讨论)很像。在数据的最佳方差的快照(解释)中,对于如何为线性关系建模这一点,LDA和ANAVA方差分析及逻辑回归都有类似处。
咱们使用一个线性SVM分类器,来测试相较于原始数据集,降维后的效果。再一次,咱们要使用银行营销电话数据集。
准备:需装好pandas和MLPY。

使用下面的方法,经过LDA下降维度(reduce_LDA文件):

 1 @hlp.timeit
 2 def reduce_LDA(x, y):
 3     '''
 4         Reduce the dimensions using Linear Discriminant
 5         Analysis
 6     '''
 7     # create the PCA object
 8     lda = ml.LDA(method='fast')
 9 
10     # learn the principal components from all the features
11     lda.learn(x, y)
12 
13     return lda 

原理:和之前同样,程序是以读入数据集并拆成自变量集合x和因变量集合y开始的。因为本技巧中,咱们要看降维对分类的影响,因此咱们还要将初始的数据集拆成训练子集和测试子集:

1 # split the original data into training and testing
2 train_x_orig, train_y_orig, \
3 test_x_orig,  test_y_orig, \
4 labels_orig = hlp.split_data(
5     csv_read,
6     y = 'credit_application'
7 ) 

完成了这步,如今咱们能够下降数据集的维度:

# reduce the dimensionality
csv_read['reduced'] = reduce_LDA(x, y).transform(x)

这一步作了不止一件事:咱们一个个分析。
首先,咱们调用reduce_LDA(...)方法。这个方法有两个参数必传,x是自变量集合,y是因变量集合。这看上去不太合乎正统,毕竟以前全部的方法只要自变量。但LDA要找的是自变量和因变量之间的(模型)线性关系,它得知道目标(还记得第4章开始导论部分讨论的有监督训练否)。
MLPY提供了.LDA(...)估算器。这里指定了方法为fast,由于咱们的数据集有4万多记录。接着,咱们让模型学习(.learn(...)方法)学习数据中的联系,返回训练后的对象。
.transform(x)方法将自变量编码,而后存到原始数据集csv_read的reduced列。
如今是时候为分类器另建立一个训练集和测试集了:

1 # split the reduced data into training and testing
2 train_x_r, train_y_r, \
3 test_x_r,  test_y_r, \
4 labels_r = hlp.split_data(
5     csv_read,
6     y = 'credit_application',
7     x = ['reduced']
8 ) 

这里,咱们只选取降维后的列做为自变量。
咱们如今估算分类器:

1 # train the models
2 classifier_r    = fitLinearSVM((train_x_r, train_y_r))
3 classifier_orig = fitLinearSVM((train_x_orig, train_y_orig))

若是你还记得咱们在第3章中作过什么,这里就不会意外了。咱们重用了本书3.5节里介绍的fitLinearSVM(...)方法。

 1 @hlp.timeit
 2 def fitLinearSVM(data):
 3     '''
 4         Build the linear SVM classifier
 5     '''
 6     # create the classifier object
 7     svm = ml.LibSvm(svm_type='c_svc',
 8         kernel_type='linear', C=20.0)
 9 
10     # fit the data
11     svm.learn(data[0],data[1])
12 
13     # return the classifier
14     return svm


测试下咱们的方法:

1 # classify the unseen data 预测数据
2 predicted_r    = classifier_r.pred(test_x_r)
3 predicted_orig = classifier_orig.pred(test_x_orig)
4 
5 # print out the results 打印结果
6 hlp.printModelSummary(test_y_r, predicted_r)
7 hlp.printModelSummary(test_y_orig, predicted_orig)

Tips:

/*
 ValueError: Buffer dtype mismatch, expected 'int_t' but got 'long long' 
*/

解决方案:从非官方下载32位mlpy-3.5.0-cp37-cp37m-win32.whl文件,python官网下载32位安装文件python-3.7.5.exe

首先,咱们用估算的分类器预测类别,而后评估模型。咱们看到:

/*
The method reduce_LDA took 0.46 sec to run.
The method fitLinearSVM took 2.23 sec to run.
The method fitLinearSVM took 131.19 sec to run.
Overall accuracy of the model is 90.70 percent
Classification report:
               precision    recall  f1-score   support

         0.0       0.92      0.98      0.95     12167
         1.0       0.69      0.33      0.44      1556

    accuracy                           0.91     13723
   macro avg       0.80      0.65      0.70     13723
weighted avg       0.89      0.91      0.89     13723

Confusion matrix:
 [[11940   227]
 [ 1049   507]]
ROC:  0.6535892262415742
Overall accuracy of the model is 90.52 percent
Classification report:
               precision    recall  f1-score   support

         0.0       0.92      0.98      0.95     12145
         1.0       0.65      0.32      0.43      1524

    accuracy                           0.91     13669
   macro avg       0.79      0.65      0.69     13669
weighted avg       0.89      0.91      0.89     13669

Confusion matrix:
 [[11889   256]
 [ 1040   484]]
ROC:  0.6482533343274454

*/

LDA在咱们的数据上很快。
然而你能够注意到执行时间的差异:使用完整(未降维)的数据集估算的模型,要比降维的模型慢接近50倍。这应该是情理之中的,毕竟降维数据集只有一个特征,而完整的有59个特征。
比较两个模型的能力,降维空间的要优于完整数据集的!差别并不大,但用降维特征估算的SVM在召回率、f-指标和ROC方面的表现更好。另外,总体的精确度更接近91%,而完整特征的数据集更接近90%。

5.7用kNN分类模型给电话分类时使用多种降维技巧

既然咱们已经看到,维度的下降会带给咱们表现更好的分类模型,那么让咱们试试更多的方法,介绍下另外一个分类算法:kNN(k-Nearest Neighbors,k邻近)算法。
本技巧中,咱们会测试并比较三种降维模型:PCA(做为基准)、快速ICA(Independent Component Analysis,独立成分分析)以及截断奇异值分解。
准备:需装好pandas和Scikit。

本技巧中,咱们会用一个小例子展现,在Python中一切都是对象(方法也是),咱们能够将其做为参数传入方法。这是咱们下降维度的方法(reduce_kNN.py文件):

 1 import sklearn.decomposition as cd
 2 import sklearn.neighbors as nb
 3 
 4 @hlp.timeit
 5 def reduceDimensions(method, data, **kwrd_params):
 6     '''
 7         Reduce the dimensions
 8     '''
 9     # split into independent and dependent features
10     x = data[data.columns[:-1]]
11     y = data[data.columns[-1]]
12 
13     # create the reducer object
14     reducer = method(**kwrd_params)
15 
16     # fit the data
17     reducer.fit(x)
18 
19     # return the classifier
20     return reducer.transform(x)

原理:reduceDimensions(...)方法以reducer方法做为第一个参数,以估算中要使用的数据做为第二个参数,以reducer方法的全部关键词参数做为第三个参数。
为了精简代码,咱们挪走了数据拆分的代码。和以前模型相关的降维方法同样,咱们先建立一个降维者,而后应用数据。不过这一次,咱们返回转换后的数据。
咱们为不一样的降维者开发了模型相关的降维和分类的方法,用于本技巧。以使用PCA降维做为一个例子: 1 @hlp.timeit

 2 def fit_kNN_classifier(data):
 3     '''
 4         Build the kNN classifier
 5     '''
 6     # create the classifier object
 7     knn = nb.KNeighborsClassifier()
 8 
 9     # fit the data
10     knn.fit(data[0],data[1])
11 
12     #return the classifier
13     return knn

这个方法必需的参数只有数据。首先,咱们定义想使用的参数。而后,咱们调用传入特性降维者的reduceDimensions(...)方法;在这个例子中,就是cd.PCA,以及全部其余必需的参数data和**kwrd_params。
而后准备估算kNN分类器所必需的数据集。咱们使用prepare_data(...)方法:

 1 def prepare_data(data, principal_components, n_components):
 2     '''
 3         Prepare the data for the classification
 4     '''
 5     # prepare the column names
 6     cols = ['pc' + str(i)
 7         for i in range(0, n_components)]
 8 
 9     # concatenate the data
10     data = pd.concat(
11         [data,
12             pd.DataFrame(principal_components,
13                 columns=cols)],
14             axis=1, join_axes=[data.index])
15 
16     # split the data into training and testing
17     train_x, train_y, \
18     test_x,  test_y, \
19     labels = hlp.split_data(
20         data,
21         y = 'credit_application',
22         x = cols
23     )
24 
25     return (train_x, train_y, test_x, test_y)

这个方法以数据做为第一个参数,包括一个编码后的主成分列表(矩阵)以及指望的成分数目。本技巧全部的建模操做中,咱们让降维模型返回五个主成分。
首先,咱们建立列名的列表,以便将咱们的原始数据集和降维后的矩阵链接。咱们使用pandas的.concat(...)方法链接。这个方法以要链接的DataFrame列表做为第一个参数;这里咱们传递原始数据,并用特定的列从principal_components建立一个DataFrame。.concat(...)的axis参数指定了链接的方向(行),join_axes指定了链接的键。
如今咱们往原始数据集加上了主成分的列,咱们能够将数据集拆分红训练集和测试集。最终,咱们返回训练和测试子集的元组。
准备了数据后,咱们如今估算kNN,并在fit_pca(...)方法中调用class_fit_predict_print(...)方法评估模型:

 1 @hlp.timeit
 2 def fit_kNN_classifier(data):
 3     '''
 4         Build the kNN classifier
 5     '''
 6     # create the classifier object
 7     knn = nb.KNeighborsClassifier()
 8 
 9     # fit the data
10     knn.fit(data[0],data[1])
11 
12     #return the classifier
13     return knn
14 
15 def class_fit_predict_print(data):
16     '''
17         Automating model estimation
18     '''
19     # train the model
20     classifier = fit_kNN_classifier((data[0], data[1]))
21 
22     # classify the unseen data
23     predicted = classifier.predict(data[2])
24 
25     # print out the results
26     hlp.printModelSummary(data[3], predicted)

传入训练数据和测试数据(形式是(train_x,train_y,test_x,test_y)元组)。
首先,咱们用fit_kNN_classifier(...)方法估算分类器。咱们仅传入(train_x,train_y)元组做为数据集用于估算。
如同其余的估算方法,咱们建立分类器对象(.KNeighborsClassifier(...))。
.KNeighborsClassifier(...)可使用不少参数,但咱们以为事情仍是简单点。文档能够参考http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighbors Classifier.html。
而后,咱们应用模型,返回分类器。咱们使用.KNeighborsClassifier(...)暴露出的.predict(...)方法预测test_x数据集的分类。最后,咱们在真实的数据(test_y)上用.prin进咱们tModelSummary(...)方法打印结果,这个方法是咱们在本书3.2节中开发的。
本技巧的开始部分已经提过,咱们比较降维与否后kNN模型的效率:

1 # compare models
2 fit_clean(csv_read)
3 fit_pca(csv_read)
4 fit_fastICA(csv_read)
5 fit_truncatedSVD(csv_read)

Tips:

1 /* The method reduceDimensions took 0.22 sec to run.
2 D:\Java2018\practicalDataAnalysis\Codes\Chapter05\reduce_kNN.py:56: FutureWarning: The join_axes-keyword is deprecated. 
3 Use .reindex or .reindex_like on the result to achieve the same functionality.
4 axis=1, join_axes=[data.index]) 5 */

解决方案

/*
    # concatenate the data
    data = pd.concat(
        [data,
            pd.DataFrame(principal_components,
                columns=cols)],
            axis=1, join_axes=[data.index])
            
            
    修改成:*********************************************
    # concatenate the data
    data = pd.concat(
        [data,
            pd.DataFrame(principal_components,
                columns=cols)],
            axis=1).reindex()
    # axis=1, join_axes=[data.index])
     */

结果以下:

/*

--PCAClean
The method fit_kNN_classifier took 0.79 sec to run.
Overall accuracy of the model is 89.06 percent
Classification report:
               precision    recall  f1-score   support

         0.0       0.91      0.97      0.94     12120
         1.0       0.55      0.25      0.34      1568

    accuracy                           0.89     13688
   macro avg       0.73      0.61      0.64     13688
weighted avg       0.87      0.89      0.87     13688

Confusion matrix:
 [[11805   315]
 [ 1182   386]]
ROC:  0.6100916851889271

----PCA
The method reduceDimensions took 0.21 sec to run.
The method fit_kNN_classifier took 0.04 sec to run.
Overall accuracy of the model is 92.07 percent
Classification report:
               precision    recall  f1-score   support

         0.0       0.93      0.98      0.96     12031
         1.0       0.77      0.44      0.56      1558

    accuracy                           0.92     13589
   macro avg       0.85      0.71      0.76     13589
weighted avg       0.91      0.92      0.91     13589

Confusion matrix:
 [[11826   205]
 [  873   685]]
ROC:  0.7113134618324997

--FastICA
The method reduceDimensions took 0.53 sec to run.
The method fit_kNN_classifier took 0.04 sec to run.
Overall accuracy of the model is 91.67 percent
Classification report:
               precision    recall  f1-score   support

         0.0       0.93      0.98      0.95     12113
         1.0       0.72      0.43      0.54      1547

    accuracy                           0.92     13660
   macro avg       0.83      0.70      0.75     13660
weighted avg       0.91      0.92      0.91     13660

Confusion matrix:
 [[11856   257]
 [  881   666]]
ROC:  0.7046468956861779

---截断奇异值
The method reduceDimensions took 0.16 sec to run.
The method fit_kNN_classifier took 0.04 sec to run.
Overall accuracy of the model is 92.71 percent
Classification report:
               precision    recall  f1-score   support

         0.0       0.94      0.98      0.96     12059
         1.0       0.77      0.49      0.60      1508

    accuracy                           0.93     13567
   macro avg       0.85      0.74      0.78     13567
weighted avg       0.92      0.93      0.92     13567

Confusion matrix:
 [[11839   220]
 [  769   739]]
ROC:  0.7359047074694424 */


fit_clean(...)在一个特征完备的数据集上应用kNN。比较之下,它表现最差,只有89%的总体精确度;其余方法都接近92%,其中截断奇异值分解几乎达到了93%。截断奇异值分解在准确率、召回率、f-指标和ROC几个指标上的表现都优于其余方法。

 

第5章完。

 python学习笔记-目录索引

 

随书源码官方下载:
http://www.hzcourse.com/web/refbook/detail/7821/92

相关文章
相关标签/搜索