12种降维⽅法及python实现
你遇到过特征超过1000个的数据集吗?超过5万个的呢?我遇到过。降维是⼀个⾮常具有挑战性的任务,尤其是当你不知道该从哪⾥开始的时候。拥有这么多变量既是⼀个恩惠——数据量越⼤,分析结果越可信;也是⼀种诅咒——你真的会感到⼀⽚茫然,⽆从下⼿。
⾯对这么多特征,在微观层⾯分析每个变量显然不可⾏,因为这⾄少要⼏天甚⾄⼏个⽉,⽽这背后的时间成本是难以估计的。为此,我们需要⼀种更好的⽅法来处理⾼维数据,⽐如本⽂介绍的降维:⼀种能在减少数据集中特征数量的同时,避免丢失太多信息并保持/改进模型性能的⽅法。
什么是降维?
每天,我们都会⽣成⼤量数据,⽽事实上,现在世界上约90%的数据都是在过去3到4年中产⽣的,这是个令⼈难以置信的现实。下⾯是收集数据的⼏个⽰例:
Facebook会收集你喜欢、分享、发布、访问的内容等数据,⽐如你喜欢哪家餐厅。
智能⼿机中的各类应⽤会收集⼤量关于你的个⼈信息,⽐如你所在的地点。
淘宝会收集你在其⽹站上购买、查看、点击的内容等数据。
赌场会跟踪每位客户的每⼀步⾏动。
随着数据的⽣成和数据收集量的不断增加,可视化和绘制推理图变得越来越困难。⼀般情况下,我们经常会通过绘制图表来可视化数据,⽐如假设我们⼿头有两个变量,⼀个年龄,⼀个⾝⾼。我们就可以绘制散点图或折线图,轻松反映它们之间的关系。
下图是⼀个简单的例⼦:
横坐标X1的单位为“千克”,纵坐标X2的单位为“磅”。可以发现,虽然是两个变量,但它们传达的信息是⼀致的,即物体的重量。所以我们只需选⽤其中的⼀个就能保留原始意义,把2维数据压缩到1维(
Y1)后,上图就变成:
类似地,我们可以把数据从原本的p维转变为⼀系列k维的⼦集(k<<n),这就是降维。
为什么要降维?
以下是在数据集中应⽤降维的⽤处:
1. 随着数据维度不断降低,数据存储所需的空间也会随之减少。
2. 低维数据有助于减少计算/训练⽤时。
3. ⼀些算法在⾼维度数据上容易表现不佳,降维可提⾼算法可⽤性。
4. 降维可以⽤删除冗余特征解决多重共线性问题。⽐如我们有两个变量:“⼀段时间内在跑步机上的耗时”和“卡路⾥消耗量”。这两
个变量⾼度相关,在跑步机上花的时间越长,燃烧的卡路⾥⾃然就越多。因此,同时存储这两个数据意义不⼤,只需⼀个就够了。5. 降维有助于数据可视化。如前所述,如果数据维度很⾼,可视化会变
得相当困难,⽽绘制⼆维三维数据的图表⾮常简单。
降维技术⼀览
数据维度的降低⽅法主要有两种:
1. 仅保留原始数据集中最相关的变量(特征选择)。
2. 寻找⼀组较⼩的新变量,其中每个变量都是输⼊变量的组合,包含与输⼊变量基本相同的信息(降维)。
1. 缺失值⽐率(Missing Value Ratio)
假设你有⼀个数据集,你第⼀步会做什么?在构建模型前,对数据进⾏探索性分析必不可少。但在浏览数据的过程中,有时候我们会发现其中包含不少缺失值。如果缺失值少,我们可以填补缺失值或直接删除这个变量;如果缺失值过多,你会怎么办呢?
当缺失值在数据集中的占⽐过⾼时,⼀般我会选择直接删除这个变量,因为它包含的信息太少了。但具体删不删、怎么删需要视情况⽽定,我们可以设置⼀个阈值,如果缺失值占⽐⾼于阈值,删除它所在的列。阈值越⾼,降维⽅法越积极。
下⾯是具体代码:
# 导⼊需要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#缺失值⽐率(Missing Value Ratio),过滤指标,传⼊数据和需要计算的列
def MissingValueRatio(data,index_column):
# #计算空值率
null_ratio = data.loc[:, index_column].isnull().sum() / len(data) * 100
new_columns = []
for i in index_column:
if null_ratio[i] <= 20: # tting the threshold as 20%
new_columns.append(i)
print(null_ratio)
print(new_columns)
#计算值=0的率
# zero_ratios = {}
# for act_column in index_column:
# #计算每列中每个值出现次数,获取为0的次数tt[0.0],计算出现率
# tt = data[act_column].value_counts()
# zero_ratio = tt[0.0] / len(data) * 100
# zero_ratios[act_column] = zero_ratio
# print(zero_ratios)
# new_columns = []
# for k, v in zero_ratios.items():
# if v < 20:
# new_columns.append(k)
# print(new_columns)
return new_columns
2. 低⽅差滤波(Low Variance Filter)
如果我们有⼀个数据集,其中某列的数值基本⼀致,也就是它的⽅差⾮常低,那么这个变量还有价值吗?和上⼀种⽅法的思路⼀致,我们通常认为低⽅差变量携带的信息量也很少,所以可以把它直接删除。
放到实践中,就是先计算所有变量的⽅差⼤⼩,然后删去其中最⼩的⼏个。需要注意的⼀点是:⽅差
与数据范围相关的,因此在采⽤该⽅法前需要对数据做归⼀化处理。
#低⽅差滤波(Low Variance Filter),过滤指标,传⼊数据和需要计算的列,其中某列的数值基本⼀致,也就是它的⽅差⾮常低
def LowVarianceFilter(data,index_column):
#计算每个指标的⽅差
var = data.loc[:, index_column].var()
new_columns = []
for i in index_column:
if var[i] > 10: # tting the threshold as 20%
new_columns.append(i)
print(var)
print(new_columns)
return new_columns
以上代码帮我们列出了⽅差⼤于10的所有变量。
3. ⾼相关滤波(High Correlation filter)
如果两个变量之间是⾼度相关的,这意味着它们具有相似的趋势并且可能携带类似的信息。同理,这类变量的存在会降低某些模型的性能(例如线性和逻辑回归模型)。为了解决这个问题,我们可以计算独⽴数值变量之间的相关性。如果相关系数超过某个阈值,就删除其中⼀个变量。
作为⼀般准则,我们应该保留那些与⽬标变量显⽰相当或⾼相关性的变量。
#⾼相关滤波(High Correlation filter),过滤指标,传⼊数据喝需要计算的列,如果两个变量之间是⾼度相关的,这意味着它们具有相似的趋势并且可能携带类似的信息。
def HighCorrelationFilter(data,index_column):
#计算每个指标的⽅差
corr = data.loc[:, index_column].corr()
print(corr)
for i in index_column:
for j in index_column:
if corr[i][j] > 0.5 and i != j: # tting the threshold as 20%
print(i,j,corr[i][j])
ve(j)
new_columns = index_column
print(new_columns)
return new_columns
如上表所⽰,⽰例数据集中不存在⾼相关变量,但通常情况下,如果⼀对变量之间的相关性⼤于0.5-0.6,那就应该考虑是否要删除⼀列了。
4. 随机森林(Random Forest)
随机森林是⼀种⼴泛使⽤的特征选择算法,它会⾃动计算各个特征的重要性,所以⽆需单独编程。这有助于我们选择较⼩的特征⼦集。
在开始降维前,我们先把数据转换成数字格式,因为随机森林只接受数字输⼊。可以直接使⽤SelectFromModel,它根据权重的重要性选择特征。
ble import RandomForestRegressor
# 随机森林(Random Forest)
def RandomorestropDimension(data,index_column,score_column):
df = data.loc[:, index_column]
score_df = data.loc[:, score_column]
model = RandomForestRegressor(random_state=1, max_depth=10)
model.fit(df, score_df)
features = df.columns
print(features)
importances = model.feature_importances_ #特征重要性
print(importances)
#确定特征数,根据特征重要性排序筛选特征
feature = SelectFromModel(model)
fit = feature.fit_transform(df, score_df)
column_num = fit.shape[1]
#np.argsort(x) #按升序排列 np.argsort(-x) #按降序排列取索引
indices = np.argsort(-importances)
print(indices)
new_columns = []
for c in range(column_num):
new_columns.append(features[indices[c]])
print(new_columns)
return new_columns
5. 反向特征消除(Backward Feature Elimination)
以下是反向特征消除的主要步骤:
先获取数据集中的全部n个变量,然后⽤它们训练⼀个模型。
计算模型的性能。
在删除每个变量(n次)后计算模型的性能,即我们每次都去掉⼀个变量,⽤剩余的n-1个变量训练模型。
确定对模型性能影响最⼩的变量,把它删除。
重复此过程,直到不再能删除任何变量。
在构建线性回归或Logistic回归模型时,可以使⽤这种⽅法。
from sklearn.linear_model import LinearRegression
from sklearn.feature_lection import RFE
from sklearn import datats
lreg = LinearRegression()
rfe = RFE(lreg, 10)
rfe = rfe.fit_transform(df, train.Item_Outlet_Sales)
我们需要指定算法和要选择的特征数量,然后返回反向特征消除输出的变量列表。此外,rfe.ranking_可以⽤来检查变量排名。
6. 前向特征选择(Forward Feature Selection)
前向特征选择其实就是反向特征消除的相反过程,即找到能改善模型性能的最佳特征,⽽不是删除弱影响特征。它背后的思路如下所述:
1. 选择⼀个特征,⽤每个特征训练模型n次,得到n个模型。
2. 选择模型性能最佳的变量作为初始变量。
3. 每次添加⼀个变量继续训练,重复上⼀过程,最后保留性能提升最⼤的变量。
4. ⼀直添加,⼀直筛选,直到模型性能不再有明显提⾼。
from sklearn.feature_lection import f_regression
# 前向特征选择(Forward Feature Selection)前向特征选择其实就是反向特征消除的相反过程,即找到能改善模型性能的最佳特征
def ForwardFeatureSelection(data,index_column,score_column):
df = data.loc[:, index_column]
score_df = data.loc[:, score_column]
#F值越⼤,p值越⼩越好,这⾥我们选择F值⼤于10的变量
ffs = f_regression(df,score_df )
print(ffs)
new_columns = []
for i in range(0, lumns) - 1):
if ffs[0][i] >= 10:
new_columns.lumns[i])
print(new_columns)
return new_columns
上述代码会返回⼀个数组,其中包括变量F值和每个F对应的p值(什么是F值)。在这⾥,我们选择F值⼤于10的变量。
[注]:前向特征选择和反向特征消除耗时较久,计算成本也都很⾼,所以只适⽤于输⼊变量较少的数据集。
7. 因⼦分析(Factor Analysis)
因⼦分析是⼀种常见的统计⽅法,它能从多个变量中提取共性因⼦,并得到最优解。假设我们有两个变量:收⼊和教育。它们可能是⾼度相关的,因为总体来看,学历⾼的⼈⼀般收⼊也更⾼,反之亦然。所以它们可能存在⼀个潜在的共性因⼦,⽐如“能⼒”。
在因⼦分析中,我们将变量按其相关性分组,即特定组内所有变量的相关性较⾼,组间变量的相关性较低。我们把每个组称为⼀个因⼦,它是多个变量的组合。和原始数据集的变量相⽐,这些因⼦在数量上更少,但携带的信息基本⼀致。
⽤因⼦分析分解数据集:
from sklearn.decomposition import FactorAnalysis
# 因⼦分析(Factor Analysis),返回降维后的新数据
def FactorAnalysisFeatureSelection(data,index_column):
df = data.loc[:, index_column]
fa = FactorAnalysis(n_components=7) # 指定7个因⼦作为新变量
fa.fit(df)
tran_x = fa.transform(df)
factor_columns = []
for index in range(7):
tmp = "factor" + str(index + 1)
factor_columns.append(tmp)
tran_df = pd.DataFrame(tran_x, columns=factor_columns)
return tran_df
这⾥,n_components将决定转换数据中的因⼦数量。
8. 主成分分析(PCA)
如果说因⼦分析是假设存在⼀系列潜在因⼦,能反映变量携带的信息,那PCA就是通过正交变换将原始的n维数据集变换到⼀个新的被称做主成分的数据集中,即从现有的⼤量变量中提取⼀组新的变量。下⾯是关于PCA的⼀些要点:
主成分是原始变量的线性组合。
1. 第⼀个主成分具有最⼤的⽅差值。
2. 第⼆主成分试图解释数据集中的剩余⽅差,并且与第⼀主成分不相关(正交)。
3. 第三主成分试图解释前两个主成分等没有解释的⽅差。
实现PCA:
from sklearn.decomposition import PCA
#PCA数据降维
def PCADropDimension(data,index_column):
df = data.loc[:, index_column]
# PCA(copy=True, n_components=2, whiten=Fal)
# n_components: 我们可以利⽤此参数设置想要的特征维度数⽬,可以是int型的数字,也可以是阈值百分⽐,如95 %,让PCA类根据样本特征⽅差来降到合适的维数,也可以指定为string类型,MLE。
# copy: bool类型,TRUE或者FALSE,是否将原始数据复制⼀份,这样运⾏后原始数据值不会改变,默认为TRUE。
# whiten:bool类型,是否进⾏⽩化(就是对降维后的数据进⾏归⼀化,使⽅差为1),默认为FALSE。如果需要后续处理可以改为TRUE。
pca = decomposition.PCA(n_components=7) # n_components:⽬标维度
pca.fit(df)
plained_variance_ratio_) #输出贡献率
plained_variance_) # 输出⽅差值,⽅差值越⼤,表明越重要
print(pca.n_features_)
print(pca.n_features_in_)
#降维后数据
newdf = pca.fit_transform(df)
factor_columns = []
for index in range(7):
tmp = "factor" + str(index + 1)
factor_columns.append(tmp)
tran_df = pd.DataFrame(newdf, columns=factor_columns)
print(tran_df) #输出降维后的数据
#将降维后的数据转换成原始数据
# X = pca.inver_transform(newdf)
return tran_df
其中n_components将决定转换数据中的主成分。接下来,我们看⼀下这四个主成分解释了多少⽅差:
plt.plot(range(7), plained_variance_ratio_)
plt.plot(range(7), np.plained_variance_ratio_))
plt.title("Component-wi and Cumulative Explained Variance")