美文网首页
特征工程之入门总结

特征工程之入门总结

作者: 茶小美 | 来源:发表于2020-04-26 17:26 被阅读0次

    最近在做天池项目过程中,涉及到最重要的一步骤就是特征工程。本文旨在总结特征工程知识点,项目实战请移步:特征工程详解及实战项目(2)

    简单介绍特征工程

    特征工程是将数据转换成更好表示潜在问题的特征,提高机器学习的性能。
    所谓“数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已”。

    目录

      1. 数据预处理
        1.1 缺失值处理
        1.11 直接删除
        1.12 填补法:统计值填补,插值法,预测值填充
        1.2 异常值处理
        1.21 箱型图判别
        1.22 基于常识
        1.3 去除唯一性
        1.4 量钢化
        1.41 归一化min-max
        1.42 标准化z_score
        1.5 连续数据离散化
        1.51 二值化:设定阈值得到1或0
        1.52 分桶:等宽法,等频法
        1.6 类别数据处理
        1.61 序号编码label
        1.62 one-hot、哑变量get_dummies
      1. 特征构造
        2.1 统计值构造:单变量、多变量构造新特征,验证相关性
        2.2 函数变化法
      1. 特征提取
        3.1 过滤法
        3.11 方差过滤法
        3.12 相关系数
        3.13 卡方检验
        3.2 嵌入法
        3.3 包装法
      1. 类别不平衡
      1. 降维
        5.1 无监督之PCA主成分分析
      1. 交叉验证

    1.数据预处理

    1.1缺失值处理
    1.1.1直接删除

    如果某字段缺失值占比较大,对训练模型没有明显特征可言,可以选择直接删除该字段(只能删除训练集,慎用)
    如果缺失值较少可以删除记录,但是会把其他字段不缺失也一并删除掉,所以不建议使用

    python实现:df.dropna()
    
    1.1.2 填补
    • 统计值填补:用均值,中位数,众数等。或者可以用空值、自定义数值、向前向后填补。当特征具有强类别信息时,比如男女性别身高填补,要分类统计效果才会好
      注:由于众数可能存在多个,要考虑到这点
      注:向前后填补要考虑到第/最后一行为空值情况
    df.fillna(method=' ')
    
    • 插值法填补:在X个范围内求出一个值填至缺失值中
    def f(s,n,k=5):#s=df,n缺失值位置,k范围=5
        y = s[list(range(n-k,n+1+k))]#y求出缺失值前5后5的df数值
        y = y[y.notnull()]#确保里面没有nan
        return(lagrange(y.index,list(y))(n))#lagrange计算出第n个值
    
    for i in range(len(df)):
        if df.isnull()[i]:
            data[i] = f(df,i)
            print(f(df,i))   
    
    1.1.3预测值填充

    a.把需要填充的某列作为新特征列Label_A
    b.选出与Label_A相关性强的组成模型
    c.非缺失值作为训练集,缺失值作为测试集
    d.连续性数据用回归预测,离散型进行分类学习

    dataset['Label_A']=dataset['A']#a
    dataset.corr()#b
    x_trian,y_train,x_test,y_test = train_test_split(x,y)#c,x是b中相关性较强的特征列,y是结果列
    lr = LinearRegression()
    lr.fit(x_train, y_train)
    y_train_pred = lr.predict(x_train)
    print('>>>在训练集中的表现:', r2_score(y_train_pred, y_train))
    y_valid_pred = lr.predict(x_valid)
    print('>>>在验证集中的表现:', r2_score(y_valid_pred, y_valid))
    y_test_pred = lr.predict(test.iloc[:, :2])
    test.loc[:, 'Label_petal_length'] = y_test_pred
    df_no_nan = pd.concat([train, test], axis=0)
    
    1.2异常值处理
    • 基于常识,判定为异常值,比如:年龄不能有负数
    • 箱型图判断异常值
      IQR=Q3-Q1
      极端异常值上限>Q3+3IQR
      极端异常值下限<Q1-3IQR
      一般异常值上限>Q3+1.5IQR
      一般异常值下限<Q1-1.5IQR
    • 3西格玛方法:
      如果数据符合正态分布,那么缺失值被定义为值与均值之差的绝对值大于3倍标准差

    异常值处理方法一般是删除填补

    1.3去除唯一属性

    一般都是ID类属性不能反映样本特征情况,直接删除即可

    1.4 无量纲化
    • 归一化min-max
      x* = (x-x.min)/(x.max-x.min)
      又叫区间缩放法,把值缩放至[0,1]区间
    from sklearn.preprocessing import MinMaxScaler
    
    x_scaler = MinMaxScaler()
    y_scaler = MinMaxScaler()
    
    #特征归一化
    x_train_sca = x_scaler.fit_transform(X_train)
    x_test_sca = x_scaler.transform(X_test)
    y_train_sca = y_scaler.fit_transform(pd.DataFrame(y_train))
    
    
    • 标准化z_score
      x* = (x-x.mean)/σ
    from sklearn.preprocessing import StandardScaler
    
    #一般把train和test集放在一起做标准化,或者在train集上做标准化后,用同样的标准化器去标准化test集
    scaler = StandardScaler()
    train = scaler.fit_transform(train)
    test = scaler.transform(test)
    
    1.5 连续变量离散化

    需要离散化的原因:

    • 算法需要,如决策树,朴素贝叶斯是基于离散型数据
    • 离散化数据较连续型更容易被理解
    • 强鲁棒性,客服数据缺陷
    • 离散化后数据稳定性强,可以进行特征交叉,对于逻辑回归来说离散后每个特征具有权重,相当于为模型引入非线性
    二值化:设定阈值,大于阈值为1,反之为0
    from sklearn.preprocessing import Binarizer
    
    # 阈值自定义为 3.0
    # 大于阈值映射为 1,反之为 0
    b = Binarizer(threshold=3.0)  
    x_binarizer = b.fit_transform(df.iloc[:, :4])
    x_binarizer[:5]
    
    分桶

    *等宽:按照特征值划分pd.cut
    *等频:将样本数量等频数划分pd.qcut

    聚类划分

    KMeans无监督学习,定义k个簇不断增加样本值改变簇的位置,直到簇不再改变。

    # 聚类划分
    import seaborn as sns
    import numpy as np
    from sklearn.cluster import KMeans
    
    data = sns.load_dataset('iris')
    X = data.iloc[:,1]
    kmeans = KMeans(n_clusters=4)  # 离散为 4 等份
    kmeans.fit_transform(np.array(X).reshape(-1, 1))  # 只取一个特征进行聚类离散化
    
    1.6 类别数据处理
    • 序号编码,可以理解为打标签
    from sklearn.preprocessing import LabelEncoder
    
    le = LabelEncoder()
    x_le = le.fit_transform(data['species'])
    x_le
    
    • One-hot编码
    from sklearn import preprocessing
    x = np.array([[1,2,3],[3,4,6],[1,5,6]])
    l = preprocessing.OneHotEncoder()
    l.fit(x)
    y = np.array([3,2,6])
    print(l.transform(y.reshape(1,-1)).toarray())
    

    result: [[0. 1. 1. 0. 0. 0. 1.]]

    注:可能会造成特征灾害,需要用特征选择降维
    LR模型一般会用到连续数据离散化,再进行one-hot编码,使其具有非线性能力
    one-hot与哑变量的区别

    2. 特征构造

    属性分割结合是特征构造中常用的方法,可以尝试组合二个、三个不同的属性构造新的特征。如果存在时间相关性,可以得到同一属性下不同时间的特征。

    2.1 统计值构造

    比如:统计单个变量次数作为新特征,多变量统计值作为新特征

    # 多变量
    name = {'count': 'petal_width_count', 'min':'petal_width_min', 
            'max':'petal_width_max', 'mean':'petal_width_mean', 
            'std':'petal_width_std'}
    newF2 = df.groupby(by=['sepal_length'])['petal_width'].agg(['count', 'min', 'max', 'mean', 'std']).rename(columns=name).reset_index()
    df_newF2 = pd.merge(df, newF2, on='sepal_length', how='inner')
    # 由于聚合分组之后有一些样本的 std 会存在缺失值,所以统一填充为 0
    df_newF2['petal_width_std'] = df_newF2['petal_width_std'].fillna(0)
    df_newF2.head()
    

    验证相关性:

    df_newF2.corr()
    

    通过验证相关系数表可以发现,新构建的 5 个特征,除了 count、std 之外,其余 3 个特征跟目标相关性系数较高,
    可以初步认为这 3 个特征是有实际意义的特征,下面进行模型训练简单验证一下

    2.3 函数变化法

    包括:平方(小数值-大数值)、开方(大数值-小数值)、对数(非正态-正态分布)、指数、差分(常用于时间序列)

    2.31 对数法
    2.32 差分法
    plt.rcParams['font.sans-serif'] = ['SimHei']#避免中文乱码
    plt.rcParams['axes.unicode_minus'] = False#避免中文乱码
    
    y = np.array([1.3, 5.5, 3.3, 5.3, 3.4, 8.0, 6.6, 8.7, 6.8, 7.9])
    x = np.array(list(range(y.shape[0])))
    plt.figure(1,figsize = (5,3)),plt.title('原数据')
    plt.plot(x,y)
    #print('----log----')
    y_log = np.log(y)
    plt.figure(2,figsize = (5,3)),plt.title('log变换后数据')
    plt.plot(x,y_log)
    #print('----差分---')
    def diff(data):
        DIFF = []
        DIFF.append(data[0])
        for i in range(1,data.shape[0]):# 1 次差分
            value = data[i]-data[i-1]
            DIFF.append(value)
        for i in range(1,data.shape[0]):# 2 次差分
            value = DIFF[i]-DIFF[i-1]
            DIFF.append(value)
        x = list(range(1,len(DIFF)+1))
        plt.figure(3,figsize = (5,3)),plt.title('2次差分后数据')
        plt.plot(x,DIFF)
        return DIFF
    DIFF = diff(y)
    
    image.png

    从上面图像发现经过对数变换的数据明显比差分变换的效果更好,对数变换后的数据更加的平稳。

    2.33 算术运算法

    如featureA/featureB

    2.4 连续数据离散化

    见预处理部分

    2.5 离散数据编码

    见预处理部分

    3. 特征提取

    选取对模型预测重要性的特征,减少模型预测时间提高精确度。

    3.1 过滤法Filter

    单个特征目标特征的相关性。适用于离散型变量

    3.11 方差过滤法

    方差是与偏离均值的程度大小,方差越大离散程度越大,意味着该变量对模型贡献作用越明显,应该保留。

    # 调用 sklearn 模块 API 接口
    from sklearn.feature_selection import VarianceThreshold
    
    vt = VarianceThreshold(0.6)#设定阈值,默认为0
    x_vt = vt.fit_transform(df.iloc[:,:4])
    print(vt.variances_)#查看所有方差
    x_vt[:5]
    
    3.12 相关系数
    • 特征与特征之间的相关系数
      corr<0,代表负相关,互补特征;
      corr=0,代表无相关;
      corr>0,代表正相关,替代特征;
      corr的绝对值在0.9-1之间,就可以保留其中一个特征即可。

    • 目标与特征之间的相关系数及p值
      适用于回归问题的特征选择。先检验显著性,如果显著再看相关系数。

    # 调用 sklearn 模块 API 接口
    from sklearn.feature_selection import SelectKBest
    from scipy.stats import pearsonr
    from numpy import array
    
    fun = lambda X, Y: tuple(map(tuple, array(list(map(lambda x: pearsonr(x, Y), X.T))).T))
    sb = SelectKBest(fun, k=2)#返回k个最佳特征
    x_sb = sb.fit_transform(df.iloc[:,:4], df.iloc[:, 4])
    print('>>>检验统计值(相关系数):\n', sb.scores_)
    print('\n>>>P值:\n', sb.pvalues_)
    x_sb[:5]
    
    3.13 卡方检验

    卡方检验是比较理论频数和实际频数的吻合程度,适用于分类问题
    就是统计样本的实际观测值与理论推断值之间的偏离程度,实际观测值与理论推断值之间的偏离程度就决定卡方值的大小**(对于分类问题(y离散)),卡方值越大,越不符合;卡方值越小,偏差越小,越趋于符合

    # 调用 sklearn 模块 API 接口
    from sklearn.feature_selection import SelectKBest
    from sklearn.feature_selection import chi2
    
    skb = SelectKBest(chi2, k=2)
    x_skb = skb.fit_transform(df.iloc[:,:4], df.iloc[:,4])
    print('>>>检验统计值(卡方值):\n', skb.scores_)
    print('\n>>>P值:\n', skb.pvalues_)
    x_skb[:5]
    
    3.2 嵌入法Embedde

    嵌入法是特征和算法同时进行,让算法决定使用哪种特征。
    先使用算法和模型进行训练,得到各个特征的权重系数由大到小,这个系数代表特征对模型的重要性。比如决策树和树的集成模型中的feature_importances_属性,可以列出各个特征对树的建立的贡献,我们就可以基于这种贡献的评估,找出对模型建立最有用的特征。
    因此相比于过滤法,嵌入法会精确到模型本身。但是过滤法会通过统计常识查找范围,比如p值0.05,而嵌入法的权值系数最佳临界值需要判断

    3.3 包装法Wrapper

    未完待续

    4. 类别不平衡

    未完待续

    5. 降维

    5.1 无监督主成成分PCA

    通过线性变换将原始数据变换为一组各维度线性无关的表示,用于提取主要特征。是一个过程结果,用于辅助模型。


    image.png

    如上图所示,将100个维度降至10个维度,10个维度中每个维度=特征值b1*新变量a1,而新变量a1又是通过原100个维度得来的,无法形容a1到底是什么,但却与100个维度都相关。其中b就是特征向量,将b从大到小排序,计算累计贡献率,一般选取贡献率达到85%以上的为主成分。

    from sklearn.datasets import load_digits
    digit = load_digits()
    pca = PCA(n_components=10)
    pca.fit(digit['data'])
    print(pca.explained_variance_)#特征值b
    print(pca.components_)#特征向量e
    s = pca.explained_variance_
    df = pd.DataFrame({'z':s,
                      'z_sum':s.cumsum()/s.sum()})
    df['z_sum'].plot(linestyle = '--',marker = 'o')
    plt.axhline(0.85)
    

    6. 交叉验证

    交叉验证就是将样本数据分位训练集和测试集,训练集用来训练模型测试集用来评估模型预测好坏。得到多组训练集和测试集,这次是训练集下次可能会成为测试集,这就是所谓的“交叉”。
    一般在样本数量级少于1w条时,会用到交叉验证。当样本集较大时,会将数据集分为三份,训练集用来训练模型,验证集用来验证模型调参,把最终得到的模型再用于测试集,最终决定使用哪个模型以及对应参数。

    主要介绍简单交叉验证、k折交叉验证和分层交叉验证

    6.1 简单交叉验证train_test_split

    随机将样本集分为训练集和测试集

    from sklearn.model_selection import train_test_split
    '''
    (1)random_state不填或者为0时,每次都不同;其余值表示不同随机数
    (2)shuffle表示是否在分割之前对数据进行洗牌(默认True)
    '''
    X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.30,random_state=42,shuffle=True)
    

    测试集占30%训练集70%.
    这种方法处理简单,缺点是没有达到交叉的思想。

    6.2 k折交叉验证KFold

    将数据集均分k分,其中k-1份作为训练集,1分作为测试集。重复k次后,将所得出模型准确率的均值作为当前 k 折交叉验证下模型的性能指标。

    from sklearn.model_selection import KFold
    kf = KFold(n_splits=2)
    for train_index, test_index in kf.split(X):
        print('X_train:%s ' % X[train_index])
        print('X_test: %s ' % X[test_index])
    
    
    6.3 分层交叉验证StratifiedShuffleSplit

    对于非平衡数据选择分层交叉

    from sklearn.model_selection import StratifiedKFold,StratifiedShuffleSplit # 分层分割
    
    X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1,2]])
    Y = np.array([0, 0, 0, 1, 1])
    ss = StratifiedShuffleSplit(n_splits=5, test_size=0.5, random_state=seed) 
    
    for train_index, test_index in ss.split(X, Y):
        print(train_index, test_index)
       
     结果:
    [4 0] [1 2 3]
    [1 4] [0 2 3]
    [3 1] [0 2 4]
    [2 3] [1 4 0]
    [3 0] [4 1 2]
    
    

    举个简单的例子:
    n_splits分成多少组
    test_size测试集占比多少
    这里数据集为5行3列,重复5组
    (1)test_size占比0.5,那么5组0.5=2.5,四舍五入=3,那么测试集在5行中占3行,训练集就占2行。
    (2)看y值中包括3个负样本2个正样本,那么负样本占比3/5,正样本占比2/5
    (3)那么训练样本2个中负样本有2
    3/5=1.2约等于1个,正样本有22/5=0.8约等于1个;测试样本3个中负样本有33/5=1.8约等于2个,正样本有3*2/5=1.2约等于1个;

    参考:

    http://www.elecfans.com/d/816121.html连续变量离散化
    https://blog.csdn.net/snowdroptulip/article/details/78770088卡方检验
    https://blog.csdn.net/weixin_43172660/article/details/84340164特征选择

    相关文章

      网友评论

          本文标题:特征工程之入门总结

          本文链接:https://www.haomeiwen.com/subject/awtdwhtx.html