数据 = np 。阵列([[ 2.5 ,0.5 ,2.2 ,1.9 ,3.1 ,2.3 ,2 ,1 ,1.5 ,1.1 ],[ 2.4 ,0.7 ,2.9 ,2.2 ,3.0 ,2.7 ,1.6 ,1.1 ,1.6 ,0.9 ]])
打印(数据)
无花果 = plt 。图()
ax0 = 图。add_subplot ( 111 )
轴0 。分散(数据[ 0 ],数据[ 1 ])
ax0 。散射(数据[ 0 ],NP 。ones_like (数据[ 1 ])*分钟(数据[ 1 ])- 0.2 ,颜色= “红” )
AX0 。散射(NP 。ones_like (数据[ 0 ])*min ( data [ 0 ]) - 0.2 , data [ 1 ], color = “blue” )
ax0 。箭头( min ( data [ 0 ]) - 0.2 , min ( data [ 1 ]) - 0.2 , 0 , max ( data [ 1 ]) - 0.5 , width = 0.01 , color =“blue” , alpha = 0.4 , length_includes_head = “True” )
ax0 。箭头( min ( data [ 0 ]) - 0.2 , min ( data [ 1 ]) - 0.2 , max ( data [ 0 ]) - 0.3 , 0 , width = 0.01 , color = “red” , alpha = 0.4 ,length_includes_head = “True” )
ax0 。vlines ( data [ 0 ], min ( data [ 1 ]) - 0.2 , data [ 1 ], colors = “red” , linestyles = “–” , linewidth = 0.7 )
ax0 。hlines ( data [ 1 ], min ( data [ 0 ]) - 0.2 ,数据[ 0 ],颜色= “蓝色” ,线型= “–” ,线宽= 0.7 )
PLT 。显示()
输出:

