专栏 | 基于 Jupyter 的特征工程手册:特征选择(一)

2022-01-14 11:39:40 浏览数 (1)

作者:陈颖祥、杨子晗

编译:AI有道

数据预处理后,我们生成了大量的新变量(比如独热编码生成了大量仅包含0或1的变量)。但实际上,部分新生成的变量可能是多余:一方面它们本身不一定包含有用的信息,故无法提高模型性能;另一方面过这些多余变量在构建模型时会消耗大量内存和计算能力。因此,我们应该进行特征选择并选择特征子集进行建模。

项目地址:

https://github.com/YC-Coder-Chen/feature-engineering-handbook

本文将介绍特征工程第一种算法:Filter Methods 过滤法(上)。

目录:

1.1 Filter Methods 过滤法

过滤法通过使用一些统计量或假设检验结果为每个变量打分。得分较高的功能往往更加重要,因此应被包含在子集中。以下为一个简单的基于过滤法的机器学习工作流(以最简单的训练-验证-测试这种数据集划分方法为例)。

1.1.1 Univariate Filter Methods 单变量特征过滤

单变量过滤方法依据单变量统计量或统计检验选择最佳特征。其仅仅考虑单个变量与目标变量的关系(方差选择法仅基于单个变量)。

1.1.1.1 Variance Threshold 方差选择法

方差选择法删除变量方差低于某个阈值的所有特征。例如,我们应删除方差为零的特征(所有观测点中具有相同值的特征),因为该特征无法解释目标变量的任何变化。

代码语言:javascript复制
import numpy as np
import pandas as pd
from sklearn.feature_selection import VarianceThreshold


# 合成一些数据集用于演示
train_set = np.array([[1,2,3],[1,4,7],[1,4,9]]) # 可见第一个变量方差为0
# array([[1, 2, 3],
#        [1, 4, 7],
#        [1, 4, 9]])


test_set = np.array([[3,2,3],[1,2,7]]) # 故意将第二个变量方差设为0
# array([[3, 2, 3],
#        [1, 2, 7]])


selector = VarianceThreshold()
selector.fit(train_set) # 在训练集上训练
transformed_train = selector.transform(train_set) # 转换训练集
# 下面为返回结果,可见第一个变量已被删除
# array([[2, 3],
#        [4, 7],
#        [4, 9]])


transformed_test = selector.transform(test_set) # 转换测试集
# 下面为返回结果,可见第一个变量已被删除
# array([[2, 3],
#        [2, 7]])
# 虽然测试集中第二个变量的方差也为0
# 但是我们的选择是基于训练集,所以我们依然删除第一个变量

1.1.1.2 Pearson Correlation (regression problem) 皮尔森相关系数 (回归问题)

皮尔森相关系数一般用于衡量两个连续变量之间的线性相关性,也可以用于衡量二元变量与目标变量的相关性。故可以将类别变量利用独热编码转换为多个二元变量之后利用皮尔森相关系数进行筛选。

公式:

代码语言:javascript复制
import numpy as np
from scipy.stats import pearsonr
from sklearn.feature_selection import SelectKBest


# 直接载入数据集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 数据集来演示
# 此数据集中,X,y均为连续变量,故此满足使用皮尔森相关系数的条件


# 选择前15000个观测点作为训练集
# 剩下的作为测试集
train_set = X[0:15000,:]
test_set = X[15000:,]
train_y = y[0:15000]


# sklearn 中没有直接的方程可以使用
# 此处将用 scipy.stats.pearsonr方程来实现基于皮尔森相关系数的特征过滤
# 注意 scipy.stats.pearsonr 计算的是两个变量之间的相关系数
# 因sklearn SelectKBest需要,我们将基于scipy.stats.pearsonr 重写允许多特征同时输入的方程 udf_pearsonr


def udf_pearsonr(X, y):
    # 将会分别计算每一个变量与目标变量的关系
    result = np.array([pearsonr(x, y) for x in X.T]) # 包含(皮尔森相关系数, p值) 的列表
    return np.absolute(result[:,0]), result[:,1]


# SelectKBest 将会基于一个判别方程自动选择得分高的变量
# 这里的判别方程为皮尔森相关系数
selector = SelectKBest(udf_pearsonr, k=2) # k => 我们想要选择的变量数
selector.fit(train_set, train_y) # 在训练集上训练
transformed_train = selector.transform(train_set) # 转换训练集
transformed_train.shape #(15000, 2), 其选择了第一个及第七个变量 
assert np.array_equal(transformed_train, train_set[:,[0,6]])


