天池龙珠计划_金融风控训练营——Task3 特征工程学习打卡

1 学习任务

天池龙珠计划_金融风控训练营

天池龙珠计划_金融风控训练营链接

Task3 特征工程学习打卡

学习文章链接

1.1 学习目标

  • 学习特征预处理、缺失值、异常值处理、数据分桶等特征处理方法
  • 学习特征交互、编码、选择的相应方法
  • 完成相应学习打卡任务,两个选做的作业不做强制性要求,供学有余力同学自己探索。

2.学习内容

2.1 数据预处理

数据预处理(data preprocessing)是指在主要的处理以前对数据进行的一些处理。如对大部分地球物理面积性观测数据在进行转换或增强处理之前,首先将不规则分布的测网经过插值转换为规则网的处理,以利于计算机的运算。

2.1.1 脏数据

在实际业务处理中,数据通常是脏数据。所谓的脏,指数据可能存在以下几种问题(主要问题):

  1. 数据缺失:存在null值
  2. 数据噪声:数据中存在着错误或异常
  3. 数据不一致: 数据前后矛盾
  4. 数据冗余:数据量或者属性数目超出数据分析需要的情况
  5. 数据集不均衡:同一类别数据值差距悬殊
  6. 离群点/异常值:远离数据集中其余部分的数据
  7. 数据重复:在数据集中出现多次的数据

2.1.2 数据预处理步骤

数据进行预处理主要有以下步骤,第一部分提及的各种脏数据的处理就在这些步骤中完成:

  1. 数据清洗 Data Cleansing
  2. 数据转换 Data Transformation
  3. 数据描述 Data Description
  4. 特征选择 Feature Selection 或特征组合 Feature Combination
  5. 特征抽取 Feature Extraction

2.2 缺失值处理

缺失值的分类有:

  1. 完全随机缺失
  2. 随机缺失
  3. 完全不随机缺失

对于缺失值的处理主要有直接使用、删除和补全,其中缺失值补全包括均值插补、利用同类均值插补、极大似然估计、多重插补、插值法填充、模型填充等方法。

#查看缺失值情况
data_train.isnull().sum()
# 缺失值填充
#把所有缺失值替换为指定的值0
data_train = data_train.fillna(0)
#向用缺失值上面的值替换缺失值
data_train = data_train.fillna(axis=0,method='ffill')
#纵向用缺失值下面的值替换缺失值,且设置最多只填充两个连续的缺失值
data_train =data_train.fillna(axis=0,method='bfill',limit=2)

2.3 异常值(离群值)处理

首先我们要对数据进行异常值的检测。在发现异常值后,需要去了解异常值产生的原因,考虑其出现的原因是否与数据特性相关,考虑是否代表一种规律性、还是极其偶然的现象。

  • 如果异常值的存在代表着一种真实存在的现象,此时我们需要格外注意并妥善处理,不能直接删除。需要考虑如何合理地处理该异常值,研究其规律,将处理后的异常值纳入模型中进行训练。
  • 如果异常值只是偶然的个例,或者不想研究这种偶然的现象,可以直接删除。(只能对train的数据)

常见的异常值判断方法有:

  1. MAD法 (绝对中位差)
    M A D = m e d i a n ( ∣ X i − m e d i a n ( X ) ∣ ) MAD=median(|Xi−median(X)|) MAD=median(Ximedian(X))
  2. 3σ法
def find_outliers_by_3segama(data,fea):
    data_std = np.std(data[fea])
    data_mean = np.mean(data[fea])
    outliers_cut_off = data_std * 3
    lower_rule = data_mean - outliers_cut_off
    upper_rule = data_mean + outliers_cut_off
    data[fea+'_outliers'] = data[fea].apply(lambda x:str('异常值') if x > upper_rule or x < lower_rule else '正常值')
    return data
  1. 箱线图(百分位)
    四分位数会将数据分为三个点和四个区间,IQR = Q3 -Q1,下触须=Q1 − 1.5x IQR,上触须=Q3 + 1.5x IQR;
  2. 基于距离
  3. 基于密度
  4. 基于聚类

2.4 时间格式处理

在本次比赛的数据中存在一些非标准时间格式的数据,例如:

data_train['employmentLength'].value_counts(dropna=False).sort_index()

输出:
在这里插入图片描述
这些数据是数字和字符混合的数据,需要进行处理。

def employmentLength_to_int(s):
    if pd.isnull(s):
        return s
    else:
        return np.int8(s.split()[0])
