在sklearn中恢复PCA的explained_variance_ratio_特征名称

102 投票
5 回答
87248 浏览
提问于 2025-04-18 02:10

我正在尝试从使用scikit-learn进行的主成分分析(PCA)中找回哪些特征被选为相关特征。

这是一个经典的例子,使用的是IRIS数据集。

import pandas as pd
import pylab as pl
from sklearn import datasets
from sklearn.decomposition import PCA

# load dataset
iris = datasets.load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)

# normalize data
df_norm = (df - df.mean()) / df.std()

# PCA
pca = PCA(n_components=2)
pca.fit_transform(df_norm.values)
print pca.explained_variance_ratio_

这段代码返回了

In [42]: pca.explained_variance_ratio_
Out[42]: array([ 0.72770452,  0.23030523])

我该如何找回这两个特征,它们在数据集中解释了这两个方差?换句话说,我该如何获取这些特征在iris.feature_names中的索引?

In [47]: print iris.feature_names
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']

5 个回答

6

在你已经训练好的模型 pca 中,组件可以在 pca.components_ 找到。这些组件表示数据集中变化最大的方向。

21

重要的 特征 是那些对组件影响最大,因此在组件上有 很大的绝对值/系数/载荷

from sklearn.decomposition import PCA
import pandas as pd
import numpy as np
np.random.seed(0)

# 10 samples with 5 features
train_features = np.random.rand(10,5)

model = PCA(n_components=2).fit(train_features)
X_pc = model.transform(train_features)

# number of components
n_pcs= model.components_.shape[0]

# get the index of the most important feature on EACH component i.e. largest absolute value
# using LIST COMPREHENSION HERE
most_important = [np.abs(model.components_[i]).argmax() for i in range(n_pcs)]

initial_feature_names = ['a','b','c','d','e']

# get the names
most_important_names = [initial_feature_names[most_important[i]] for i in range(n_pcs)]

# using LIST COMPREHENSION HERE AGAIN
dic = {'PC{}'.format(i+1): most_important_names[i] for i in range(n_pcs)}

# build the dataframe
df = pd.DataFrame(sorted(dic.items()))

这将输出:

     0  1
 0  PC1  e
 1  PC2  d

结论/解释:

所以在PC1上,名为 e 的特征是最重要的,而在PC2上是 d

49

这个问题的提法让我想起了我刚开始学习主成分分析(PCA)时的一些误解。我想在这里分享一下,希望能让其他人少走一些弯路,不用像我那样花费很多时间才明白过来。

提到“恢复”特征名称,似乎是在说PCA能找出数据集中最重要的特征。但其实这并不完全正确。

根据我的理解,PCA是找出数据集中变化最大的特征,然后利用这个特性来创建一个更小的数据集,同时尽量减少信息的损失。小数据集的好处是处理起来需要的计算能力更少,而且数据中的噪声也会减少。但是,变化最大的特征并不一定是数据集中“最好”或“最重要”的特征,毕竟这些概念本身就有点模糊。

接下来我们看看@Rafa的示例代码:

# load dataset
iris = datasets.load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)

# normalize data
from sklearn import preprocessing
data_scaled = pd.DataFrame(preprocessing.scale(df),columns = df.columns) 

# PCA
pca = PCA(n_components=2)
pca.fit_transform(data_scaled)

考虑以下内容:

post_pca_array = pca.fit_transform(data_scaled)

print data_scaled.shape
(150, 4)

print post_pca_array.shape
(150, 2)

在这个例子中,post_pca_arraydata_scaled都有150行数据,但data_scaled的四列数据被减少到了两列。

这里的关键点是,post_pca_array的这两列(为了术语一致,我们称之为“成分”)并不是data_scaled中“最好”的两列。它们是由sklearn.decompositionPCA模块的算法生成的新列。@Rafa例子中的第二列PC-2,主要受sepal_width的影响,但PC-2的值和data_scaled['sepal_width']的值并不相同。

因此,虽然了解原始数据中每一列对后PCA数据集成分的贡献是很有趣的,但“恢复”列名这个说法有点误导,确实让我困惑了很久。只有在主成分的数量和原始数据的列数相同的情况下,后PCA和原始列才会有匹配。但这样做其实没有意义,因为数据并没有改变。你只是绕了一圈又回到了原点罢了。

61

编辑:正如其他人所评论的,你可以从 .components_ 属性中获得相同的值。


每个主成分都是原始变量的线性组合:

pca-coef

这里的 X_i 是原始变量,而 Beta_i 是对应的权重,或者说是系数。

要获得这些权重,你可以简单地把单位矩阵传递给 transform 方法:

>>> i = np.identity(df.shape[1])  # identity matrix
>>> i
array([[ 1.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]])

>>> coef = pca.transform(i)
>>> coef
array([[ 0.5224, -0.3723],
       [-0.2634, -0.9256],
       [ 0.5813, -0.0211],
       [ 0.5656, -0.0654]])

上面的 coef 矩阵的每一列显示了在获得对应主成分时的线性组合中的权重:

>>> pd.DataFrame(coef, columns=['PC-1', 'PC-2'], index=df.columns)
                    PC-1   PC-2
sepal length (cm)  0.522 -0.372
sepal width (cm)  -0.263 -0.926
petal length (cm)  0.581 -0.021
petal width (cm)   0.566 -0.065

[4 rows x 2 columns]

例如,上面显示第二个主成分(PC-2)主要与 sepal width 对齐,这个变量的绝对权重最高,为 0.926

由于数据已经标准化,你可以确认主成分的方差为 1.0,这相当于每个系数向量的范数也为 1.0

>>> np.linalg.norm(coef,axis=0)
array([ 1.,  1.])

还可以确认主成分可以通过上面的系数和原始变量的点积来计算:

>>> np.allclose(df_norm.values.dot(coef), pca.fit_transform(df_norm.values))
True

注意,我们需要使用 numpy.allclose 而不是普通的相等运算符,因为浮点数精度可能会出错。

117

这些信息包含在 pca 属性的 components_ 中。根据 文档 的说明,pca.components_ 输出的是一个形状为 [n_components, n_features] 的数组。所以,要了解这些成分是如何与不同特征线性相关的,你需要:

注意:每个系数代表某个特定成分和特征之间的相关性。

import pandas as pd
import pylab as pl
from sklearn import datasets
from sklearn.decomposition import PCA

# load dataset
iris = datasets.load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)

# normalize data
from sklearn import preprocessing
data_scaled = pd.DataFrame(preprocessing.scale(df),columns = df.columns) 

# PCA
pca = PCA(n_components=2)
pca.fit_transform(data_scaled)

# Dump components relations with features:
print(pd.DataFrame(pca.components_,columns=data_scaled.columns,index = ['PC-1','PC-2']))

      sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
PC-1           0.522372         -0.263355           0.581254          0.565611
PC-2          -0.372318         -0.925556          -0.021095         -0.065416

重要提示: 另外要提一下,PCA 的符号并不影响它的解释,因为符号不会影响每个成分所包含的方差。只有形成 PCA 维度的特征之间的相对符号才是重要的。实际上,如果你再次运行 PCA 代码,你可能会得到符号反转的 PCA 维度。为了理解这一点,可以想象在三维空间中的一个向量及其负向量——这两个向量本质上表示的是相同的方向。

查看 这篇文章 以获取更多参考信息。

撰写回答