transformed_test = selector.transform(test_set) # 转换测试集
assert np.array_equal(transformed_test, test_set[:,[0,6]]);
# 可见对于测试集,其依然选择了第一个及第七个变量
代码语言:javascript复制
# 验算一下我们的结果
for idx in range(train_set.shape[1]):
    pea_score, p_value = pearsonr(train_set[:,idx], train_y)
    print(f"第{idx   1}个变量和目标的皮尔森相关系数的绝对值为{round(np.abs(pea_score),2)}, p-值为{round(p_value,3)}")
# 应选择第一个及第七个变量
代码语言:javascript复制
第1个变量和目标的皮尔森相关系数的绝对值为0.7, p-值为0.0
第2个变量和目标的皮尔森相关系数的绝对值为0.07, p-值为0.0
第3个变量和目标的皮尔森相关系数的绝对值为0.14, p-值为0.0
第4个变量和目标的皮尔森相关系数的绝对值为0.04, p-值为0.0
第5个变量和目标的皮尔森相关系数的绝对值为0.02, p-值为0.011
第6个变量和目标的皮尔森相关系数的绝对值为0.05, p-值为0.0
第7个变量和目标的皮尔森相关系数的绝对值为0.23, p-值为0.0
第8个变量和目标的皮尔森相关系数的绝对值为0.08, p-值为0.0

1.1.1.3 Distance Correlation (regression problem) 距离相关系数 (回归问题)

与皮尔森相关系数类似,距离相关系数也一般被用于衡量两个连续变量之间的相关性。但与皮尔森相关系数不同的是,距离相关系数还衡量了两个变量之间的非线性关联。

公式:

首先,计算(n x n)距离矩阵dX。dX中的每一个元素为???????????????? 。类似的,我们也可以计算距离矩阵dY,其中dY中的每个元素为????????????????。????????????????是为观测点i与观测点j之间的距离:

其次,我们计算如下双中心距离并更新距离矩阵。其中, ????????¯ 为距离矩阵dX的第i行平均值, ????????¯ 为距离矩阵dX的第j列的平均值, ∑????????∑???????????????????????? 为全局平均值:

随后,我们便可以算出样本距离协方差及距离方差:

最后,距离相关系数 ????????????????(????,????) 便为如下:

代码语言:javascript复制
import numpy as np
from dcor import distance_correlation
from dcor.independence import distance_covariance_test
from sklearn.feature_selection import SelectKBest


# 直接载入数据集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 数据集来演示
# 此数据集中,X,y均为连续变量,故此满足使用距离相关系数的条件


# 选择前15000个观测点作为训练集
# 剩下的作为测试集
train_set = X[0:15000,:]
test_set = X[15000:,]
train_y = y[0:15000]


# sklearn 中没有直接的方程可以使用
# 此处将用 dcor.distance_correlation方程来实现基于距离相关系数的特征过滤
# 注意 dcor.distance_correlation 计算的是两个变量之间的相关系数
# 因sklearn SelectKBest需要,我们将基于dcor.distance_correlation 重写允许多特征同时输入的方程 udf_dcorr


def udf_dcorr(X, y):
    # 将会分别计算每一个变量与目标变量的关系
    result = np.array([[distance_correlation(x, y), 
                        distance_covariance_test(x,y)[0]]for x in X.T]) # 包含(距离相关系数, p值) 的列表
    return result[:,0], result[:,1]


# SelectKBest 将会基于一个判别方程自动选择得分高的变量
# 这里的判别方程为距离相关系数
selector = SelectKBest(udf_dcorr, k=2) # k => 我们想要选择的变量数
selector.fit(train_set, train_y) # 在训练集上训练
transformed_train = selector.transform(train_set) # 转换训练集
transformed_train.shape #(15000, 2), 其选择了第一个及第三个变量 
assert np.array_equal(transformed_train, train_set[:,[0,2]])


