Восстановление имен функций explain_variance_ratio_ в PCA с помощью sklearn
Я пытаюсь восстановить из PCA, сделанного с помощью scikit-learn, , которые выбираются как релевантные.
Классический пример с набором данных 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)']
Заранее благодарим за помощь.
Ответы
Ответ 1
Эта информация включена в атрибут 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 с инвертированными знаками. Для интуитивного представления об этом подумайте о векторе и его негативе в трехмерном пространстве - оба по существу представляют одно и то же направление в пространстве. Проверьте этот пост для дальнейшего использования.
Ответ 2
Изменить: как прокомментировали другие, вы можете получить те же значения из атрибута .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
вместо обычного оператора равенства из-за ошибки точности с плавающей запятой.
Ответ 3
То, как этот вопрос сформулирован, напоминает мне о непонимании Принципиального Компонента, когда я впервые пытался понять это. Id нравится проходить через это здесь, надеясь, что другие не будут тратить столько времени на дорогу в никуда, как я делал, прежде чем пенни наконец упали.
Понятие "восстановления" имен функций предполагает, что PCA идентифицирует те функции, которые наиболее важны в наборе данных. Это не совсем верно.
PCA, как я понимаю, идентифицирует функции с наибольшей дисперсией в наборе данных и затем может использовать это качество набора данных для создания меньшего набора данных с минимальной потерей описательной мощности. Преимущества меньшего набора данных в том, что он требует меньшей вычислительной мощности и должен иметь меньше шума в данных. Но особенности наибольшей дисперсии не являются "лучшими" или "наиболее важными" характеристиками набора данных, поскольку такие понятия могут вообще существовать.
Чтобы привести эту теорию в практические примеры кода примера @Rafas выше:
# 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_array
имеет те же 150 строк данных, что и data_scaled
, но data_scaled
четыре столбца сокращены с четырех до двух.
Критическая точка здесь состоит в том, что два столбца - или компоненты, которые должны быть терминологически согласованы - post_pca_array
, не являются двумя "лучшими" столбцами data_scaled
. Это два новых столбца, определяемые алгоритмом модуля sklearn.decomposition
s PCA
. Второй столбец PC-2
в примере @Rafas сообщается sepal_width
больше, чем любой другой столбец, но значения в PC-2
и data_scaled['sepal_width']
не совпадают.
Таким образом, хотя интересно узнать, насколько каждый столбец исходных данных внес вклад в компоненты набора данных после ПКА, понятие "восстановления" имен столбцов мало вводит в заблуждение и, конечно, вводит меня в заблуждение в течение длительного времени время. Единственная ситуация, в которой было бы совпадение между пост-PCA и исходными столбцами, было бы, если бы количество основных компонентов было установлено с тем же номером, что и столбцы в оригинале. Однако не было бы смысла использовать одинаковое количество столбцов, потому что данные не изменились бы. Вы бы только пошли туда, чтобы вернуться, как это было.
Ответ 4
С учетом вашей установленной оценки pca
компоненты должны быть найдены в pca.components_
, которые представляют направления наибольшей дисперсии в наборе данных.
Ответ 5
Важными характеристиками являются те, которые оказывают большее влияние на компоненты и, следовательно, имеют большое абсолютное значение/коэффициент/нагрузку на компонент.
Получить the most important feature name
на ПК:
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
Заключение/Объяснение:
Таким образом, на ПК1 функция с именем e
является наиболее важной, а на ПК2 - d
.