for data in [data_train, data_test_a]:
    data['employmentLength'].replace(to_replace='10+ years', value='10 years', inplace=True)
    data['employmentLength'].replace('< 1 year', '0 years', inplace=True)
    data['employmentLength'] = data['employmentLength'].apply(employmentLength_to_int)
data['employmentLength'].value_counts(dropna=False).sort_index()

在这里插入图片描述

2.5 数据分箱

  • 特征分箱的目的:

    • 从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。
  • 数据分桶的对象:

    • 将连续变量离散化
    • 将多状态的离散变量合并成少状态
  • 分箱的原因:

    • 数据的特征内的值跨度可能比较大,对有监督和无监督中如k-均值聚类它使用欧氏距离作为相似度函数来测量数据点之间的相似度。都会造成大吃小的影响,其中一种解决方法是对计数值进行区间量化即数据分桶也叫做数据分箱,然后使用量化后的结果。
  • 分箱的优点:

    • 处理缺失值:当数据源可能存在缺失值,此时可以把null单独作为一个分箱。
    • 处理异常值:当数据中存在离群点时,可以把其通过分箱离散化处理,从而提高变量的鲁棒性(抗干扰能力)。例如,age若出现200这种异常值,可分入“age > 60”这个分箱里,排除影响。
    • 业务解释性:我们习惯于线性判断变量的作用,当x越来越大,y就越来越大。但实际x与y之间经常存在着非线性关系,此时可经过WOE变换。
  • 特别要注意一下分箱的基本原则:

    • (1)最小分箱占比不低于5%
    • (2)箱内不能全部是好客户
    • (3)连续箱单调
  • 数据分箱的类型:

    • 无监督分箱
      - 等距分箱(固定宽度分箱)
      - 等频分箱(分位数分箱)
      - 聚类分箱

    • 有监督分箱
      - 卡方分箱法(ChiMerge)
      - 最小熵法分箱

2.5.1 等距分箱

从最小值到最大值之间,均分为 N 等份, 这样, 如果 A,B 为最小最大值, 则每个区间的长度为 W=(B−A)/N , 则区间边界值为A+W,A+2W,….A+(N−1)W 。这里只考虑边界,每个等份里面的实例数量可能不等。

# 通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是loanAmnt/1000
data['loanAmnt_bin1'] = np.floor_divide(data['loanAmnt'], 1000)
## 通过对数函数映射到指数宽度分箱
data['loanAmnt_bin2'] = np.floor(np.log10(data['loanAmnt']))

2.5.2 等频分箱

区间的边界值要经过选择,使得每个区间包含大致相等的实例数量。比如说 N=10 ,每个区间应该包含大约10%的实例。

data['loanAmnt_bin3'] = pd.qcut(data['loanAmnt'], 10, labels=False)

2.5.3 聚类分箱

基于k均值聚类的分箱:k均值聚类法将观测值聚为k类,但在聚类过程中需要保证分箱的有序性:第一个分箱中所有观测值都要小于第二个分箱中的观测值,第二个分箱中所有观测值都要小于第三个分箱中的观测值,等等。

2.5.4 卡方分箱

卡方分箱是基于卡方分布和卡方检验为理论基础的分箱法,是自底向上的(即基于合并的)数据离散化方法。

它依赖于卡方检验:具有最小卡方值的相邻区间合并在一起,直到满足确定的停止准则。

基本思想:
对于精确的离散化,相对类频率在一个区间内应当完全一致。因此,如果两个相邻的区间具有非常类似的类分布,则这两个区间可以合并;否则,它们应当保持分开。而低卡方值表明它们具有相似的类分布。

2.6 交互特征(多项式特征)

  • 交互特征的构造非常简单,使用起来却代价不菲。如果线性模型中包含有交互特征对,那它的训练时间和评分时间就会从 O(n) 增加到 O(n2),其中 n 是单一特征的数量。

在天池学习文章中,将相同’grade’和 'subGrade’的’isDefault’求平均,并生成新的特征grade_target_mean和subGrade_target_mean。

for col in ['grade', 'subGrade']: 
    temp_dict = data_train.groupby([col])['isDefault'].agg(['mean']).reset_index().rename(columns={'mean': col + '_target_mean'})
    temp_dict.index = temp_dict[col].values
    temp_dict = temp_dict[col + '_target_mean'].to_dict()

    data_train[col + '_target_mean'] = data_train[col].map(temp_dict)
    data_test_a[col + '_target_mean'] = data_test_a[col].map(temp_dict)
