天天看點

專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

基于 Jupyter 的特征工程手冊:資料預處理的上一篇: 專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(一) 專欄 | 基于 Jupyter 的特征工程手冊: 資料預處理(二) 項目位址: https://github.com/YC-Coder-Chen/feature-engineering-handbook

本項目将探讨資料預處理部分:介紹了如何利用 scikit-learn 處理靜态的連續變量,利用 Category Encoders 處理靜态的類别變量以及利用 Featuretools 處理常見的時間序列變量。

目錄

特征工程的資料預處理我們将分為三大部分來介紹:

  • 靜态連續變量
  • 靜态類别變量
  • 時間序列變量

本文将介紹 1.2 靜态類别變量的資料預處理(下部分,即1.2.7-1.2.11)。下面将結合 Jupyter,使用 sklearn,進行詳解。

專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

1.2 Static Categorical Variables 靜态類别變量

真實世界的資料集還往往包含類别特征。但是由于scikit-learn中的模型隻能處理數值特征,是以我們需要将類别特征編碼為數值特征但是,很多新的模型開始直接提供類别變量支援,例如lightGBM和Catboost。這裡我們使用category_encoders包,因為它涵蓋了更多的編碼方法。

1.2.7 M-estimate Encoding M估計量編碼

M估計量編碼是目标編碼的一個簡化版本。與目标編碼器相比,M估計量編碼僅具有一個可調參數(m),而目标編碼器具有兩個可調參數(min_samples_leaf和smoothing)。

公式:

專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

其中m為使用者定義的參數;

m:m為非負數, m的值越高,先驗機率的權重則更大。

𝑋′𝑘為類别特征X中類别k的編碼值;

Prior:目标變量的先驗機率/期望值;

𝑛+:訓練集中特征X類别為k,而且具有正因變量标簽的樣本數;

𝑦+:訓練集中具有正因變量标簽的樣本數;

參考文獻:Micci-Barreca, D. (2001). A preprocessing scheme for high-cardinality categorical attributes in classification and prediction problems. ACM SIGKDD Explorations Newsletter, 3(1), 27-32.

import numpy as np
import pandas as pd
from category_encoders.m_estimate import MEstimateEncoder
# category_encoders 直接支援dataframe

# 随機生成一些訓練集
train_set = pd.DataFrame(np.array([['male',10],['female', 20], ['male',10], 
                       ['female',20],['female',15]]),
             columns = ['Sex','Type'])
train_y = np.array([False, True, True, False, False])

# 随機生成一些測試集, 并有意讓其包含未在訓練集出現過的類别與缺失值
test_set = pd.DataFrame(np.array([['female',20],['male', 20], ['others',15], 
                       ['male',20],['female',40], ['male', 25]]),
             columns = ['Sex','Type'])
test_set.loc[4,'Type'] = np.nan      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
encoder = MEstimateEncoder(cols=['Sex','Type'], 
                           handle_unknown='value',  
                           handle_missing='value').fit(train_set,train_y) # 在訓練集上訓練
encoded_train = encoder.transform(train_set) # 轉換訓練集
encoded_test = encoder.transform(test_set) # 轉換測試集

# handle_unknown 和 handle_missing 被設定為 'value'
# 在目标編碼中,handle_unknown 和 handle_missing 僅接受 ‘error’, ‘return_nan’ 及 ‘value’ 設定
# 兩者的預設值均為 ‘value’, 即對未知類别或缺失值填充訓練集的因變量平均值

encoded_test # 編碼後的變量數與原類别變量數一緻      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
# 驗證一下計算的結果,在測試集中,‘male’類别的編碼值為 0.466667

y_positive = 2 # 在訓練集中,共有兩個樣本有正的因變量标簽
n_positive = 1 # 在訓練集中,共有兩個樣本在變量‘Sex’中有‘male’标簽,在兩個樣本中僅有一個有正的因變量标簽
prior = train_y.mean() # 訓練集因變量先驗機率
m = 1.0 # 預設值
male_encode = (n_positive + prior * m)/(y_positive + m)
male_encode # return 0.4666666666666666,與要驗證的值吻合
      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

1.2.8 James-Stein Encoder James-Stein 編碼

