【數(shù)據(jù)競賽】Kaggle實戰(zhàn)之單類別變量特征工程總結!
特征工程--類別變量完結篇!

這是一個系列篇,后續(xù)我們會按照我們第一章中的框架進行更新,因為大家平時都較忙,不會定期更新,如有興趣歡迎長期關注我們的公眾號,如有任何建議可以在評論區(qū)留言,該系列以往的經(jīng)典內容可參考下面的篇章。
1. kaggle競賽寶典-競賽框架篇!
2.3 kaggle競賽寶典-二分類相關指標優(yōu)化!
2.4 kaggle競賽寶典-多分類相關指標優(yōu)化!
3.1 數(shù)據(jù)探索分析-全局數(shù)據(jù)探索分析!
3.2 數(shù)據(jù)探索分析-單變量數(shù)據(jù)分析!
4.1 kaggle競賽寶典-樣本篩選篇!
4.2 kaggle競賽寶典-樣本組織篇!



類別特征編碼
在很多表格類的問題中,高基數(shù)的特征類別處理一直是一個困擾著很多人的問題,究竟哪一種操作是最好的,很難說,不同的數(shù)據(jù)集有不同的特性,可能某一種數(shù)據(jù)轉化操作這A數(shù)據(jù)集上取得了提升,但在B數(shù)據(jù)集上就不行了,但是知道的技巧越多,我們能取得提升的概率往往也會越大。此處我們會介紹幾種常見的處理類別特征的方法。

1. Label編碼
無序的類別變量,在很多時候是以字符串形式的出現(xiàn)的,例如:
顏色:紅色,綠色,黑色... 形狀:三角形,正方形,圓形...
而我們知道,梯度提升樹模型是無法對此類特征進行處理的。直接將其輸入到模型就會報錯。而這個時候最為常見的就是使用LabelEncoder對其進行編碼。LabelEncoder可以將類型為object的變量轉變?yōu)閿?shù)值形式,具體的例子如下:

LabelEncoder默認會先將object類型的變量進行排序,然后按照大小順序進行的編碼,此處N為該特征中不同變量的個數(shù)。幾乎所有的賽題中都會這么做,這樣做我們就可以將轉化后的特征輸入到模型,雖然這并不是模型最喜歡的形式,但是至少也可以吸收10%左右的信息,會總直接丟棄該變量的信息好很多。
對應代碼:
from sklearn import preprocessing
df = pd.DataFrame({'color':['red','blue','black','green']})
le = preprocessing.LabelEncoder()
le.fit(df['color'].values)
df['color_labelencode'] = le.transform(df['color'].values)
df
| color | color_labelencode | |
|---|---|---|
| 0 | red | 3 |
| 1 | blue | 1 |
| 2 | black | 0 |
| 3 | green | 2 |
2. One-Hot編碼
One-Hot編碼對于一個類別特征變量,我們對每個類別,使用二進制編碼(0或1)創(chuàng)建一個新列(有時稱為dummy變量),以表示特定行是否屬于該類別。One-Hot編碼可以將一個基數(shù)為的類別變量轉變?yōu)?span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;cursor: pointer;">個二元向量,我們以上面的顏色為案例,進行one-hot編碼之后就得到:

我們發(fā)現(xiàn)One-Hot編碼將我們的數(shù)據(jù)展開之后內存的消耗變得非常大,因為使用One-Hot編碼時需要創(chuàng)建額外的列,為我們需要編碼的特征列中的每個每個唯一值創(chuàng)建一個列。也就是說,如果我們有一個包含10000個不同值的類別特征,那么在One-Hot編碼之后將會生成10000個額外的新的列,這是不可以接受的。
但它的好處也非常明顯,One-Hot編碼之后,我們的線性模型可以更好的吸收High-Cadinality的類別信息,原先我們的采用線性模型,那么我們類別變量A的對預測帶來的貢獻為,, (A由(組成)我們發(fā)現(xiàn)類別2的貢獻就是類別1的一倍,這很明顯和我們的直覺不符,但是展開之后,我們類別變量A對預測帶來的貢獻為:。變量之間的關系變得更加合理了。所以One-Hot編碼對于很多線性模型是有必要的。
那么對于XGBoost,LightGBM之類的樹模型是否有必要呢?答案是有的!在我們的實踐中,很多時候對高基數(shù)的類別特征直接進行One-Hot編碼的效果往往可能不如直接LabelEncoder來的好。但是當我們的類別變量中有一些變量是人為構造的,加入了很多噪音,這個時候將其展開,那么模型可以更加快的找到那些非構建的類別。(參考訊飛18年舉辦的推薦比賽),取得更好的效果。
對應代碼:
from sklearn import preprocessing
df = pd.DataFrame({'color':['red','blue','black','green']})
pd.get_dummies(df['color'].values)
| black | blue | green | red | |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 1 |
| 1 | 0 | 1 | 0 | 0 |
| 2 | 1 | 0 | 0 | 0 |
| 3 | 0 | 0 | 1 | 0 |
3. Frequency編碼
Frequency編碼是數(shù)據(jù)競賽中使用最為廣泛的技術,在90%以上的數(shù)據(jù)建模的問題中都可以帶來提升。因為在很多的時候,頻率的信息與我們的目標變量往往存在有一定關聯(lián),例如:
在音樂推薦問題中,對于樂曲進行Frequency編碼可以反映該樂曲的熱度,而熱度高的樂曲往往更受大家的歡迎; 在購物推薦問題中,對于商品進行Frequency編碼可以反映該商品的熱度,而熱度高的商品大家也更樂于購買; 微軟設備被攻擊概率問題中,預測設備受攻擊的概率,那么設備安裝的軟件是非常重要的信息,此時安裝軟件的count編碼可以反映該軟件的流行度,越流行的產(chǎn)品的受眾越多,那么黑客往往會傾向對此類產(chǎn)品進行攻擊,這樣黑客往往可以獲得更多的利益
Frequency編碼通過計算特征變量中每個值的出現(xiàn)次數(shù)來表示該特征的信息,詳細的案例如下所示:

在很多實踐問題中,Count編碼往往可以給模型的效果帶來不錯的幫助。
對應代碼:
from sklearn import preprocessing
df = pd.DataFrame({'color':['red','red','red','blue','blue','black','green','green','green']})
df['color_cnt'] = df['color'].map(df['color'].value_counts())
df
| color | color_cnt | |
|---|---|---|
| 0 | red | 3 |
| 1 | red | 3 |
| 2 | red | 3 |
| 3 | blue | 2 |
| 4 | blue | 2 |
| 5 | black | 1 |
| 6 | green | 3 |
| 7 | green | 3 |
| 8 | green | 3 |
4. target編碼
target編碼是06年提出的一種結合標簽進行編碼的技術,它將類別特征替換為從標簽衍生而來的特征,在類別特征為高基數(shù)的時候非常有效。該技術在非常多的數(shù)據(jù)競賽中都取得了非常好的效果,但特別需要注意過擬合的問題。在kaggle競賽中成功的案例有owen zhang的leave-one-out的操作和莫斯科GM的基于K-fold的mean-target編碼,此處我們介紹兩種Mean-target編碼;
4.1. Leave-one-out mean-target 編碼
Leave-one-out mean-target編碼的思路相對簡單,我們每次編碼時,不考慮當前樣本的情況,用其它樣本對應的標簽的均值作為我們的編碼,而測試集則用全部訓練集樣本的均值進行編碼,案例如下:

的案例摘自owen-zhang(曾經(jīng)的kaggle第一名)的分享。
對應代碼:
from sklearn import preprocessing
from pandas import pandas
from category_encoders.leave_one_out import LeaveOneOutEncoder
df_tr = pd.DataFrame({'color':['red','red','red','red','red','red','black','black'], 'label':[1,0,1,1,0,1,1,0]})
df_te = pd.DataFrame({'color':['red','red','black'] })
loo = LeaveOneOutEncoder()
loo.fit_transform(df_tr['color'], df_tr['label'])
| color | |
|---|---|
| 0 | 0.6 |
| 1 | 0.8 |
| 2 | 0.6 |
| 3 | 0.6 |
| 4 | 0.8 |
| 5 | 0.6 |
| 6 | 0.0 |
| 7 | 1.0 |
loo.transform(df_te['color'])
| color | |
|---|---|
| 0 | 0.666667 |
| 1 | 0.666667 |
| 2 | 0.500000 |
4.2. K-fold mean-target 編碼
K-fold mean-target編碼的基本思想來源于Mean target編碼。K-fold mean-target編碼的訓練步驟如下,我們先將訓練集劃分為K折;
在對第A折的樣本進行編碼時,我們刪除K折中A折,并用剩余的數(shù)據(jù)計算如下公式:
后利用上面計算得到的值對第A折進行編碼; 依次對所有折進行編碼即可。
首先我們先理解一下上面的公式,最原始的Mean-target編碼是非常容易導致過擬合的,這其中過擬合的最大的原因之一在于對于一些特征列中出現(xiàn)次數(shù)很少的值過擬合了,比如某些值只有1個或者2到3個,但是這些樣本對應的標簽全部是1,怎么辦,他們的編碼值就應該是1,但是很明顯這些值的統(tǒng)計意義不大,大家可以通過伯努利分布去計算概率來理解。而如果我們直接給他們編碼了,就會誤導模型的學習。那么我們該怎么辦呢?
加正則!
于是我們就有了上面的計算式子,式子是值出現(xiàn)的次數(shù),是它對應的概率,是全局的均值, 那么當為0同時比較小的時候, 就會有大概率出現(xiàn)過擬合的現(xiàn)象,此時我們調大就可以緩解這一點,所以很多時候都需要不斷地去調整的值。
from category_encoders.target_encoder import TargetEncoder
from sklearn import base
from sklearn.model_selection import KFold
df = pd.DataFrame({'Feature':['A','B','B','B','B', 'A','B','A','A','B','A','A','B','A','A','B','B','B','A','A'],\
'Target':[1,0,0,1,1, 1,0,0,0,0,1, 0,1, 0,1,0,0,0,1,1]})
class KFoldTargetEncoderTrain(base.BaseEstimator, base.TransformerMixin):
def __init__(self, colnames,targetName,n_fold=5,verbosity=True,discardOriginal_col=False):
self.colnames = colnames
self.targetName = targetName
self.n_fold = n_fold
self.verbosity = verbosity
self.discardOriginal_col = discardOriginal_col
def fit(self, X, y=None):
return self
def transform(self,X):
assert(type(self.targetName) == str)
assert(type(self.colnames) == str)
assert(self.colnames in X.columns)
assert(self.targetName in X.columns)
mean_of_target = X[self.targetName].mean()
kf = KFold(n_splits = self.n_fold, shuffle = False, random_state=2019)
col_mean_name = self.colnames + '_' + 'Kfold_Target_Enc'
X[col_mean_name] = np.nan
for tr_ind, val_ind in kf.split(X):
X_tr, X_val = X.iloc[tr_ind], X.iloc[val_ind]
X.loc[X.index[val_ind], col_mean_name] = X_val[self.colnames].map(X_tr.groupby(self.colnames)[self.targetName].mean())
X[col_mean_name].fillna(mean_of_target, inplace = True)
if self.verbosity:
encoded_feature = X[col_mean_name].values
print('Correlation between the new feature, {} and, {} is {}.'.format(col_mean_name,
self.targetName,
np.corrcoef(X[self.targetName].values, encoded_feature)[0][1]))
if self.discardOriginal_col:
X = X.drop(self.targetName, axis=1)
return X
class KFoldTargetEncoderTest(base.BaseEstimator, base.TransformerMixin):
def __init__(self,train,colNames,encodedName):
self.train = train
self.colNames = colNames
self.encodedName = encodedName
def fit(self, X, y=None):
return self
def transform(self,X):
mean = self.train[[self.colNames,self.encodedName]].groupby(self.colNames).mean().reset_index()
dd = {}
for index, row in mean.iterrows():
dd[row[self.colNames]] = row[self.encodedName]
X[self.encodedName] = X[self.colNames]
X = X.replace({self.encodedName: dd})
return X
'''
訓練集編碼
'''
targetc = KFoldTargetEncoderTrain('Feature','Target',n_fold=5)
new_train = targetc.fit_transform(df)
new_train
Correlation between the new feature, Feature_Kfold_Target_Enc and, Target is 0.11082358287080162.
| Feature | Target | Feature_Kfold_Target_Enc | |
|---|---|---|---|
| 0 | A | 1 | 0.555556 |
| 1 | B | 0 | 0.285714 |
| 2 | B | 0 | 0.285714 |
| 3 | B | 1 | 0.285714 |
| 4 | B | 1 | 0.250000 |
| 5 | A | 1 | 0.625000 |
| 6 | B | 0 | 0.250000 |
| 7 | A | 0 | 0.625000 |
| 8 | A | 0 | 0.714286 |
| 9 | B | 0 | 0.333333 |
| 10 | A | 1 | 0.714286 |
| 11 | A | 0 | 0.714286 |
| 12 | B | 1 | 0.250000 |
| 13 | A | 0 | 0.625000 |
| 14 | A | 1 | 0.625000 |
| 15 | B | 0 | 0.250000 |
| 16 | B | 0 | 0.375000 |
| 17 | B | 0 | 0.375000 |
| 18 | A | 1 | 0.500000 |
| 19 | A | 1 | 0.500000 |
'''
測試集編碼
'''
test_targetc = KFoldTargetEncoderTest(new_train,
'Feature',
'Feature_Kfold_Target_Enc')
new_test = test_targetc.fit_transform(test)
4.3. Beta Target編碼
Beta Target編碼來源于kaggle之前的競賽Avito Demand Prediction Challenge第14名方案。該編碼和傳統(tǒng)Target Encoding不一樣,
Beta Target Encoding可以提取更多的特征,不僅僅是均值,還可以是方差等等; 在開源中,是沒有進行N Fold提取特征的,所以可能在時間上提取會更快一些;
Beta Target編碼利用Beta分布作為共軛先驗,對二元目標變量進行建模。Beta分布用和來參數(shù)化,和可以被當作是重復Binomial實驗中的正例數(shù)和負例數(shù)。分布中許多有用的統(tǒng)計數(shù)據(jù)可以用和表示,例如,
平均值:
方差:
等等。
從實驗對比上我們發(fā)現(xiàn),使用Beta Target Encoding可以得到大幅提升。因為Beta Target Encoding屬于類別編碼的一種,所以適用于高基數(shù)類別特征的問題。
對應代碼:
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder
'''
代碼摘自原作者:https://www.kaggle.com/mmotoki/beta-target-encoding
'''
class BetaEncoder(object):
def __init__(self, group):
self.group = group
self.stats = None
# get counts from df
def fit(self, df, target_col):
# 先驗均值
self.prior_mean = np.mean(df[target_col])
stats = df[[target_col, self.group]].groupby(self.group)
# count和sum
stats = stats.agg(['sum', 'count'])[target_col]
stats.rename(columns={'sum': 'n', 'count': 'N'}, inplace=True)
stats.reset_index(level=0, inplace=True)
self.stats = stats
# extract posterior statistics
def transform(self, df, stat_type, N_min=1):
df_stats = pd.merge(df[[self.group]], self.stats, how='left')
n = df_stats['n'].copy()
N = df_stats['N'].copy()
# fill in missing
nan_indexs = np.isnan(n)
n[nan_indexs] = self.prior_mean
N[nan_indexs] = 1.0
# prior parameters
N_prior = np.maximum(N_min-N, 0)
alpha_prior = self.prior_mean*N_prior
beta_prior = (1-self.prior_mean)*N_prior
# posterior parameters
alpha = alpha_prior + n
beta = beta_prior + N-n
# calculate statistics
if stat_type=='mean':
num = alpha
dem = alpha+beta
elif stat_type=='mode':
num = alpha-1
dem = alpha+beta-2
elif stat_type=='median':
num = alpha-1/3
dem = alpha+beta-2/3
elif stat_type=='var':
num = alpha*beta
dem = (alpha+beta)**2*(alpha+beta+1)
elif stat_type=='skewness':
num = 2*(beta-alpha)*np.sqrt(alpha+beta+1)
dem = (alpha+beta+2)*np.sqrt(alpha*beta)
elif stat_type=='kurtosis':
num = 6*(alpha-beta)**2*(alpha+beta+1) - alpha*beta*(alpha+beta+2)
dem = alpha*beta*(alpha+beta+2)*(alpha+beta+3)
# replace missing
value = num/dem
value[np.isnan(value)] = np.nanmedian(value)
return value
N_min = 1000
feature_cols = []
# encode variables
for c in cat_cols:
# fit encoder
be = BetaEncoder(c)
be.fit(train, 'deal_probability')
# mean
feature_name = f'{c}_mean'
train[feature_name] = be.transform(train, 'mean', N_min)
test[feature_name] = be.transform(test, 'mean', N_min)
feature_cols.append(feature_name)
# mode
feature_name = f'{c}_mode'
train[feature_name] = be.transform(train, 'mode', N_min)
test[feature_name] = be.transform(test, 'mode', N_min)
feature_cols.append(feature_name)
# median
feature_name = f'{c}_median'
train[feature_name] = be.transform(train, 'median', N_min)
test[feature_name] = be.transform(test, 'median', N_min)
feature_cols.append(feature_name)
# var
feature_name = f'{c}_var'
train[feature_name] = be.transform(train, 'var', N_min)
test[feature_name] = be.transform(test, 'var', N_min)
feature_cols.append(feature_name)
# skewness
feature_name = f'{c}_skewness'
train[feature_name] = be.transform(train, 'skewness', N_min)
test[feature_name] = be.transform(test, 'skewness', N_min)
feature_cols.append(feature_name)
# kurtosis
feature_name = f'{c}_kurtosis'
train[feature_name] = be.transform(train, 'kurtosis', N_min)
test[feature_name] = be.transform(test, 'kurtosis', N_min)
feature_cols.append(feature_name)
5. Weight of evidence
Weight of evidence(WoE)是變量轉化的利器,經(jīng)常會出現(xiàn)在信用卡評分等問題中,用來判斷好的和壞的客戶。WOE不僅簡單,而且可以依據(jù)其大小來篩選出重要的分組(group),可解釋性較強,早期WOE和邏輯回歸算法經(jīng)常一起使用并且可以幫助獲得較大的提升,在Kaggle的數(shù)據(jù)競賽中,我們發(fā)現(xiàn)WOE和梯度提升樹模型結合也可以取得不錯的效果。
此處的Event和Non Event為別是標簽為1的樣本的分布以及標簽為0的樣本的分布; 標簽為1的樣本分布:在某個類內正樣本占所有正樣本的比例;標簽為0的樣本分布也是類似的。
從上面的公式中,我們知道,正樣本的分布和負樣本的分布如果在某個類中差別越大的話,涵蓋的信息就越大,如果WOE的值越大,擇該這個類內的為正的概率極大,反之越小。
在實踐中,我們可以直接通過下面的步驟計算得到WOE的結果:
對于一個連續(xù)變量可以將數(shù)據(jù)先進行分箱,對于類別變量(無需做任何操作); 計算每個類內(group)中正樣本和負樣本出現(xiàn)的次數(shù); 計算每個類內(group)正樣本和負樣本的百分比events%以及non events %; 按照公式計算WOE;
對應代碼:
'''
代碼摘自:https://github.com/Sundar0989/WOE-and-IV
'''
import os
import pandas as pd
import numpy as np
df = pd.read_csv('./data/bank.csv',sep=';')
dic = {'yes':1, 'no':0}
df['target'] = df['y'].map(dic)
df = df.drop('y',axis=1)
import pandas.core.algorithms as algos
from pandas import Series
import scipy.stats.stats as stats
import re
import traceback
import string
max_bin = 20
force_bin = 3
# define a binning function
def mono_bin(Y, X, n = max_bin):
df1 = pd.DataFrame({"X": X, "Y": Y})
justmiss = df1[['X','Y']][df1.X.isnull()]
notmiss = df1[['X','Y']][df1.X.notnull()]
r = 0
while np.abs(r) < 1:
try:
d1 = pd.DataFrame({"X": notmiss.X, "Y": notmiss.Y, "Bucket": pd.qcut(notmiss.X, n)})
d2 = d1.groupby('Bucket', as_index=True)
r, p = stats.spearmanr(d2.mean().X, d2.mean().Y)
n = n - 1
except Exception as e:
n = n - 1
if len(d2) == 1:
n = force_bin
bins = algos.quantile(notmiss.X, np.linspace(0, 1, n))
if len(np.unique(bins)) == 2:
bins = np.insert(bins, 0, 1)
bins[1] = bins[1]-(bins[1]/2)
d1 = pd.DataFrame({"X": notmiss.X, "Y": notmiss.Y, "Bucket": pd.cut(notmiss.X, np.unique(bins),include_lowest=True)})
d2 = d1.groupby('Bucket', as_index=True)
d3 = pd.DataFrame({},index=[])
d3["MIN_VALUE"] = d2.min().X
d3["MAX_VALUE"] = d2.max().X
d3["COUNT"] = d2.count().Y
d3["EVENT"] = d2.sum().Y # 正樣本
d3["NONEVENT"] = d2.count().Y - d2.sum().Y # 負樣本
d3 = d3.reset_index(drop=True)
if len(justmiss.index) > 0:
d4 = pd.DataFrame({'MIN_VALUE':np.nan},index=[0])
d4["MAX_VALUE"] = np.nan
d4["COUNT"] = justmiss.count().Y
d4["EVENT"] = justmiss.sum().Y
d4["NONEVENT"] = justmiss.count().Y - justmiss.sum().Y
d3 = d3.append(d4,ignore_index=True)
d3["EVENT_RATE"] = d3.EVENT/d3.COUNT # 正樣本類內百分比
d3["NON_EVENT_RATE"] = d3.NONEVENT/d3.COUNT # 負樣本類內百分比
d3["DIST_EVENT"] = d3.EVENT/d3.sum().EVENT # 正的樣本占所有正樣本百分比
d3["DIST_NON_EVENT"] = d3.NONEVENT/d3.sum().NONEVENT # 負的樣本占所有負樣本百分比
d3["WOE"] = np.log(d3.DIST_EVENT/d3.DIST_NON_EVENT)
d3["IV"] = (d3.DIST_EVENT-d3.DIST_NON_EVENT)*np.log(d3.DIST_EVENT/d3.DIST_NON_EVENT)
d3["VAR_NAME"] = "VAR"
d3 = d3[['VAR_NAME','MIN_VALUE', 'MAX_VALUE', 'COUNT', 'EVENT', 'EVENT_RATE', 'NONEVENT', 'NON_EVENT_RATE', 'DIST_EVENT','DIST_NON_EVENT','WOE', 'IV']]
d3 = d3.replace([np.inf, -np.inf], 0)
d3.IV = d3.IV.sum()
return(d3)
def char_bin(Y, X):
df1 = pd.DataFrame({"X": X, "Y": Y})
justmiss = df1[['X','Y']][df1.X.isnull()]
notmiss = df1[['X','Y']][df1.X.notnull()]
df2 = notmiss.groupby('X',as_index=True)
d3 = pd.DataFrame({},index=[])
d3["COUNT"] = df2.count().Y
d3["MIN_VALUE"] = df2.sum().Y.index
d3["MAX_VALUE"] = d3["MIN_VALUE"]
d3["EVENT"] = df2.sum().Y
d3["NONEVENT"] = df2.count().Y - df2.sum().Y
if len(justmiss.index) > 0:
d4 = pd.DataFrame({'MIN_VALUE':np.nan},index=[0])
d4["MAX_VALUE"] = np.nan
d4["COUNT"] = justmiss.count().Y
d4["EVENT"] = justmiss.sum().Y
d4["NONEVENT"] = justmiss.count().Y - justmiss.sum().Y
d3 = d3.append(d4,ignore_index=True)
d3["EVENT_RATE"] = d3.EVENT/d3.COUNT
d3["NON_EVENT_RATE"] = d3.NONEVENT/d3.COUNT
d3["DIST_EVENT"] = d3.EVENT/d3.sum().EVENT
d3["DIST_NON_EVENT"] = d3.NONEVENT/d3.sum().NONEVENT
d3["WOE"] = np.log(d3.DIST_EVENT/d3.DIST_NON_EVENT)
d3["IV"] = (d3.DIST_EVENT-d3.DIST_NON_EVENT)*np.log(d3.DIST_EVENT/d3.DIST_NON_EVENT)
d3["VAR_NAME"] = "VAR"
d3 = d3[['VAR_NAME','MIN_VALUE', 'MAX_VALUE', 'COUNT', 'EVENT', 'EVENT_RATE', 'NONEVENT', 'NON_EVENT_RATE', 'DIST_EVENT','DIST_NON_EVENT','WOE', 'IV']]
d3 = d3.replace([np.inf, -np.inf], 0)
d3.IV = d3.IV.sum()
d3 = d3.reset_index(drop=True)
return(d3)
def data_vars(df1, target):
stack = traceback.extract_stack()
filename, lineno, function_name, code = stack[-2]
vars_name = re.compile(r'\((.*?)\).*$').search(code).groups()[0]
final = (re.findall(r"[\w']+", vars_name))[-1]
x = df1.dtypes.index
count = -1
for i in x:
if i.upper() not in (final.upper()):
if np.issubdtype(df1[i], np.number) and len(Series.unique(df1[i])) > 2:
conv = mono_bin(target, df1[i])
conv["VAR_NAME"] = i
count = count + 1
else:
conv = char_bin(target, df1[i])
conv["VAR_NAME"] = i
count = count + 1
if count == 0:
iv_df = conv
else:
iv_df = iv_df.append(conv,ignore_index=True)
return iv_df
final_iv = data_vars(df,df.target)
6. 人工編碼
6.1 人工轉化編碼:
這個需要一些專業(yè)背景知識,可以認為是Label編碼的一種補充,如果我們的類別特征是字符串類型的,例如:
城市編號:'10','100','90','888'...
這個時候,我們使用Labelencoder會依據(jù)字符串排序編碼。在字符串中'90' > '100',但我們直觀感覺是為'100' > '90',所以需要人為但進行干預編碼,如果都是可以直接轉化為數(shù)值形的,編碼時可以直接轉化為數(shù)值,或者自己書寫一個字典進行映射。
6.2 人工組合編碼:
這個同樣的也設計到部分專業(yè)背景知識,有些問題會出現(xiàn)一些臟亂的數(shù)據(jù),例如:
在一些位置字段中,有的是中文的,有的是英文的,例如“ShangHai”,“上?!?,二者描述的是同一個地方,但如果我們不注意就忽略了;
這個時候,我們可以先采用字典映射等方式對其進行轉化,然后再使用上面所屬的Frequency等編碼重新對其進行處理。
7. 擴展
上面是數(shù)據(jù)競賽中最為常用的編碼特征,在基于梯度提升樹模型的建模中,上面的編碼往往可以帶來非常大的幫助,也都是非常值得嘗試的。當然還有一些其它的類別特征的編碼形式,目前使用較多的一個庫就是category_encoders,有興趣的朋友可以當作擴展進行學習,此處不再敘述。
往期精彩回顧
本站qq群851320808,加入微信群請掃碼:
