常用特征总结(不定时补充)--数据挖掘稍微进阶版

        特征工程可谓是对于结构化赛题来说最重要的一点了,但是你还是只会对表格进行groupby然后取统计吗?那你就真的要说拜拜啦

K折目标编码

def gen_target_encoding_feats(train, test, encode_cols, target_col, n_fold=10):
    '''生成target encoding特征'''
    # for training set - cv
    tg_feats = np.zeros((train.shape[0], len(encode_cols)))
    kfold = StratifiedKFold(n_splits=n_fold, random_state=1024, shuffle=True)
    for _, (train_index, val_index) in enumerate(kfold.split(train[encode_cols], train[target_col])):
        df_train, df_val = train.iloc[train_index], train.iloc[val_index]
        for idx, col in enumerate(encode_cols):
            target_mean_dict = df_train.groupby(col)[target_col].mean()
            df_val[f'{col}_mean_target'] = df_val[col].map(target_mean_dict)
            tg_feats[val_index, idx] = df_val[f'{col}_mean_target'].values
​
    for idx, encode_col in enumerate(encode_cols):
        train[f'{encode_col}_mean_target'] = tg_feats[:, idx]
    for col in encode_cols:
    target_mean_dict = train.groupby(col)[target_col].mean()
    test[f'{col}_mean_target'] = test[col].map(target_mean_dict)
    return train,test
features = ['house_exist', 'debt_loan_ratio', 'industry', 'title']
train_data, train_inte, test_public = gen_target_encoding_feats(train_data, test_public, features,'isDefault', n_fold=10)

类别特征交叉

def encode_LE(col,train = train,test = test,verbose=True):#单独对train.test中的一列特征进行编码
    data = pd.concat([train,test],sort = False)
    lbl = LabelEncoder()
    lbl.fit(list(data[col].values))
    data[col] = lbl.transform(list(data[col].values))
    train = data[0:len(train)]
    test = data[len(train):]
    return train,test
highestEdu进行编码
train['highestEdu'] = train['edu'].astype(str)+'_'+train['highestEdu'].astype(str)
test['highestEdu'] = test['edu'].astype(str)+'_'+test['highestEdu'].astype(str)
train,test = encode_LE(['highestEdu'],train = train,test = test,verbose=True)

取编码类特征的前几位作为新特征