James-Stein編碼也是一種基于目标編碼的編碼方法。與M估計量編碼一樣,James-Stein編碼器也嘗試通過參數B來平衡先驗機率與觀測到的條件機率。但與目标編碼與M估計量編碼不同的是,James-Stein編碼器通過方差比而不是樣本大小來平衡兩個機率。

James-Stein編碼可使用獨立方法,合并方法等多種方法來估計參數B。有關更多資訊,請參閱category_encoders官方網站:

http://contrib.scikit-learn.org/categorical-encoding/jamesstein.html

James-Stein編碼假定服從正态分布。是以為了滿足所需的假設,Category Encoders預設使用對數比來轉換二分類問題。

獨立方法的公式:

專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

其中,

𝑋′𝑘是類别特征X中類别k的編碼值;

先驗機率:目标變量的先驗機率/期望值;

𝑛+:在訓練集中,在類别特征X上的标簽為k且具有因變量正标簽的樣本數;

𝑛: 在訓練集中,在類别特征X上标簽為k的樣本數; 

𝑉𝑎𝑟(𝑦𝑘):訓練集中,在特征X上标簽為k的樣本因變量方差; 

𝑉𝑎𝑟(𝑦):總體因變量的方差; 

𝑉𝑎𝑟(𝑦𝑘)和𝑉𝑎𝑟(𝑦)都應通過樣本統計資料進行估算。

從直覺的角度來講,B起到來平衡先驗機率與觀測到的條件機率的作用,若條件機率的均值不可靠(y_k具有高方差),則我們應當對先驗機率賦予更大的權重。

import numpy as np
import pandas as pd
from category_encoders.james_stein import JamesSteinEncoder
# category_encoders 直接支援dataframe

# 随機生成一些訓練集
train_set = pd.DataFrame(np.array([['male',10],['female', 20], ['male',10], 
                       ['female',20],['female',15]]),
             columns = ['Sex','Type'])
train_y = np.array([False, True, True, False, False])

# 随機生成一些測試集, 并有意讓其包含未在訓練集出現過的類别與缺失值
test_set = pd.DataFrame(np.array([['female',20],['male', 20], ['others',15], 
                       ['male',20],['female',40], ['male', 25]]),
             columns = ['Sex','Type'])
test_set.loc[4,'Type'] = np.nan      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
encoder = JamesSteinEncoder(cols=['Sex','Type'], 
                           handle_unknown='value', 
                           model='independent',  
                           handle_missing='value').fit(train_set,train_y) # 在訓練集上訓練
encoded_train = encoder.transform(train_set) # 轉換訓練集
encoded_test = encoder.transform(test_set) # 轉換測試集

# handle_unknown 和 handle_missing 被設定為 'value'
# 在目标編碼中,handle_unknown 和 handle_missing 僅接受 ‘error’, ‘return_nan’ 及 ‘value’ 設定
# 兩者的預設值均為 ‘value’, 即對未知類别或缺失值填充訓練集的因變量平均值

encoded_test # 編碼後的變量數與原類别變量數一緻

# 因為在category_encoders中,其對前文所述的公式做了一些修改,故此處不會進一步驗證結果      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

1.2.9 Weight of Evidence Encoder 證據權重編碼

與上述方法類似,證據權重編碼器也是根據類别變量與因變量的關系對分類變量進行編碼。

專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

以上是WoE的原始定義,但在category_encoders中,它還增加了正則項以應對過拟合。帶正則項的 𝑑𝑖𝑠𝑡𝑟𝑖𝑏𝑢𝑡𝑖𝑜𝑛_𝑜𝑓_𝑝𝑜𝑠𝑖𝑡𝑖𝑣𝑒 , 𝑑𝑖𝑠𝑡𝑟𝑖𝑏𝑢𝑡𝑖𝑜𝑛_𝑜𝑓_𝑛𝑒𝑔𝑎𝑡𝑖𝑣𝑒 如下所示:

專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
import numpy as np
import pandas as pd
from category_encoders.woe import WOEEncoder
# category_encoders 直接支援dataframe

# 随機生成一些訓練集
train_set = pd.DataFrame(np.array([['male',10],['female', 20], ['male',10], 
                       ['female',20],['female',15]]),
             columns = ['Sex','Type'])