# 其他衍生变量 mean 和 std
for df in [data_train, data_test_a]:
    for item in ['n0','n1','n2','n4','n5','n6','n7','n8','n9','n10','n11','n12','n13','n14']:
        df['grade_to_mean_' + item] = df['grade'] / df.groupby([item])['grade'].transform('mean')
        df['grade_to_std_' + item] = df['grade'] / df.groupby([item])['grade'].transform('std')

2.6.1 sklearn的PolynomialFeatures

机器学习——特征工程——交互特征(多项式特征)

使用 sklearn.preprocessing.PolynomialFeatures 这个类可以进行特征的构造,构造的方式就是特征与特征相乘(自己与自己,自己与其他人),这种方式叫做使用多项式的方式。例如:有、两个特征,那么它的 2 次多项式的次数为 []。

PolynomialFeatures 这个类有 3 个参数:

  • degree:控制多项式的次数;
  • interaction_only:默认为 False,如果指定为 True,那么就不会有特征自己和自己结合的项,组合的特征中没有和;
  • include_bias:默认为 True 。如果为 True 的话,那么结果中就会有 0 次幂项,即全为 1 这一列。

简单演示:

from sklearn.preprocessing import PolynomialFeatures
import numpy as np
X=np.arange(6).reshape(3,2)
'''
输出: X=array([[0, 1],
       			[2, 3],
       			[4, 5]])
       			 '''

设置多项式阶数为2,其他默认。

poly1=PolynomialFeatures(degree=2)
res1=poly1.fit_transform(X)
res1
'''
输出:
array([[ 1.,  0.,  1.,  0.,  0.,  1.],
       [ 1.,  2.,  3.,  4.,  6.,  9.],
       [ 1.,  4.,  5., 16., 20., 25.]])
       '''

2.7 特征选择

  • 特征选择技术可以精简掉无用的特征,以降低最终模型的复杂性,它的最终目的是得到一个简约模型,在不降低预测准确率或对预测准确率影响不大的情况下提高计算速度。特征选择不是为了减少训练时间(实际上,一些技术会增加总体训练时间),而是为了减少模型评分时间。

特征选择的方法:

  • 1 Filter
    • 方差选择法
    • 相关系数法(pearson 相关系数)
    • 卡方检验
    • 互信息法
  • 2 Wrapper (RFE)
    • 递归特征消除法
  • 3 Embedded
    • 基于惩罚项的特征选择法
    • 基于树模型的特征选择

学习链接:机器学习:特征选择(feature selection)

2.8 交叉验证模型

学习并理解天池文章中的交叉验证模型。

