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:  # setting 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:  # setting 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:  # setting the threshold as 20%
                print(i,j,corr[i][j])
                index_column.remove(j)
    new_columns = index_column
    print(new_columns)
    return new_columns

如上表所示,示例数据集中不存在高相关变量,但通常情况下,如果一对变量之间的相关性大于0.5-0.6,那就应该考虑是否要删除一列了。

4. 随机森林(Random Forest)

随机森林是一种广泛使用的特征选择算法,它会自动计算各个特征的重要性,所以无需单独编程。这有助于我们选择较小的特征子集。

在开始降维前,我们先把数据转换成数字格式,因为随机森林只接受数字输入。可以直接使用SelectFromModel,它根据权重的重要性选择特征。

from sklearn.ensemble 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_selection import RFE
from sklearn import datasets
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_selection 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, len(df.columns) - 1):
        if ffs[0][i] >= 10:
            new_columns.append(df.columns[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=False)
    # 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)
    print(pca.explained_variance_ratio_)  #输出贡献率
    print(pca.explained_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.inverse_transform(newdf)
    return tran_df

其中n_components将决定转换数据中的主成分。接下来,我们看一下这四个主成分解释了多少方差:

plt.plot(range(7), pca.explained_variance_ratio_)
plt.plot(range(7), np.cumsum(pca.explained_variance_ratio_))
plt.title("Component-wise and Cumulative Explained Variance")

在上图中,蓝线表示分量解释的方差,而橙线表示累积解释的方差。我们只用四个成分就解释了数据集中约90%的方差。
在这里插入图片描述

9. 独立分量分析(ICA)

独立分量分析(ICA)基于信息理论,是最广泛使用的降维技术之一。PCA和ICA之间的主要区别在于,PCA寻找不相关的因素,而ICA寻找独立因素。

如果两个变量不相关,它们之间就没有线性关系。如果它们是独立的,它们就不依赖于其他变量。例如,一个人的年龄和他吃了什么/看了什么电视无关。

该算法假设给定变量是一些未知潜在变量的线性混合。它还假设这些潜在变量是相互独立的,即它们不依赖于其他变量,因此它们被称为观察数据的独立分量。

下图是ICA和PCA的一个直观比较:
在这里插入图片描述
(a)PCA,(b)ICA
PCA的等式是x = Wχ。
这里,
x是观察结果
W是混合矩阵
χ是来源或独立分量
现在我们必须找到一个非混合矩阵,使分量尽可能独立。而测试分量独立性最常用的方法是非高斯性:

根据中心极限定理(Central Limit Theorem),多个独立随机变量混合之后会趋向于正态分布(高斯分布)。
在这里插入图片描述
因此,我们可以寻找所有独立分量中能最大化峰度的分量。
一旦峰度被最大化,整个分布会呈现非高斯分布,我们也能得到独立分量。

在Python中实现ICA:

from sklearn.decomposition import FastICA
#独立分量分析(ICA)PCA和ICA之间的主要区别在于,PCA寻找不相关的因素,而ICA寻找独立因素。
def ICADropDimension(data,index_column):
    df = data.loc[:, index_column]
    ICA = FastICA(n_components=7, random_state=12)
    newdf = ICA.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)  # 输出降维后的数据
    return tran_df

10. IOSMAP

代码:

from sklearn import manifold 
trans_data = manifold.Isomap(n_neighbors=5, n_components=3, n_jobs=-1).fit_transform(df[feat_cols][:6000].values)

使用的参数:

n_neighbors:决定每个点的相邻点数
n_components:决定流形的坐标数
n_jobs = -1:使用所有可用的CPU核心
可视化:

plt.figure(figsize=(12,8))
plt.title('Decomposition using ISOMAP')
plt.scatter(trans_data[:,0], trans_data[:,1])
plt.scatter(trans_data[:,1], trans_data[:,2])
plt.scatter(trans_data[:,2], trans_data[:,0])

在这里插入图片描述

11. t-SNE

代码:

from sklearn.manifold import TSNE 
tsne = TSNE(n_components=3, n_iter=300).fit_transform(df[feat_cols][:6000].values)

可视化:

plt.figure(figsize=(12,8))
plt.title('t-SNE components')
plt.scatter(tsne[:,0], tsne[:,1])
plt.scatter(tsne[:,1], tsne[:,2])
plt.scatter(tsne[:,2], tsne[:,0])

在这里插入图片描述

12. UMAP

代码:

import umap
umap_data = umap.UMAP(n_neighbors=5, min_dist=0.3, n_components=3).fit_transform(df[feat_cols][:6000].values)

这里,

n_neighbors:确定相邻点的数量。
min_dist:控制允许嵌入的紧密程度,较大的值可确保嵌入点的分布更均匀。
可视化:

plt.figure(figsize=(12,8))
plt.title('Decomposition using UMAP')
plt.scatter(umap_data[:,0], umap_data[:,1])
plt.scatter(umap_data[:,1], umap_data[:,2])
plt.scatter(umap_data[:,2], umap_data[:,0])

在这里插入图片描述

总结

到目前为止,我们已经介绍了12种降维/特征选择方法,考虑到篇幅,我们没有仔细编译后三种方法的原理,感兴趣的读者可以找资料查阅,因为它们中的任何一个都足够写一篇专门介绍的长文。本节会对这12种方法做一个总结,简要介绍它们的优点和缺点。

缺失值比率:如果数据集的缺失值太多,我们可以用这种方法减少变量数。
低方差滤波:这个方法可以从数据集中识别和删除常量变量,方差小的变量对目标变量影响不大,所以可以放心删去。
高相关滤波:具有高相关性的一对变量会增加数据集中的多重共线性,所以用这种方法删去其中一个是有必要的。
随机森林:这是最常用的降维方法之一,它会明确算出数据集中每个特征的重要性。
前向特征选择和反向特征消除:这两种方法耗时较久,计算成本也都很高,所以只适用于输入变量较少的数据集。
因子分析:这种方法适合数据集中存在高度相关的变量集的情况。
PCA:这是处理线性数据最广泛使用的技术之一。
ICA:我们可以用ICA将数据转换为独立的分量,使用更少的分量来描述数据。
ISOMAP:适合非线性数据处理。
t-SNE:也适合非线性数据处理,相较上一种方法,这种方法的可视化更直接。
UMAP:适用于高维数据,与t-SNE相比,这种方法速度更快。

参考
Python实现PCA降维
降维算法

  • 11
    点赞
  • 129
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值