train_y = np.array([False, True, True, False, False])

# 随機生成一些測試集, 并有意讓其包含未在訓練集出現過的類别與缺失值
test_set = pd.DataFrame(np.array([['female',20],['male', 20], ['others',15], 
                       ['male',20],['female',40], ['male', 25]]),
             columns = ['Sex','Type'])
test_set.loc[4,'Type'] = np.nan      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
encoder = WOEEncoder(cols=['Sex','Type'], 
                     handle_unknown='value',  
                     handle_missing='value').fit(train_set,train_y)  # 在訓練集上訓練
encoded_train = encoder.transform(train_set) # 轉換訓練集
encoded_test = encoder.transform(test_set) # 轉換測試集

# handle_unknown 和 handle_missing 被設定為 'value'
# 在目标編碼中,handle_unknown 和 handle_missing 僅接受 ‘error’, ‘return_nan’ 及 ‘value’ 設定
# 兩者的預設值均為 ‘value’, 即對未知類别或缺失值填充訓練集的因變量平均值

encoded_test # 編碼後的變量數與原類别變量數一緻      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
# 驗證一下計算的結果,在測試集中,‘male’類别的編碼值為 0.223144

y = 5 # 訓練集中一共有5個樣本
y_positive = 2 # 訓練集中2個樣本有正标簽

n = 2 # 訓練集中有2個樣本在Sex變量上有‘male’ 标簽
n_positive = 1 # 這兩個樣本中僅有一個有正标簽

regularization = 1.0 # 預設值

dis_postive = (n_positive + regularization) / (y_positive + 2 * regularization)
dis_negative = (n - n_positive + regularization) / (y - y_positive + 2 * regularization)
male_encode = np.log(dis_postive / dis_negative)
male_encode # return 0.22314355131420976,與要驗證的值吻合      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

1.2.10 Leave One Out Encoder 留一法編碼

留一法編碼器通過組因變量均值對每個組進行編碼。此處組指的是類别變量中的不同類别。

留一法同時考慮了過拟合問題,訓練集中的每一個樣本的編碼值是除去該樣本後的組因變量均值。是以,在訓練集中,其可以将處于相同組的每個樣本編碼為不同的值。

留一法以不同的方式對測試集進行編碼。測試集中的每個樣本均由訓練集中的組均值編碼,計算過程中沒有考慮去除該樣本。

專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

此處,若樣本j擁有k标簽,則( 𝑥𝑗==𝑘 )傳回1,否則傳回0 

𝑋𝑘𝑖 為樣本i的标簽為k情形下的編碼值

import numpy as np
import pandas as pd
from category_encoders.leave_one_out import LeaveOneOutEncoder
# category_encoders 直接支援dataframe

# 随機生成一些訓練集
train_set = pd.DataFrame(np.array([['male',10],['female', 20], ['male',10], 
                       ['female',20],['female',15]]),
             columns = ['Sex','Type'])
train_y = np.array([False, True, True, False, False])

# 随機生成一些測試集, 并有意讓其包含未在訓練集出現過的類别與缺失值
test_set = pd.DataFrame(np.array([['female',20],['male', 20], ['others',15], 
                       ['male',20],['female',40], ['male', 25]]),
             columns = ['Sex','Type'])
test_set.loc[4,'Type'] = np.nan      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
encoder = LeaveOneOutEncoder(cols=['Sex','Type'], 
                             handle_unknown='value',  
                             handle_missing='value').fit(train_set,train_y)  # 在訓練集上訓練
encoded_train = encoder.transform(train_set) # 轉換訓練集
encoded_test = encoder.transform(test_set) # 轉換測試集

# handle_unknown 和 handle_missing 被設定為 'value'
# 在目标編碼中,handle_unknown 和 handle_missing 僅接受 ‘error’, ‘return_nan’ 及 ‘value’ 設定
# 兩者的預設值均為 ‘value’, 即對未知類别或缺失值填充訓練集的因變量平均值

encoded_test # 編碼後的變量數與原類别變量數一緻
# 結果可見,所有類别值都被編碼為訓練集中的類别因變量均值      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
# 訓練集結果
LeaveOneOutEncoder(cols=['Sex','Type'],
                   handle_unknown='value',  
                   handle_missing='value').fit_transform(train_set,train_y)