[[2.5 0.5 2.2 1.9 3.1 2.3 2. 1. 1.5 1.1] [2.4 0.7 2.9 2.2 3. 2.7 1.6 1.1 1.6 0.9]]
现在您可以看到选择 x 和 y 轴作为我们的主要组件的效果。我们将数据投影到 x 和 y 轴上。我们现在可以计算投影数据的方差,看看我们的好坏。如果您对我们如何完成这种转变感到困惑,请保持冷静,我们稍后再讨论。
所以现在我们选择了 x 和 y 轴作为我们的主要组件。但如上所述,在这种情况下,这很可能是不正确的,因为我们已经看到从左下角到右上角的倾斜(绿色)线是向量跨越的线,指向最高变化的方向 == 1 . 主成分(在这一点上我不得不提到,一个数据集的主成分与其维度一样多,但第一个主成分是“最强的”)。所以我们首先假设,这条倾斜的绿线实际上是数据集的第一个主成分,即指向最大变化方向的向量。如果我们选择任意向量并做与我们将 x 和 y 方向作为我们假设的主成分完全相同的事情,这看起来如何(请注意,我们想要将数据投影到线上,因为我们想要计算变化)。请注意,由于我们现在进行实际计算,因此必须将数据归一化为零均值,否则计算将失败。
我们现在将绘制数据集并选择可以使用滑块更改其值的任意向量。我们将数据集投影到向量跨越的线上(由滑块值定义)。我们使用选择的向量转换数据集并计算结果方差。导致最大方差的点(滑块调整)为我们提供了第一个主成分。此外,我们根据我们为向量选择的值绘制“方差曲面”。总而言之,通过改变线的方向,我们希望找到这条线,当数据集投影到这条线上时,这条线会导致最大的方差。这条线是 1. 主成分。如果我们选择平行于 x 和 y 轴的线,我们只需切断另一个轴。
import numpy as np
import pandas as pd
from ipywidgets import interact 、interactive 、fixed 、interact_manual
import matplotlib.pyplot as plt
from matplotlib import style
from mpl_toolkits.mplot3d import Axes3D
style 。使用(‘五三十八’ )
定义 f ( x , y ):
数据 = np 。阵列([[ 2.5 ,0.5 ,2.2 ,1.9 ,3.1 ,2.3 ,2 ,1 ,1.5 ,1.1 ],[ 2.4 ,0.7 ,2.9 ,2.2 ,3.0 ,2.7 ,1.6 ,1.1 ,1.6 ,0.9 ])
的数据[ 0 ] = 数据[ 0 ] - np. 均值(数据[ 0 ])
数据[ 1 ] = 数据[ 1 ] - np 。平均值(数据[ 1 ])
# 创建轴
无花果 = plt 。图( figsize = ( 10 , 10 ))
ax0 = fig . add_subplot ( 121 )
ax0 。set_aspect ( ‘equal’ )
ax0 。set_ylim ( - 2 , 2 )
ax0 。set_xlim ( - 2 , 2 )
ax0 。set_title ( ‘搜索主成分’ , fontsize =14 )
ax0 。set_xlabel ( ‘PC x value’ , fontsize = 10 )
ax0 。set_ylabel ( ‘PC y 值’ , fontsize = 10 )
#vec = np.array([0.6778734,0.73517866])
vec = np . 数组([ x , y ])
轴0 。分散(数据[ 0 ],数据[ 1 ])
ax0 。情节(NP 。linspace (分钟(数据[ 0 ]),最大(数据[ 0 ])),(VEC [ 1 ] / VEC [ 0 ])* NP 。linspace (分钟(数据[ 0 ]),最大(数据[0 ])), linewidth = 1.5 , color = “black” , linestyle = “–” )
b_on_vec_list = [[],[]]
for i in range ( len ( data [ 0 ])):
a = vec
b = np 。数组([ data [ 0 ][ i ], data [ 1 ][ i ]])
b_on_a = ( np . dot ( a , b ) / np . dot ( a , a )) *a
b_on_vec_list [ 0 ] 。追加( b_on_a [ 0 ])
b_on_vec_list [ 1 ] 。追加( b_on_a [ 1 ])
ax0 。分散( b_on_a [ 0 ], b_on_a [ 1 ], color = ‘red’ )
ax0 。绘图([ b_on_a [ 0 ], b [ 0 ]],[ b_on_a [ 1], b [ 1 ]], “r–” , linewidth = 1 )
ax1 = 图。add_subplot ( 122 ,投影= ‘3d’ )
ax1 。set_aspect ( ‘equal’ )
ax1 。set_ylim ( 0 , 1 )
ax1 。set_xlim ( 0 , 1 )
ax1 。set_title ( ‘Varicane 相对于 1. PC’ , fontsize = 14 )
ax1 . set_xlabel ( ‘PC x value’ , fontsize= 10 )
ax1 。set_ylabel ( ‘PC y value’ , fontsize = 10 )
ax1 。set_zlabel (‘方差’ ,fontsize = 10 )
#变换数据
e_vec = (1 / NP 。SQRT (NP 。点(VEC ,VEC 。Ť )))* VEC
data_trans = NP 。点(数据。Ť ,e_vec )
# 绘制数据
ax0 。scatter ( data_trans , np . zeros_like ( data_trans ), c = ‘None’ , edgecolor = ‘black’ )
# 绘制扭曲线
ax0 。情节(NP 。linspace (分钟(data_trans ),最大值(data_trans ),10 ),NP 。zeros_like (data_trans ),线型= ’ - ',颜色= ‘灰色’ ,线宽= 1.5 )
#剧情圆圈
为 我 在 范围(len个(data_trans )):
AX0 。add_artist ( plt . Circle (( 0 , 0 ), data_trans [ i ], linewidth = 0.5 , linestyle = ‘dashed’ , color = ‘grey’ , fill = False ))
# 计算方差
ax0 。文本(0 ,- 1.4 ,'方差= {0} ’ 。格式(STR (NP 。圆(NP 。VAR (data_trans ),3 ))),字体大小= 20 )
# 绘制关于主成分向量的方差
# 初始化网格
cross_x , cross_y = np . meshgrid (NP 。linspace (0.001 ,1 ,NUM = 20 ),NP 。linspace (0.001 ,1 ,NUM = 20 ))
#格式为[(0.01,0.01)创建迭代器,(0.01,0.0620),(0.01 ,0.114),…(0.0620,0.01),(0.0620,0.0620),(0.0620,0.1141),…(0.999,0.01),(0.999,0.0620),…(0.999,0.999)]
x_y_pairs = []
为 我 在 范围(len ( cross_y )):
x_y_pairs 。append ( list ( zip ( cross_x [ i ], cross_y [ i ])))
flatten_x_y_pairs = [ np . 数组(列表(x_y )) 用于x_y_pairs中的子列表**,**用于 子列表 中的 x_y ]
方差 = []
for i in flatten_x_y_pairs :
e_vec = ( 1 / np . sqrt ( np . dot ( i , i . T ))) * i
data_trans = np . 点(数据。Ť ,e_vec 。Ť )
方差。追加(NP 。VAR (data_trans ))
index_of_max_variance = np 。where ( variances == max ( variances ))[ 0 ][ 0 ]
#PLot 方差表面
ax1 。散射(cross_x ,cross_y ,NP 。阵列(方差)。重塑(20 ,20 ),阿尔法= 0.8 )
# 标记方差
最大 _的点_vec_point = np . 阵列([ X ,ÿ ])
e_vec_point = (1 / NP 。SQRT (NP 。点(vec_point ,vec_point 。Ť )))* vec_point
data_trans_point = NP 。点(数据。Ť ,e_vec_point 。Ť )
AX1 。散射( x, y , np 。var ( data_trans_point ) + 0.01 , color = “orange” , s = 100 )
PLT 。显示()
交互( f , x = ( 0.001 , 1 , 0.001 ), y = ( 0.001 , 1 , 0.001 ))
输出:

<函数 __main__.f(x, y)>
通过使用滑块,我们可以看到 [0.67,0.73] 的滑块值导致最高方差。还要注意灰色虚线圆圈以及 x 轴上的灰色散点。如果我们将数据集投影到所选向量所跨越的线上,并围绕这条线进行某种扭曲,使其与我们原来的 x 轴对齐,我们将跨越线作为我们的新 x 轴。在改变向量值的同时,我们现在可以观察这些值在 x 轴上的分布,这些值分布得越多,方差就越大。数据集的这种投影和跨线的扭曲是通过使用所选向量转换原始数据集来完成的。

但是等等,你说投影和转换…我们如何投影数据?好吧,因此您必须熟悉线性代数。如果你是,那很好。如果没有,这个关于线性代数本质的youtube 播放列表可能非常有用。所以我们正在做的只是计算我们的数据集和所选向量的 *点积 *(有时也称为标量积)。现在为简单起见,假设我们使用任意数据集并希望将此数据集投影到 y 轴上。要投影数据集,我们需要两件事。一、shape的数据集nXn 和一个形状向量 nX米. 我们知道,计算点积后,得到的数据集具有维数nX米 因此如果 米<n,我们降低了原始数据集的维度。现在让我们把它付诸实践。
假设我们有一个 10X2数据集,我们希望将此数据集投影到指向 y 轴方向的向量所跨越的线上(这与我们上面所做的相同)。这个向量是单位向量 [0,1],它有维数1X2. 我们想要的是投影到 y 轴上的数据集,以及具有维度的数据集10X1. 因此,要实现这一点,我们必须计算以下项的点积:
d一个吨一个*v电子C吨 在哪里 v电子C吨 是转置的单位向量,因此不再具有形状 1X2 但 2X1 并且由此产生的数据集具有形状 10X1. 如果我们现在在实践中进行所描述的计算,这看起来像:
将 numpy 导入****为 np
data = np 。阵列([[ 2.5 ,0.5 ,2.2 ,1.9 ,3.1 ,2.3 ,2 ,1 ,1.5 ,1.1 ],[ 2.4 ,0.7 ,2.9 ,2.2 ,3.0 ,2.7 ,1.6 ,1.1 ,1.6 ,0.9 ]])
EX = NP . 数组([[1 , 0 ]])
ey = np 。数组([[ 0 , 1 ]])
打印(数据。形状)
打印(EX 。形状)
print ( np . dot ( data . T , ey . T )) # 如您所见,这正是我们数据集的 y 分量(x 维)
输出:
(2, 10)
(1, 2)
[[2.4]
[0.7]
[2.9]
[2.2]
[3. ]
[2.7]
[1.6]
[1.1]
[1.6]
[0.9]]
如您所见,结果与原始数据集的 y 值相同。这是合乎逻辑的,因为通过将数据转换到 y 轴(看上面的图)我们只是省略了数据集的 x 值。现在假设我们不使用向量 [0,1],它是指向 y 轴方向的单位向量,而是使用任何其他任意向量,例如 [0.653,1.2] 会发生什么?好吧,计算完全相同:我们计算 10x2 数据集与 2x1 向量的点积,并得到维度为 10x1 的数据集投影到该向量上。尽管如此,我们将数据投影到的线不再是一条垂直线,而是一条斜率为 1.2/0.653 的斜线。
import matplotlib.pyplot as plt
from matplotlib import style
style 。使用( ‘fivethirtyeight’ )
import numpy as np
import matplotlib.patches 作为 补丁
def f (羊肉):
# 单位向量
e_x = np . 数组([ 1 , 0 ])
e_y = np 。数组([ 0 , 1 ])
#区跨越由单位矢量
打印(NP 。交叉(e_x ,E_Y )) #区跨越由单位矢量== 1
# 任何形状为 (A-lambda*I)
A = np 的_二维矩阵 A_ 。数组([[ 2 -羔羊, 3 ],[ 3 , 0.5 -羔羊]])
# 用矩阵 A 变换单位向量 --> 不出所料,这正是矩阵 A,否则
“行列式描述变换后单位向量所跨越的区域的变化”的__符号__是没有意义的
# 绘制向量
fig = plt . 图( figsize = ( 10 , 10 ))
ax0 = fig . add_subplot ( 111 )
ax0 。set_xlim ( - 5 , 8 )
ax0 。set_ylim ( - 5 , 8 )
ax0 。set_aspect ( ‘相等’ )
# 矩阵 A
ax0 的_向量_。arrow ( 0 , 0 , A [ 0 ][ 0 ], A [ 0 ][ 1 ], color = “red” , linewidth = 1 , head_width = 0.05 ) #第一个向量
ax0 。箭头( 0 , 0 , A [ 1 ][ 0 ], A [ 1 ][ 1 ],颜色= “blue” , linewidth = 1 , head_width = 0.05 ) # 第二个向量
# 向量跨越的区域**
ax0 。箭头( A [ 0 ][ 0 ], A [ 0 ][ 1 ], A [ 1 ][ 0 ], A [ 1 ][ 1 ], color = “blue” , linestyle = ‘dashed’ , alpha = 0.3 ,线宽= 1 , head_width = 0.05 )
ax0 。箭头( A[ 1 ][ 0 ], A [ 1 ][ 1 ], A [ 0 ][ 0 ], A [ 0 ][ 1 ], color = “red” , linestyle = ‘dashed’ , alpha = 0.3 , linewidth = 1 , head_width = 0.05 )
ax0 。add_patch (补丁。多边形( xy = [[ 0, 0 ],[ A [ 0 ][ 0 ], A [ 0 ][ 1 ]],[ A [ 0 ][ 0 ] + A [ 1 ][ 0 ], A [ 0 ][ 1 ] + A [ 1 ][ 1 ]],[ A [ 1 ][ 0 ], A [ 1 ][ 1 ]]], fill = True , alpha =0.1 ,颜色= ‘黄色’ ))
# 添加显示行列式和面积
ax0_计算的文本_。文本(3 ,- 0 ,小号= - [R ‘$行列式= A_ {11} * A_ {22} -a_ {21} * A_ {12} $’ + '= {0} ’ 。格式(NP 。轮(甲[ 0 ][ 0 ] * A [ 1 ][ 1 ] - A [ 1 ][ 0] * A [ 0 ][ 1 ], 3 )))
#ax0.text(3,-1,s=‘area = {0}’.format(np.round(np.cross(AT[0],AT) [1]),3)))
ax0 。文本( 3 , - 0.5 , s = r ‘’ + '= {0} * {1} - {2} * {3} = {4} ’ .格式( A [ 0 ][ 0 ], A [ 1 ][ 1 ], A[ 1 ][ 0 ],A [ 0 ][ 1 ],np 。round ( A [ 0 ][ 0 ] * A [ 1 ][ 1 ] - A [ 1 ][ 0 ] * A [ 0 ][ 1 ], 3 )))
ax0 。text ( 3 , - 4 , s = ‘**注意在这种情况下行列式的值\n和 area(cross product --> Yellow shaded) 是相同的\n因为单位向量跨越的区域是 1’ ,fontsize = 8 )
# 绘制特征向量
ax0 。箭头( 0 , 0 , 0.61505 , - 0.788491 , color = “black” , linestyle = ‘dashed’ , alpha = 0.3 , linewidth = 1 , head_width = 0.05 )
ax0 。箭头( 0 , 0 , 0.78771 , 0.6159 , color = “black” , linestyle =‘dashed’ , alpha = 0.3 , linewidth = 1 , head_width = 0.05 )
# 使用找到的特征向量为不同的 lambda 值计算 (A-lambda I)*nu。
当 nu 垂直于 (A-lambda I) 时
,结果必须为_# 0_ # 注意,对于 v1 和 v2 的计算,我们必须求解线性方程组 (A-lambda I)*v=0
# 对于 v1 和v2
v1 = - 3 * ((( - 1 + 0.5 *羊肉) / ( - 9 - 2 *羊肉+羊肉** 2 ))) / ( 2 -羊肉)
v2 = ( - 1 + 0.5 *羊肉) / ( - 9 - 2 *羊肉+羊肉** 2 )
v = np .阵列((1 / NP 。SQRT (V1 ** 2 + V2 ** 2 ))* NP 。阵列([ V1 ,V2 ))
AX0 。text ( 3 , - 1 , s = r ‘’ + ’ {0} ’ . format ( lamb ) + r ‘KaTeX parse error: Undefined control sequence: \* at position 3: I)\̲*̲\\nu’ + '= {0} ’ . format( np . round ( np . dot ( A , v ), 3 )))
ax0 . 箭头( 0 , 0 , - v [ 0 ] * 0.5 , - v [ 1 ] * 0.5 , color = “green” , alpha = 0.8 , linewidth = 1 , head_width = 0.05 ) # 我们为 lambda 绘制特征向量
# Mind v[0]*0.5 和 v[1]*0.5 --> *0.5
# 仅用于可视化目的
PLT 。显示()
互动( f ,羔羊= ( - 5 , 5 , 0.001 ))
输出:

1 <function __main__.f(lamb)>
上面我们运行了步骤 1 到 4,所以接下来是使用特征向量转换数据。
电阻电子d你C电子 吨H电子 d一世米电子n秒一世○n一个升一世吨是 ○F 吨H电子 d一个吨一个 - 磷你吨吨一世nG 一个升升 吨○G电子吨H电子r―
一旦我们找到了数据集的特征向量和特征值,我们最终可以使用这些向量(它们是数据集的主成分)来降低数据的维数,即将数据投影到主成分上。
因此,让我们这样做,同时执行上述所有步骤,以展示在我们使用预先打包的 sklearn PCA 方法之前,如何使用 Python 从头开始使用 PCA 来完成降维。为了说明这一点,我们将使用 UCI Iris 数据集。
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.cross_validation import train_test_split
import matplotlib.pyplot as plt
from matplotlib import style
style 。使用( ‘fivethirtyeight’ )
导入 熊猫 作为 pd
“”“1. 收集数据”“”
df = pd 。read_table ( ‘data\Wine.txt’ , sep = ‘,’ , names = [ ‘酒精’ , ‘苹果酸’ , ‘灰分’ , ‘灰分碱度’ , ‘镁’ , ‘总酚’ ,
‘类黄酮’ , ‘Nonflavanoid_phenols’ 、‘原花青素’ 、‘Color_intensity’ 、‘Hue’ 、
‘OD280/OD315_of_diluted_wines’ 、‘脯氨酸’ ])
目标 = df 。指数
“”“2. 规范化数据”“”
df = StandardScaler () 。fit_transform ( df )
“”“3.计算协方差矩阵”“”
















