DimensionalityReduction
PCA和LDA进行数据降维
使用PCA对数据进行降维,我们使用两种方式:
直接按数学推导的方式实现PCA
使用sklearn实现PCA
利用降维后的特征进行逻辑回归分类
代码使用LDA对数据进行降维,我们使用两种方式:
直接按数学推导过程实现LDA
使用sklearn实现LDA
利用降维后的特征进行逻辑回归分类
#%% md
#%%
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
from matplotlib.colors import ListedColormap
from sklearn.linear_model import LogisticRegression
#%% md
# 数据获取
#%%
# 读取数据()
df_wine = pd.read_csv('wine.data',header=None)
#%% md
该数据集是UCI的公开数据集,是对意大利同一地区种植的葡萄酒进行分析的结果,数据集共14列数据,第一个属性是类标识符,分别是1/2/3来表示,代表葡萄酒的三个分类。剩余的13个属性是,酒精、苹果酸、灰、灰分的碱度、镁、总酚、黄酮类化合物、非黄烷类酚类、原花色素、颜色强度、色调等。
#%%
# 设置列索引
df_wine.columns = ['Class label', 'Alcohol', 'Malic acid', 'Ash',
'Alcalinity of ash', 'Magnesium', 'Total phenols',
'Flavanoids', 'Nonflavanoid phenols', 'Proanthocyanins',
'Color intensity', 'Hue',
'OD280/OD315 of diluted wines', 'Proline']
#%%
# 数据维度
df_wine.shape
#%%
# 每一类数据包含的样本个数
df_wine['Class label'].value_counts()
#%%
df_wine.head()
#%% md
# 数据集划分
#%%
# 数据集设置:X为样本特征数据,y为目标数据,即标注结果
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
#%%
# 数据集划分: 将数据集划分为训练集和测试集数据(测试集数据为30%,训练集为70%)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3,
stratify=y,
random_state=0)
#%% md
# 数据标准化
#%%
# 实例化
sc = StandardScaler()
#%%
# 对数据集进行标准化(一般情况下我们在训练集中进行均值和方差的计算,直接在测试集中使用)
X_train_std = sc.fit_transform(X_train)
X_test_std = sc.transform(X_test)
#%% md
# PCA(Pricipal component analysis)
#%% md
## PCA实现
#%% md
### 特征值计算
#%%
# 计算协方差矩阵
cov_mat = np.cov(X_train_std.T)
#%%
# 对协方差矩阵进行特征值分解
eigen_vals, eigen_vecs = np.linalg.eig(cov_mat)
#%%
# 特征值
eigen_vals
#%% md
### 特征值分布
#%%
# 特征值之和
tot = sum(eigen_vals)
#%%
# 对特征进行排序,并计算所占的比例
var_exp = [(i / tot) for i in sorted(eigen_vals, reverse=True)]
#%%
# 累计求和
cum_var_exp = np.cumsum(var_exp)
#%%
# 绘制图像
plt.figure()
plt.bar(range(1, 14), var_exp, alpha=0.5, align='center',
label='特征值分布')
plt.step(range(1, 14), cum_var_exp, where='mid',
label='累计特征值')
plt.ylabel('特征值比例')
plt.xlabel('特征index')
plt.legend(loc='best')
#%% md
### 特征降维
#%%
# 创建列表,由(eigenvalue, eigenvector)元组构成
eigen_pairs = [(np.abs(eigen_vals[i]), eigen_vecs[:, i])
for i in range(len(eigen_vals))]
#%%
# 按特征值从大到小对列表(eigenvalue, eigenvector)排序
eigen_pairs.sort(key=lambda k: k[0], reverse=True)
#%%
# 特征值与特征向量
eigen_pairs
#%%
# 取前两个特征值对应的特征向量作为主要成分
w = np.hstack((eigen_pairs[0][1][:, np.newaxis],
eigen_pairs[1][1][:, np.newaxis]))
#%%
w
#%%
# 原始特征(以第一个样本为例)
X_train_std[0]
#%%
# 特征压缩后结果
X_train_std[0].dot(w)
#%%
# 全部特征压缩
X_train_pca = X_train_std.dot(w)
#%%
# 特征压缩后结果展示
colors = ['r', 'b', 'g']
markers = ['s', 'x', 'o']
for l, c, m in zip(np.unique(y_train), colors, markers):
# 按照样本的真实值进行展示
plt.scatter(X_train_pca[y_train == l, 0],
X_train_pca[y_train == l, 1],
c=c, label=l, marker=m)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()
#%% md
## 使用sklearn实现PCA
#%% md
sklearn中提供了进行PCA的API
#%% md
### 特征值计算
#%%
# 实例化pca,保留所有特征
pca = PCA()
#%%
# 特征提取
X_train_pca = pca.fit_transform(X_train_std)
# 特征值结果
pca.explained_variance_ratio_
#%%
# 特征值绘制
# 绘制图像
plt.figure()
plt.bar(range(1, 14), pca.explained_variance_ratio_, alpha=0.5, align='center',
label='特征值分布')
plt.step(range(1, 14), np.cumsum(pca.explained_variance_ratio_), where='mid',
label='累计特征值')
plt.ylabel('特征值比例')
plt.xlabel('特征index')
plt.legend(loc='best')
#%% md
### 特征降维
#%%
# 压缩到二维特征
pca = PCA(n_components=2)
#%%
# 对训练数据进行处理
X_train_pca = pca.fit_transform(X_train_std)
#%%
# 特征值结果(只保留两个特征)
print(pca.explained_variance_ratio_)
#%%
# 对测试集数据进行处理
X_test_pca = pca.transform(X_test_std)
#%%
# 特征降维后结果展示
colors = ['r', 'b', 'g']
markers = ['s', 'x', 'o']
for l, c, m in zip(np.unique(y_train), colors, markers):
# 按照样本的真实值进行展示
plt.scatter(X_train_pca[y_train == l, 0],
X_train_pca[y_train == l, 1],
c=c, label=l, marker=m)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()
#%% md
## 利用逻辑回归进行分类
#%% md
### 绘制函数
#%%
# 绘制样本及其目标值
def plot_decision_regions(X, y, classifier, resolution=0.02):
"""
X:样本特征值
y:目标值
classifier: 分类器
"""
# 设置图像的标记及颜色
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])
# 利用样本点创建meshgrid
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
np.arange(x2_min, x2_max, resolution))
# 预测结果
Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
Z = Z.reshape(xx1.shape)
# 绘制预测结果的等高线
plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())
# 绘制样本点,并根据真实值进行着色
for idx, cl in enumerate(np.unique(y)):
# 绘制散点图
plt.scatter(x=X[y == cl, 0],
y=X[y == cl, 1],
alpha=0.6,
c=cmap(idx),
edgecolor='black',
marker=markers[idx],
label=cl)
#%% md
### PCA特征降维
#%%
# 利用PCA进行特征降维(提取)
# 保留两维特征
pca = PCA(n_components=2)
# 训练集数据处理
X_train_pca = pca.fit_transform(X_train_std)
# 测试集数据处理
X_test_pca = pca.transform(X_test_std)
#%% md
### LR分类器
#%%
# 实例化
lr = LogisticRegression()
# 模型训练
lr = lr.fit(X_train_pca, y_train)
#%% md
### 训练数据结果
#%%
plot_decision_regions(X_train_pca, y_train, classifier=lr)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()
#%% md
### 测试数据结果
#%%
plot_decision_regions(X_test_pca, y_test, classifier=lr)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()
#%% md
# LDA(Linear discriminant analysis)
#%% md
## LDA实现
#%%
# 精度设置,浮点数
np.set_printoptions(precision=4)
#%% md
### 平均向量
#%%
# 计算每一类数据的平均向量
mean_vecs = []
for label in range(1, 4):
mean_vecs.append(np.mean(X_train_std[y_train == label], axis=0))
print('MV %s: %s\n' % (label, mean_vecs[label - 1]))
#%% md
### 类内散度矩阵Sw
#%%
# 特征维度
d = 13
S_W = np.zeros((d, d))
# 获取每个类别的平均值向量
for label, mv in zip(range(1, 4), mean_vecs):
# 每一类别的散度矩阵
class_scatter = np.zeros((d, d))
for row in X_train_std[y_train == label]:
# 列向量
row, mv = row.reshape(d, 1), mv.reshape(d, 1)
class_scatter += (row - mv).dot((row - mv).T)
# 每个类别散度矩阵之和
S_W += class_scatter
#%% md
### 类间散度矩阵SB
#%%
# 全局平均值
mean_overall = np.mean(X_train_std, axis=0)
# 特征维度
d = 13
S_B = np.zeros((d, d))
# 获取每个类别的平均值
for i, mean_vec in enumerate(mean_vecs):
n = X_train[y_train == i + 1, :].shape[0]
# 列向量
mean_vec = mean_vec.reshape(d, 1)
mean_overall = mean_overall.reshape(d, 1)
# 类间散度矩阵
S_B += n * (mean_vec - mean_overall).dot((mean_vec - mean_overall).T)
#%% md
### 特征值计算
#%% md
求解矩阵 $S_W^{-1}S_B$的特征值和特征向量
#%%
# 计算LDA的特征值
eigen_vals, eigen_vecs = np.linalg.eig(np.linalg.inv(S_W).dot(S_B))
#%%
eigen_vals
#%% md
### 特征值分布
#%%
# 创建由特征值和特征向量组成的list
eigen_pairs = [(np.abs(eigen_vals[i]), eigen_vecs[:, i])
for i in range(len(eigen_vals))]
# 根据特征值从大到小排序(eigenvalue, eigenvector)
eigen_pairs = sorted(eigen_pairs, key=lambda k: k[0], reverse=True)
# 特征值结果
for eigen_val in eigen_pairs:
print(eigen_val[0])
#%%
# 实部求和
tot = sum(eigen_vals.real)
# 计算比例
discr = [(i / tot) for i in sorted(eigen_vals.real, reverse=True)]
# 累计求和
cum_discr = np.cumsum(discr)
plt.bar(range(1, 14), discr, alpha=0.5, align='center',
label='"区分度"分布')
plt.step(range(1, 14), cum_discr, where='mid',
label='累计"区分度"')
plt.ylabel('"区分度" 比例')
plt.xlabel('特征维度')
plt.ylim([-0.1, 1.1])
plt.legend(loc='best')
plt.show()
#%% md
### 特征降维
#%%
# 保留两维特征
w = np.hstack((eigen_pairs[0][1][:, np.newaxis].real,
eigen_pairs[1][1][:, np.newaxis].real))
#%%
w
#%%
# 特征降维
X_train_lda = X_train_std.dot(w)
#%%
# 降维前
X_test_std[0]
#%%
# 降维后
X_train_lda[0]
#%%
# 结果绘制
colors = ['r', 'b', 'g']
markers = ['s', 'x', 'o']
for l, c, m in zip(np.unique(y_train), colors, markers):
plt.scatter(X_train_lda[y_train == l, 0],
X_train_lda[y_train == l, 1] * (-1),
c=c, label=l, marker=m)
plt.xlabel('LD 1')
plt.ylabel('LD 2')
plt.legend(loc='lower right')
plt.show()
#%% md
## 使用sklearn实现LDA并进行LR分类
#%%
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
# 实例化
lda = LDA(n_components=2)
#%%
# 对训练数据进行LDA处理
X_train_lda = lda.fit_transform(X_train_std, y_train)
#%%
X_train_lda[0]
#%%
# 实例化逻辑回归
lr = LogisticRegression()
# 训练
lr = lr.fit(X_train_lda, y_train)
#%%
# 训练数据结果
plot_decision_regions(X_train_lda, y_train, classifier=lr)
plt.xlabel('LD 1')
plt.ylabel('LD 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()
#%%
# 测试数据结果
X_test_lda = lda.transform(X_test_std)
plot_decision_regions(X_test_lda, y_test, classifier=lr)
plt.xlabel('LD 1')
plt.ylabel('LD 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()
#%% md
# kernel PCA
#%% md
对于线性不可分的数据,在降维时可以使用带核函数的PCA
#%%
from sklearn.decomposition import KernelPCA
from sklearn.datasets import make_moons
# 数据生成
X, y = make_moons(n_samples=100, random_state=123)
# 实例化
scikit_kpca = KernelPCA(n_components=2, kernel='rbf', gamma=15)
# 数据处理
X_skernpca = scikit_kpca.fit_transform(X)
# 结果绘制
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(7, 3))
ax[0].scatter(X[:,0],X[:,1],marker='o',c=y)
ax[1].scatter(X_skernpca[y == 0, 0], X_skernpca[y == 0, 1],
color='red', marker='^', alpha=0.5)
ax[1].scatter(X_skernpca[y == 1, 0], X_skernpca[y == 1, 1],
color='blue', marker='o', alpha=0.5)
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.show()
#%%