# 進行小驗算:
# 對第一個樣本而言,其在Sex變量上的标簽為‘male’
# 除去該樣本後,‘male’标簽樣本的因變量平均值為1.0 (僅剩樣本3有‘male’标簽,且其有正的因變量标簽)
# 同理,對第三個同樣有‘male’标簽的樣本,除去它後标簽樣本的因變量平均值變為了0.0
      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

1.2.11 Catboost Encoder Catboost 編碼

CatBoost是一個基于樹的梯度提升模型。其在包含大量類别特征的資料集問題中具有出色的效果。該模型針對分類特征提出了一種基于“留一法編碼器”的新編碼系統。在使用Catboost編碼器之前,必須先對訓練資料随機排列,因為在Catboost中,編碼是基于“時間”的概念,即資料集中觀測值的順序。

專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)

其中,若樣本j擁有k标簽,則( 𝑥𝑗==𝑘 )傳回1,否則傳回0 

𝑋𝑘𝑖 為樣本i的标簽為k情形下的編碼值 

Prior 為因變量的先驗機率/期望值 

a為正則化系數

import numpy as np
import pandas as pd
from category_encoders.cat_boost import CatBoostEncoder
# category_encoders 直接支援dataframe

# 随機生成一些訓練集
train_set = pd.DataFrame(np.array([['male',10],['female', 20], ['male',10], 
                       ['female',20],['female',15]]),
             columns = ['Sex','Type'])
train_y = np.array([False, True, True, False, False])

# 随機生成一些測試集, 并有意讓其包含未在訓練集出現過的類别與缺失值
test_set = pd.DataFrame(np.array([['female',20],['male', 20], ['others',15], 
                       ['male',20],['female',40], ['male', 25]]),
             columns = ['Sex','Type'])
test_set.loc[4,'Type'] = np.nan      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
# 事實上,在使用Catboost編碼前,我們本應先打亂資料順序
# 但由于我們的資料本身已經是随機生成的,故無需打亂

encoder = CatBoostEncoder(cols=['Sex','Type'], 
                          handle_unknown='value',  
                          handle_missing='value').fit(train_set,train_y)   # 在訓練集上訓練
encoded_train = encoder.transform(train_set) # 轉換訓練集
encoded_test = encoder.transform(test_set) # 轉換測試集

# handle_unknown 和 handle_missing 被設定為 'value'
# 在目标編碼中,handle_unknown 和 handle_missing 僅接受 ‘error’, ‘return_nan’ 及 ‘value’ 設定
# 兩者的預設值均為 ‘value’, 即對未知類别或缺失值填充訓練集的因變量平均值

encoded_test # 編碼後的變量數與原類别變量數一緻
      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
# 驗證一下計算的結果,在測試集中,‘male’類别的編碼值為 0.466667

Prior = train_y.mean() # 先驗機率
n = 2 # 在訓練集中,兩個樣本在Sex變量上具有‘male’标簽
n_positive = 1 # 這兩個樣本中,僅有一個擁有正标簽
a = 1 # 正則化系數, 預設值為1

encoded_male = (n_positive + a * prior) / (n + a) 
encoded_male # return 0.4666666666666666,與要驗證的值吻合      
專欄 | 基于 Jupyter 的特征工程手冊:資料預處理(三)
# 訓練集中第三個樣本在Sex這一變量上有‘male’标簽,其編碼值為0.2
Prior = train_y.mean() # 先驗機率
n = 1 # 在第三個樣本前僅有一個樣本有‘male’标簽
n_positive = 0 # 這僅有的一個樣本沒有正标簽
a = 1 # 正則化系數

encoded_male = (n_positive + a * Prior) / (n + a)
encoded_male # return 0.2      

0.2

好了,以上就是關于靜态類别變量(下部分)的資料預處理介紹。建議讀者結合代碼,在 Jupyter 中實操一遍。

目前該項目完整中文版正在制作中,請持續關注哦~

中文版 Jupyter 位址:

https://github.com/YC-Coder-Chen/feature-engineering-handbook/blob/master/1.%20Data%20Preprocessing.ipynb

繼續閱讀