train1['residentAddr1'] = train1['residentAddr'].apply(lambda x: str(x[:2]).astype(float)

围绕强特做细粒度特征

for i in tqdm(feas_x):
    data_train_test_all[i+'lmt_mean'] = data_train_test_all.groupby([i])['lmt'].transform('mean')
    data_train_test_all[i+'lmt_max'] = data_train_test_all.groupby([i])['lmt'].transform('max')
    data_train_test_all[i+'lmt_min'] = data_train_test_all.groupby([i])['lmt'].transform('min')
    data_train_test_all[i+'lmt_std'] = data_train_test_all.groupby([i])['lmt'].transform('std')

统计特征

np.ptp极差

将无穷大的特征值(由特征相除所引起)设为零

train_data['early_return_amount_early_return'][np.isinf(train_data['early_return_amount_early_return'])] = 0

卡方分箱:判断类与类之间是否具有显著差异以进行自动分箱

import numpy as np
from scipy.stats import chi
import pandas as pd
from pandas import DataFrame,Series
import scipy
def chi3(arr):
   '''
  计算卡方值
  arr:频数统计表,二维numpy数组。
  '''
   assert(arr.ndim==2)
   #计算每行总频数
   R_N = arr.sum(axis=1)
   #每列总频数
   C_N = arr.sum(axis=0)
   #总频数
   N = arr.sum()
   # 计算期望频数 C_i * R_j / N。
   E = np.ones(arr.shape)* C_N / N
   E = (E.T * R_N).T
   square = (arr-E)**2 / E
   #期望频数为0时,做除数没有意义,不计入卡方值
   square[E==0] = 0
   #卡方值
   v = square.sum()
   return v
def chiMerge(df,col,target,max_groups=None,threshold=None):
​
   '''
  卡方分箱
  df: pandas dataframe数据集
  col: 需要分箱的变量名(数值型)
  target: 类标签
  max_groups: 最大分组数。
  threshold: 卡方阈值,如果未指定max_groups,默认使用置信度95%设置threshold。
  return: 包括各组的起始值的列表.
  '''
​
   freq_tab = pd.crosstab(df[col],df[target])
​
   #转成numpy数组用于计算。
   freq = freq_tab.values
​
   #初始分组切分点,每个变量值都是切分点。每组中只包含一个变量值.
​
   #分组区间是左闭右开的,如cutoffs = [1,2,3],则表示区间 [1,2) , [2,3) ,[3,3+)。
   cutoffs = freq_tab.index.values
​
   #如果没有指定最大分组
   if max_groups is None:    
       #如果没有指定卡方阈值,就以95%的置信度(自由度为类数目-1)设定阈值。
       if threshold is None:
           #类数目
           cls_num = freq.shape[-1]
           threshold = chi2.isf(0.05,df= cls_num - 1)
​
   while True:
       minvalue = None
       minidx = None
       #从第1组开始,依次取两组计算卡方值,并判断是否小于当前最小的卡方
       for i in range(len(freq) - 1):
           v = chi3(freq[i:i+2])
           if minvalue is None or (minvalue > v): #小于当前最小卡方,更新最小值
               minvalue = v
               minidx = i
​
       #如果最小卡方值小于阈值,则合并最小卡方值的相邻两组,并继续循环
       if (max_groups is not None and  max_groups< len(freq) ) or (threshold is not None and minvalue < threshold):
           #minidx后一行合并到minidx
           tmp = freq[minidx] + freq[minidx+1]
           freq[minidx] = tmp
           #删除minidx后一行
           freq = np.delete(freq,minidx+1,0)
           #删除对应的切分点
           cutoffs = np.delete(cutoffs,minidx+1,0)
​
       else: #最小卡方值不小于阈值,停止合并。
           break
   return cutoffs
   
def value2group(x,cutoffs):
​
   '''
  将变量的值转换成相应的组。
  x: 需要转换到分组的值
  cutoffs: 各组的起始值。
  return: x对应的组,如group1。从group1开始。
  '''
​
   #切分点从小到大排序。
   cutoffs = sorted(cutoffs)
   num_groups = len(cutoffs)
​
   #异常情况:小于第一组的起始值。这里直接放到第一组。
   #异常值建议在分组之前先处理妥善。
   if x < cutoffs[0]:
       return 'group1'
​
   for i in range(1,num_groups):
       if cutoffs[i-1] <= x < cutoffs[i]:
           return 'group{}'.format(i)
​
   #最后一组,也可能会包括一些非常大的异常值。
   return 'group{}'.format(num_groups)

示例:

cutoffs = chiMerge(train,'a2','y',max_groups=8)
train['分类后的特征'] = train['a2'].apply(value2group,args=(cutoffs,))
valid['分类后的特征'] = valid['a2'].apply(value2group,args=(cutoffs,))
    分类后的特征:
1    group1
2    group2
3    gtoup2
...之后再进行类别编码即可

WOE分箱:对每个特征进行一个与目标关联的排序

def calIV(df,var,target):
​
   '''
  计算IV值
  param df:数据集pandas.dataframe
  param var:已分组的列名,无缺失值
  param target:响应变量(0,1)
  return:IV值
  '''
   eps = 0.000001  #避免除以0
   gbi = pd.crosstab(df[var],df[target]) + eps
   gb = df[target].value_counts() + eps
   gbri = gbi/gb
   gbri['woe'] = np.log(gbri[1]/gbri[0])
   gbri['iv'] = (gbri[1] - gbri[0])*gbri['woe']
   return gbri['iv'].sum()

示例:

woe_map = calWOE(data,'MAX_AMOUNT_chigroup','y')#注:'MAX_AMOUNT_chigroup'为类别特征
train['MAX_AMOUNT_chigroup']=train['MAX_AMOUNT_chigroup'].map(woe_map)
valid['MAX_AMOUNT_chigroup']=valid['MAX_AMOUNT_chigroup'].map(woe_map)
    data
1   0.00528
2   0.00388
.
.
.
100 0.00388
101 0.00528 

K_means最佳簇心选择

from sklearn.cluster import KMeans
import multiprocessing
def train_cluster(train_vecs, model_name=None, start_k=2, end_k=20):
    print('training cluster')
    SSE = []
    SSE_d1 = [] #sse的一阶导数
    SSE_d2 = [] #sse的二阶导数
    models = [] #保存每次的模型
    for i in range(start_k, end_k):
        kmeans_model = KMeans(n_clusters=i, n_jobs=multiprocessing.cpu_count())
        kmeans_model.fit(train_vecs)
        SSE.append(kmeans_model.inertia_)  # 保存每一个k值的SSE值
        print('{} Means SSE loss = {}'.format(i, kmeans_model.inertia_))
        models.append(kmeans_model)
    # 求二阶导数,通过sse方法计算最佳k值
    SSE_length = len(SSE)
    for i in range(1, SSE_length):
        SSE_d1.append((SSE[i - 1] - SSE[i]) / 2)
    for i in range(1, len(SSE_d1) - 1):
        SSE_d2.append((SSE_d1[i - 1] - SSE_d1[i]) / 2)
​
    best_model = models[SSE_d2.index(max(SSE_d2)) + 1]
    return best_model
#示例:
model = KMeans(n_clusters=30, n_jobs=multiprocessing.cpu_count())#试出来30次效果最好
need['level']=model.labels_

特征手动分箱

cutoff = [float('-inf'), 0, 1, 4, 10, 15, float('inf')]
 
dt['A_cut'] = pd.cut(dt['A'],bins=cutoff, right=False)
eg:
    A_cut
0   [-inf,0)
1   [0,1)
再接类别编码转成数值类型
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值