基於 Jupyter 的特徵工程手冊:資料預處理的上一篇:
專欄 | 基於 Jupyter 的特徵工程手冊:資料預處理(一)
專案地址:
https://github.com/YC-Coder-Chen/feature-engineering-handbook
本專案將探討資料預處理部分:介紹瞭如何利用 scikit-learn 處理靜態的連續變數,利用 Category Encoders 處理靜態的類別變數以及利用 Featuretools 處理常見的時間序列變數。
目錄
特徵工程的資料預處理我們將分為三大部分來介紹:
- 靜態連續變數
- 靜態類別變數
- 時間序列變數
本文將介紹 1.2 靜態類別變數的資料預處理(上部分,即1.2.1-1.2.6)。下面將結合 Jupyter,使用 sklearn,進行詳解。
1.2 Static Categorical Variables 靜態類別變數
真實世界的資料集還往往包含類別特徵。但是由於scikit-learn中的模型只能處理數值特徵,因此我們需要將類別特徵編碼為數值特徵但是,很多新的模型開始直接提供類別變數支援,例如lightGBM和Catboost。這裡我們使用category_encoders包,因為它涵蓋了更多的編碼方法。
1.2.1 Ordinal Encoding 序數編碼
序數編碼將類別變數轉化為一列序數變數,包含從1到類別數量之間的整數
import numpy as np
import pandas as pd
from category_encoders import OrdinalEncoder
# 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
train_set # 原始訓練集
test_set # 原始測試集
encoder = OrdinalEncoder(cols = ['Sex', 'Type'],
handle_unknown = 'value',
handle_missing = 'value').fit(train_set,train_y) # 在訓練集上訓練
# 將 handle_unknown設為‘value’,即測試集中的未知特徵值將被標記為-1
# 將 handle_missing設為‘value’,即測試集中的缺失值將被標記為-2
# 其他的選擇為:‘error’:即報錯;‘return_nan’:即未知值/缺失之被標記為nan
encoded_train = encoder.transform(train_set) # 轉換訓練集
encoded_test = encoder.transform(test_set) # 轉換測試集
# 以測試集結果為例
encoded_test
# 在序數編碼中:
# 變數Sex中: 'male' => 1.0, 'female' => 2.0, 未知 => -1.0, 缺失值 => -2.0
# (事實上,測試集中完全有可能出現未知與缺失情況)
# 在我們的例子中, Sex這一變數中的'other' 類別從未在訓練集中出現過
# 變數 Type 中: 10 => 1.0, 20 => 2.0, 15 => 3.0, 未知 => -1.0, 缺失值 => -2.0
encoded_train.astype(float) # 訓練集結果
1.2.2 One-hot Encoding 獨熱編碼
Scikit-learn中也提供來獨熱編碼函式,其可以將具有n_categories個可能值的一個分類特徵轉換為n_categories個二進位制特徵,其中一個為1,所有其他為0在category_encoders中,它包含了附加功能,即指示缺失或未知的值。在這裡,我們繼續使用category_encoders
import numpy as np
import pandas as pd
from category_encoders import OneHotEncoder
# 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
train_set # 原始訓練集
test_set # 原始測試集
encoder = OneHotEncoder(cols=['Sex', 'Type'],
handle_unknown='indicator',
handle_missing='indicator',
use_cat_names=True).fit(train_set,train_y) # 在訓練集上訓練
encoded_train = encoder.transform(train_set) # 轉換訓練集
encoded_test = encoder.transform(test_set) # 轉換測試集
# 將 handle_unknown設為‘indicator’,即會新增一列指示未知特徵值
# 將 handle_missing設為‘indicator’,即會新增一列指示缺失值
# 其他的handle_unknown/handle_missing 的選擇為:
# ‘error’:即報錯; ‘return_nan’:即未知值/缺失之被標記為nan; ‘value’:即未知值/缺失之被標記為0
# 以測試集結果為例
encoded_test
# 在獨熱編碼中:
# 變數 Sex => 變為了4個新變數: 'male' => [1 ,0 ,0, 0];
# 'female' => [0 ,1 ,0, 0];
# 未知 => [0 ,0 ,0, 1];
# 缺失 => [0, 0, 1, 0];
# 變數 Type => 變為了5個新變數: 10 => [1, 0, 0, 0, 0];
# 20 => [0, 1, 0, 0, 0];,
# 15 => [0, 0, 1, 0, 0];
# 未知 => [0, 0, 0, 0, 1];
# 缺失 => [0, 0, 0, 1, 0];
1.2.3 Hashing Encoding 雜湊編碼
雜湊編碼基於特徵雜湊的方法。它將雜湊函式應用於變數,將任意數量的變數以一定的規則對映到給定數量的變數。特徵雜湊可能會導致要素之間發生衝突。但雜湊編碼的優點是它不需要制定和維護原變數與新變數之間的對映關係。因此,雜湊編碼器的大小及複雜程度不隨資料類別的增多而增多。
import numpy as np
import pandas as pd
from category_encoders.hashing import HashingEncoder
# 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
train_set # 原始訓練集
test_set # 原始測試集
encoder = HashingEncoder(cols=['Sex', 'Type'],
n_components = 5).fit(train_set,train_y)
encoded_train = encoder.transform(train_set) # 轉換訓練集
encoded_test = encoder.transform(test_set) # 轉換測試集
# 將兩列的資料集雜湊編碼為5列
# 雜湊編碼結果與訓練集/測試集中的內容無關
# 只要列名匹配,我們就可以在任何新資料集上使用雜湊編碼方法
# 編碼結果僅由雜湊函式確定
# 通常雜湊編碼應用於更高和更稀疏的維空間,這裡以兩個變數作為雜湊編碼的例子
# 以測試集結果為例
encoded_test
encoded_train # 訓練集結果
1.2.4 Helmert Encoding Helmert 編碼
Helmert編碼通常在計量經濟學中使用。在Helmert編碼(分類特徵中的每個值對應於Helmert矩陣中的一行)之後,線性模型中編碼後的變數係數可以反映在給定該類別變數某一類別值的情形下因變數的平均值與給定該類別其他類別值的情形下因變數的平均值的差值。在category_encoders包中實現的Helmert編碼為反向Helmert編碼。更多資訊:
https://www.statsmodels.org/devel/contrasts.html
import numpy as np
import pandas as pd
from category_encoders import HelmertEncoder
# 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
train_set # 原始訓練集
test_set # 原始測試集
encoder = HelmertEncoder(cols=['Sex', 'Type'],
handle_unknown='indicator',
handle_missing='indicator').fit(train_set,train_y) # 在訓練集上訓練
encoded_train = encoder.transform(train_set) # 轉換訓練集
encoded_test = encoder.transform(test_set) # 轉換測試集
# 將 handle_unknown設為‘indicator’,即會新增一列指示未知特徵值
# 將 handle_missing設為‘indicator’,即會新增一列指示缺失值
# 其他的handle_unknown/handle_missing 的選擇為:
# ‘error’:即報錯; ‘return_nan’:即未知值/缺失之被標記為nan; ‘value’:即未知值/缺失之被標記為0
# 以測試集結果為例
encoded_test
# 在Helmert編碼中:
# 變數 Sex => 變為了4個新變數(包含常數項): 'male' => [ 1. -1. -1. -1.];
# 'female' => [ 1. 1. -1. -1.];
# 未知 => [ 1. 0. 0. 3.];
# 缺失 => [ 1. 0. 2. -1.];
# 變數 Type => 變為了5個新變數(包含常數項): 10 => [ 1. -1. -1. -1. -1.];
# 20 => [ 1. 1. -1. -1. -1.];,
# 15 => [ 1. 0. 2. -1. -1.];
# 未知 => [ 1. 0. 0. 0. 4.];
# 缺失 => [ 1. 0. 0. 3. -1.];
# 可以透過如下程式碼計算變數Type的Helmert 矩陣
from patsy.contrasts import Helmert
levels = [1,2,3,4,5] # 3個變數值 + 1個未知值 + 1個缺失值
contrast = Helmert().code_with_intercept(levels)
print(contrast.matrix) # 第一列為常數項
[[ 1. -1. -1. -1. -1.]
[ 1. 1. -1. -1. -1.]
[ 1. 0. 2. -1. -1.]
[ 1. 0. 0. 3. -1.]
[ 1. 0. 0. 0. 4.]]
encoded_train # 訓練集結果
1.2.5 Sum (Deviation) Encoding 偏差編碼
偏差編碼也通常在計量經濟學中被使用。偏差編碼後,線性模型的係數可以反映該給定該類別變數值的情況下因變數的平均值與全域性因變數的平均值的差異。更多資訊:
https://www.statsmodels.org/devel/contrasts.html
import numpy as np
import pandas as pd
from category_encoders.sum_coding import SumEncoder
# 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
train_set # 原始訓練集
test_set # 原始測試集
encoder = SumEncoder(cols=['Sex', 'Type'],
handle_unknown='indicator',
handle_missing='indicator').fit(train_set,train_y) # 在訓練集上訓練
encoded_train = encoder.transform(train_set) # 轉換訓練集
encoded_test = encoder.transform(test_set) # 轉換測試集
# 將 handle_unknown設為‘indicator’,即會新增一列指示未知特徵值
# 將 handle_missing設為‘indicator’,即會新增一列指示缺失值
# 其他的handle_unknown/handle_missing 的選擇為:
# ‘error’:即報錯; ‘return_nan’:即未知值/缺失之被標記為nan; ‘value’:即未知值/缺失之被標記為0
# 以測試集結果為例
encoded_test
# 在Helmert編碼中:
# 變數 Sex => 變為了4個新變數(包含常數項): 'male' => [ 1. 1. 0. 0.];
# 'female' => [ 1. 0. 1. 0.];
# 未知 => [ 1. -1. -1. -1.];
# 缺失 => [ 1. 0. 0. 1.];
# 變數 Type => 變為了5個新變數(包含常數項): 10 => [ 1. 1. 0. 0. 0.];
# 20 => [ 1. 0. 1. 0. 0.];,
# 15 => [ 1. 0. 0. 1. 0.];
# 未知 => [ 1. -1. -1. -1. -1.];
# 缺失 => [ 1. 0. 0. 0. 1.];
# 可以透過如下程式碼計算變數Type的Deviation 矩陣
from patsy.contrasts import Sum
levels = [1,2,3,4,5] # 3個變數值 + 1個未知值 + 1個缺失值
contrast = Sum().code_with_intercept(levels)
print(contrast.matrix) # 第一列為常數項
[[ 1. 1. 0. 0. 0.]
[ 1. 0. 1. 0. 0.]
[ 1. 0. 0. 1. 0.]
[ 1. 0. 0. 0. 1.]
[ 1. -1. -1. -1. -1.]]
encoded_train # 訓練集結果
1.2.6 Target Encoding 目標編碼
目標編碼是一種不僅基於特徵值本身,還基於相應因變數的類別變數編碼方法。對於分類問題:將類別特徵替換為給定某一特定類別值的因變數後驗機率與所有訓練資料上因變數的先驗機率的組合。對於連續目標:將類別特徵替換為給定某一特定類別值的因變數目標期望值與所有訓練資料上因變數的目標期望值的組合。該方法嚴重依賴於因變數的分佈,但這大大減少了生成編碼後特徵的數量。
公式:
其中min_samples_leaf和smoothing是使用者定義的引數;
min_samples_leaf:計算類別平均值時的最小樣本數(即若該類別出現次數少,則將被忽略),用以控制過擬合;
smoothing:平衡分類平均值與先驗平均值的平滑係數。其值越高,則正則化越強;
?′?是類別特徵X中類別為k的編碼值;
Prior Prob:目標變數的先驗機率/期望;
n:類別特徵X中,類別為k的樣本數;
?+:不僅在類別特徵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.target_encoder import TargetEncoder
# 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
train_set # 原始訓練集
test_set # 原始測試集
encoder = TargetEncoder(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 # 編碼後的變數數與原類別變數數一致
# 驗證一下計算的結果,在測試集中,‘male’類別的編碼值為 0.473106
prior = train_y.mean() # 先驗機率
min_samples_leaf = 1.0 # 預設為1.0
smoothing = 1.0 # 預設為1.0
n = 2 # 訓練集中,兩個樣本包含‘male’這個標籤
n_positive = 1 # 在訓練集中,這兩個包含‘male’標籤的樣本中僅有一個有正的因變數標籤
?????? = 1 / (1 + np.exp(-(n - min_samples_leaf) / smoothing))
male_encode = prior * (1-??????) + ?????? * n_positive/n
male_encode # return 0.4731058578630005,與要驗證的值吻合
0.4731058578630005
encoded_train # 訓練集結果
好了,以上就是關於靜態類別變數(上部分)的資料預處理介紹。建議讀者結合程式碼,在 Jupyter 中實操一遍。
目前該專案完整中文版正在製作中,請持續關注哦~
中文版 Jupyter 地址:
本文首發於公眾號:AI有道(ID: redstonewill),歡迎關注!