transformed_test = selector.transform(test_set) # 转换测试集
assert np.array_equal(transformed_test, test_set[:,[0,2]]);
# 可见对于测试集,其依然选择了第一个及第三个变量
代码语言:javascript复制
# 验算一下我们的结果
for idx in range(train_set.shape[1]):
    d_score = distance_correlation(train_set[:,idx], train_y)
    p_value = distance_covariance_test(train_set[:,idx], train_y)[0]
    print(f"第{idx   1}个变量和目标的距离相关系数为{round(d_score,2)}, p-值为{round(p_value,3)}")
# 应选择第一个及第三个变量
代码语言:javascript复制
第1个变量和目标的距离相关系数为0.66, p-值为1.0
第2个变量和目标的距离相关系数为0.07, p-值为1.0
第3个变量和目标的距离相关系数为0.31, p-值为1.0
第4个变量和目标的距离相关系数为0.12, p-值为1.0
第5个变量和目标的距离相关系数为0.08, p-值为1.0
第6个变量和目标的距离相关系数为0.29, p-值为1.0
第7个变量和目标的距离相关系数为0.25, p-值为1.0
第8个变量和目标的距离相关系数为0.19, p-值为1.0

1.1.1.4 F-Score (regression problem) F-统计量 (回归问题)

F统计量(F-Score)用于检验线性回归模型的整体显著性。在sklearn中,其将对每一个变量分别建立一个一元的线性回归模型,然后分别报告每一个对应模型的F统计量。F-统计量的零假设是该线性模型系数不显著,在一元模型中,该统计量能够反映各变量与目标变量之间的线性关系。因此,我们应该选择具有较高F统计量的特征(更有可能拒绝原假设)。

公式:

SST为总平方和,SSR为回归平方和,p为线性回归自变量数(包括常数项,故在上述的一元线性模型中,p=2), ???? 为自变量与因变量的线性相关系数,n为总观测数。因上述线性模型为一元线性模型,故可证 ????2=????2 。

代码语言:javascript复制
import numpy as np
from sklearn.feature_selection import f_regression
from sklearn.feature_selection import SelectKBest


# 直接载入数据集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 数据集来演示
# 此数据集中,X,y均为连续变量,故此满足使用F统计量的条件


# 选择前15000个观测点作为训练集
# 剩下的作为测试集
train_set = X[0:15000,:]
test_set = X[15000:,]
train_y = y[0:15000]


# sklearn 中直接提供了方程用于计算F统计量
# SelectKBest 将会基于一个判别方程自动选择得分高的变量
# 这里的判别方程为F统计量
selector = SelectKBest(f_regression, k=2) # k => 我们想要选择的变量数
selector.fit(train_set, train_y) # 在训练集上训练
transformed_train = selector.transform(train_set) # 转换训练集
transformed_train.shape #(15000, 2), 其选择了第一个及第七个变量 
assert np.array_equal(transformed_train, train_set[:,[0,6]])


transformed_test = selector.transform(test_set) # 转换测试集
assert np.array_equal(transformed_test, test_set[:,[0,6]]);
# 可见对于测试集,其依然选择了第一个及第七个变量
代码语言:javascript复制
# 验算一下我们的结果
for idx in range(train_set.shape[1]):
    score, p_value = f_regression(train_set[:,idx].reshape(-1,1), train_y)
    print(f"第{idx   1}个变量的F统计量为{round(score[0],2)}, p-值为{round(p_value[0],3)}")
# 故应选择第一个及第七个变量
代码语言:javascript复制
第1个变量的F统计量为14111.79, p-值为0.0
第2个变量的F统计量为71.99, p-值为0.0
第3个变量的F统计量为317.04, p-值为0.0
第4个变量的F统计量为23.93, p-值为0.0
第5个变量的F统计量为6.54, p-值为0.011
第6个变量的F统计量为35.93, p-值为0.0
第7个变量的F统计量为846.61, p-值为0.0
第8个变量的F统计量为98.06, p-值为0.0

专栏系列:

专栏 | 基于 Jupyter 的特征工程手册:数据预处理(一)

专栏 | 基于 Jupyter 的特征工程手册:数据预处理(二)

专栏 | 基于 Jupyter 的特征工程手册:数据预处理(三)

专栏 | 基于 Jupyter 的特征工程手册:数据预处理(四)

目前该项目完整中文版正在制作中,请持续关注哦~

中文版 Jupyter 地址:

https://github.com/YC-Coder-Chen/feature-engineering-handbook/blob/master/中文版/2. 特征选择.ipynb

0 人点赞