# 特征标签
features = [f for f in data_train.columns if f not in ['id','issueDate','isDefault'] and '_outliers' not in f]
x_train = data_train[features]
x_test = data_test_a[features]
y_train = data_train['isDefault']
# 交叉验证模型函数
def cv_model(clf, train_x, train_y, test_x, clf_name):
    folds = 5 # 5折交叉验证
    seed = 2020 # 随机种子2020
    
    #K折交叉验证
    kf = KFold(n_splits=folds, shuffle=True, random_state=seed) 
	# 构造0数组
    train = np.zeros(train_x.shape[0])
    test = np.zeros(test_x.shape[0])

    cv_scores = []
	
	#枚举kfold后的索引
    for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
        print('************************************ {} ************************************'.format(str(i+1)))
        # kf后的数据集
        trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]
		
		# 当分类器为lgb
        if clf_name == "lgb":
        	#转换为Dataset数据格式
            train_matrix = clf.Dataset(trn_x, label=trn_y)
            valid_matrix = clf.Dataset(val_x, label=val_y)
			#参数
            params = {
                'boosting_type': 'gbdt',
                'objective': 'binary',
                'metric': 'auc',
                'min_child_weight': 5,
                'num_leaves': 2 ** 5,
                'lambda_l2': 10,
                'feature_fraction': 0.8,
                'bagging_fraction': 0.8,
                'bagging_freq': 4,
                'learning_rate': 0.1,
                'seed': 2020,
                'nthread': 28,
                'n_jobs':24,
                'silent': True,
                'verbose': -1,
            }
			#设置模型
            model = clf.train(params, train_matrix, 50000, valid_sets=[train_matrix, valid_matrix], verbose_eval=200,early_stopping_rounds=200)
            # 迭代次数为模型最佳迭代
            val_pred = model.predict(val_x, num_iteration=model.best_iteration)
            test_pred = model.predict(test_x, num_iteration=model.best_iteration)
            
            # print(list(sorted(zip(features, model.feature_importance("gain")), key=lambda x: x[1], reverse=True))[:20])
                
        if clf_name == "xgb":
        	#dataframe转Dmatrix格式
            train_matrix = clf.DMatrix(trn_x , label=trn_y)
            valid_matrix = clf.DMatrix(val_x , label=val_y)
            
            params = {'booster': 'gbtree',
                      'objective': 'binary:logistic',
                      'eval_metric': 'auc',
                      'gamma': 1,
                      'min_child_weight': 1.5,
                      'max_depth': 5,
                      'lambda': 10,
                      'subsample': 0.7,
                      'colsample_bytree': 0.7,
                      'colsample_bylevel': 0.7,
                      'eta': 0.04,
                      'tree_method': 'exact',
                      'seed': 2020,
                      'nthread': 36,
                      "silent": True,
                      }
            #观察表
            watchlist = [(train_matrix, 'train'),(valid_matrix, 'eval')]
            '''
            evals:这是一个列表,用于对训练过程中进行评估列表中的元素。
            形式是evals = [(dtrain,'train'),(dval,'val')]或者是evals = [(dtrain,'train')],
            对于第一种情况,它使得我们可以在训练过程中观察验证集的效果
            '''
            model = clf.train(params, train_matrix, num_boost_round=50000, evals=watchlist, verbose_eval=200, early_stopping_rounds=200)
            val_pred  = model.predict(valid_matrix, ntree_limit=model.best_ntree_limit)
            test_pred = model.predict(test_x , ntree_limit=model.best_ntree_limit)
                 
        if clf_name == "cat":
            params = {'learning_rate': 0.05, 'depth': 5, 'l2_leaf_reg': 10, 'bootstrap_type': 'Bernoulli',
                      'od_type': 'Iter', 'od_wait': 50, 'random_seed': 11, 'allow_writing_files': False}
            
            model = clf(iterations=20000, **params)
            model.fit(trn_x, trn_y, eval_set=(val_x, val_y),
                      cat_features=[], use_best_model=True, verbose=500)
            
            val_pred  = model.predict(val_x)
            test_pred = model.predict(test_x)
            
        train[valid_index] = val_pred
        test = test_pred / kf.n_splits
        cv_scores.append(roc_auc_score(val_y, val_pred))
        # 打印cv得分
        print(cv_scores)
        
    print("%s_scotrainre_list:" % clf_name, cv_scores)
    print("%s_score_mean:" % clf_name, np.mean(cv_scores))
    print("%s_score_std:" % clf_name, np.std(cv_scores))
    return train, test
# lgb,xgb,catboost模型
def lgb_model(x_train, y_train, x_test):
    lgb_train, lgb_test = cv_model(lgb, x_train, y_train, x_test, "lgb")
    return lgb_train, lgb_test

def xgb_model(x_train, y_train, x_test):
    xgb_train, xgb_test = cv_model(xgb, x_train, y_train, x_test, "xgb")
    return xgb_train, xgb_test

def cat_model(x_train, y_train, x_test):
    cat_train, cat_test = cv_model(CatBoostRegressor, x_train, y_train, x_test, "cat")
#以lgb为例
lgb_train, lgb_test = lgb_model(x_train, y_train, x_test)

输出:
输出

3. 学习心得体会

在本次的学习内容中,我遇到了很多新的知识,例如:数据分箱、特征交互、特征选择,catboost等,在这个学习过程中碰到了很多难点,遇到难题之后,通过搜索,阅读大佬写的文章,自学他们的文章后从而理解难点,从中收获颇丰。
特征工程是机器学习,甚至是深度学习中最为重要的一部分,在实际应用中往往也是所花费时间最多的一步。其中包括的内容实在是太多了,如何做特征工程,如何将特征工程做好,真是一大难题。不同的数据需要对其做不同的特征工程分析,需要根据具体数据的特点以及场景的实际情况,做出相应的变化,这是需要很多的数据分析经验才能做得很好。一个好的特征工程将会事半功倍。
总的来说,特征工程是一个入门简单,但想精通非常难的一件事。还需更加深入的学习,更多的实践练习,提高自身水平。

参考链接

[1] 最全面的数据预处理介绍
[2] 特征工程之分箱
[3] 卡方分箱算法
[4] 机器学习——特征工程——交互特征(多项式特征)
[4] 机器学习:特征选择(feature selection)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值