专栏 | 基于 Jupyter 的特征工程手册:特征选择(二)
作者:陳穎祥、楊子晗
編譯:AI有道
數(shù)據(jù)預(yù)處理后,我們生成了大量的新變量(比如獨熱編碼生成了大量僅包含0或1的變量)。但實際上,部分新生成的變量可能是多余:一方面它們本身不一定包含有用的信息,故無法提高模型性能;另一方面過這些多余變量在構(gòu)建模型時會消耗大量內(nèi)存和計算能力。因此,我們應(yīng)該進行特征選擇并選擇特征子集進行建模。
項目地址:
https://github.com/YC-Coder-Chen/feature-engineering-handbook
本文將介紹特征工程第一種算法:Filter Methods 過濾法(下)。
目錄:
1.1.1.5?Mutual Information (regression problem) 互信息 (回歸問題)
互信息(Mutual Information)衡量變量間的相互依賴性。其本質(zhì)為熵差,即 ????(????)?????(????|????),即知道另一個變量信息后混亂的降低程度 。當且僅當兩個隨機變量獨立時MI等于零。MI值越高,兩變量之間的相關(guān)性則越強。與Pearson相關(guān)和F統(tǒng)計量相比,它還捕獲了非線性關(guān)系。
公式:
若兩個變量均為離散變量:
p(????,????)(????,????) 為x和y的聯(lián)合概率質(zhì)量函數(shù) (PMF), p????(????)則為x的聯(lián)合概率質(zhì)量函數(shù) (PMF)。?
若兩個變量均為連續(xù)變量:
p(????,????)(????,????) 為x和y的聯(lián)合概率密度函數(shù) (PDF),p????(????)則為x的概率密度函數(shù) (PDF)。連續(xù)變量情形下,在實際操作中,往往先對數(shù)據(jù)離散化分桶,然后逐個桶進行計算。?
但是實際上,一種極有可能的情況是,x和y中的一個可能是離散變量,而另一個是連續(xù)變量。因此在sklearn中,它基于[1]和[2]中提出的基于k最臨近算法的熵估計非參數(shù)方法。?
[1] A. Kraskov, H. Stogbauer and P. Grassberger, “Estimating mutual information”. Phys. Rev. E 69, 2004.?
[2] B. C. Ross “Mutual Information between Discrete and Continuous Data Sets”. PLoS ONE 9(2), 2014.
import numpy as np from sklearn.feature_selection import mutual_info_regression from sklearn.feature_selection import SelectKBest# 直接載入數(shù)據(jù)集 from sklearn.datasets import fetch_california_housing dataset = fetch_california_housing() X, y = dataset.data, dataset.target # 利用 california_housing 數(shù)據(jù)集來演示 # 此數(shù)據(jù)集中,X,y均為連續(xù)變量,故此滿足使用MI的條件# 選擇前15000個觀測點作為訓(xùn)練集 # 剩下的作為測試集 train_set = X[0:15000,:].astype(float) test_set = X[15000:,].astype(float) train_y = y[0:15000].astype(float)# KNN中的臨近數(shù)是一個非常重要的參數(shù) # 故我們重寫了一個新的MI計算方程更好的來控制這一參數(shù) def udf_MI(X, y):result = mutual_info_regression(X, y, n_neighbors = 5) # 用戶可以輸入想要的臨近數(shù)return result# SelectKBest 將會基于一個判別方程自動選擇得分高的變量 # 這里的判別方程為F統(tǒng)計量 selector = SelectKBest(udf_MI, k=2) # k => 我們想要選擇的變量數(shù) selector.fit(train_set, train_y) # 在訓(xùn)練集上訓(xùn)練 transformed_train = selector.transform(train_set) # 轉(zhuǎn)換訓(xùn)練集 transformed_train.shape #(15000, 2), 其選擇了第一個及第八個變量 assert np.array_equal(transformed_train, train_set[:,[0,7]])transformed_test = selector.transform(test_set) # 轉(zhuǎn)換測試集 assert np.array_equal(transformed_test, test_set[:,[0,7]]); # 可見對于測試集,其依然選擇了第一個及第八個變量 # 驗算上述結(jié)果 for idx in range(train_set.shape[1]):score = mutual_info_regression(train_set[:,idx].reshape(-1,1), train_y, n_neighbors = 5)print(f"第{idx + 1}個變量與因變量的互信息為{round(score[0],2)}") # 故應(yīng)選擇第一個及第八個變量 第1個變量與因變量的互信息為0.37 第2個變量與因變量的互信息為0.03 第3個變量與因變量的互信息為0.1 第4個變量與因變量的互信息為0.03 第5個變量與因變量的互信息為0.02 第6個變量與因變量的互信息為0.09 第7個變量與因變量的互信息為0.37 第8個變量與因變量的互信息為0.461.1.1.6?Chi-squared Statistics (classification problem) 卡方統(tǒng)計量 (分類問題)
卡方統(tǒng)計量主要用于衡量兩個類別特征之間的相關(guān)性。sklearn提供了chi2方程用于計算卡方統(tǒng)計量。其輸入的特征變量必須為布爾值或頻率(故對于類別變量應(yīng)考慮獨熱編碼)。卡方統(tǒng)計量的零假設(shè)為兩個變量是獨立的,因為卡方統(tǒng)計量值越高,則兩個類別變量的相關(guān)性越強。因此,我們應(yīng)該選擇具有較高卡方統(tǒng)計量的特征。
公式:
其中, ????????,???? 為在變量X上具有i-th類別值且在變量Y上具有j-th類別值的實際觀測點計數(shù)。????????,???? 為利用概率估計的應(yīng)在在變量X上具有i-th類別值且在變量Y上具有j-th類別值的觀測點數(shù)量。n為總觀測數(shù), ???????? 為在變量X上具有i-th類別值的概率, ???????? 為在變量Y上具有j-th類別值的概率。
值得注意的是,通過解析源代碼,我們發(fā)現(xiàn)在sklearn中利用chi2計算出來的卡方統(tǒng)計量并不是統(tǒng)計意義上的卡方統(tǒng)計量。當輸入變量為布爾變量時,chi2計算值為該布爾變量為True時候的卡方統(tǒng)計量(我們將會在下文舉例說明)。這樣的優(yōu)勢是,獨熱編碼生成的所有布爾值變量的chi2值之和將等于原始變量統(tǒng)計意義上的卡方統(tǒng)計量。?
舉個簡單的例子,假設(shè)一個變量I有0,1,2兩種可能的值,則獨特編碼后一共會產(chǎn)生3個新的布爾值變量。這三個布爾值變量的chi2計算出來的值之和,將等于變量I與因變量直接計算得出的統(tǒng)計意義上的卡方統(tǒng)計量。
解析sklearn中chi2的計算
# 首先,隨機生成一個數(shù)據(jù)集 import pandas as pd sample_dict = {'Type': ['J','J','J','B','B','B','C','C','C','C','C'], 'Output': [0, 1, 0, 2, 0, 1, 0, 0, 1, 2, 2,]} sample_raw = pd.DataFrame(sample_dict) sample_raw #原始數(shù)據(jù),Output是我們的目標變量,Type為類別變量 # 下面利用獨熱編碼生成布爾變量,并利用sklearn計算每一個布爾變量的chi2值 sample = pd.get_dummies(sample_raw) from sklearn.feature_selection import chi2 chi2(sample.values[:,[1,2,3]],sample.values[:,[0]]) # 第一行為每一個布爾變量的chi2值 (array([0.17777778, 0.42666667, 1.15555556]),array([0.91494723, 0.8078868 , 0.56114397]))# 下面直接計算原始變量Type與output統(tǒng)計學(xué)意義上的卡方統(tǒng)計量 # 首先,先統(tǒng)計每一個類別下出現(xiàn)的觀測數(shù),用于創(chuàng)建列聯(lián)表 obs_df = sample_raw.groupby(['Type','Output']).size().reset_index() obs_df.columns = ['Type','Output','Count'] obs_df即列聯(lián)表(contingency table)為:
from scipy.stats import chi2_contingency obs = np.array([[1, 1, 1], [2, 1, 2],[2, 1, 0]]) chi2_contingency(obs) # 第一個值即為變量Type與output統(tǒng)計學(xué)意義上的卡方統(tǒng)計量 (1.7600000000000002,0.779791873961373,4,array([[1.36363636, 0.81818182, 0.81818182],[2.27272727, 1.36363636, 1.36363636],[1.36363636, 0.81818182, 0.81818182]]))# 而chi2方程算出來的布爾值之和為即為原始變量的統(tǒng)計意義上的卡方統(tǒng)計量 chi2(sample.values[:,[1,2,3]],sample.values[:,[0]])[0].sum() == chi2_contingency(obs)[0] True# 那么sklearn中的chi2是如何計算的呢? # 不妨以第一個生成的布爾值為例,即Type為B # chi2出來的值為0.17777778 # 而這與利用scipy以下代碼計算出的計算一致 from scipy.stats import chisquare f_exp = np.array([5/11, 3/11, 3/11]) * 3 # 預(yù)期頻數(shù)為 output的先驗概率 * Type為B 的樣本數(shù) chisquare([1,1,1], f_exp=f_exp) # [1,1,1] 即Type為B 的樣本實際頻數(shù) # 即sklearn 中的chi2 僅考慮了Type為B情形下的列連表 Power_divergenceResult(statistic=0.17777777777777778, pvalue=0.9149472287300311)如何利用sklearn 來進行特征選擇
import numpy as np from sklearn.feature_selection import chi2 from sklearn.feature_selection import SelectKBest# 直接載入數(shù)據(jù)集 from sklearn.datasets import load_iris # 利用iris數(shù)據(jù)作為演示數(shù)據(jù)集 iris = load_iris() X, y = iris.data, iris.target # 此數(shù)據(jù)集中,X為連續(xù)變量,y為類別變量 # 不滿足chi2的使用條件# 將連續(xù)變量變?yōu)椴紶栔底兞恳詽M足chi2使用條件 # 不妨利用其是否大于均值來生成布爾值(僅作為演示用) X = X > X.mean(0)# iris 數(shù)據(jù)集使用前需要被打亂順序 np.random.seed(1234) idx = np.random.permutation(len(X)) X = X[idx] y = y[idx]# 選擇前100個觀測點作為訓(xùn)練集 # 剩下的作為測試集 train_set = X[0:100,:] test_set = X[100:,] train_y = y[0:100]# sklearn 中直接提供了方程用于計算卡方統(tǒng)計量 # SelectKBest 將會基于一個判別方程自動選擇得分高的變量 # 這里的判別方程為F統(tǒng)計量 selector = SelectKBest(chi2, k=2) # k => 我們想要選擇的變量數(shù) selector.fit(train_set, train_y) # 在訓(xùn)練集上訓(xùn)練 transformed_train = selector.transform(train_set) # 轉(zhuǎn)換訓(xùn)練集 transformed_train.shape #(100, 2), 其選擇了第三個及第四個變量 assert np.array_equal(transformed_train, train_set[:,[2,3]])transformed_test = selector.transform(test_set) # 轉(zhuǎn)換測試集 assert np.array_equal(transformed_test, test_set[:,[2,3]]); # 可見對于測試集,其依然選擇了第三個及第四個變量 # 驗證上述結(jié)果 for idx in range(train_set.shape[1]):score, p_value = chi2(train_set[:,idx].reshape(-1,1), train_y)print(f"第{idx + 1}個變量與因變量的卡方統(tǒng)計量為{round(score[0],2)},p值為{round(p_value[0],3)}") # 故應(yīng)選擇第三個及第四個變量 第1個變量與因變量的卡方統(tǒng)計量為29.69,p值為0.0 第2個變量與因變量的卡方統(tǒng)計量為19.42,p值為0.0 第3個變量與因變量的卡方統(tǒng)計量為31.97,p值為0.0 第4個變量與因變量的卡方統(tǒng)計量為31.71,p值為0.01.1.1.7?F-Score (classification problem) F-統(tǒng)計量 (分類問題)
在分類機器學(xué)習(xí)問題中,若變量特征為類別特征,則我們可以使用獨熱編碼配合上述chi2方法選擇最重要的特征。但若特征為連續(xù)變量,則我們可以使用ANOVA-F值。ANOVA F統(tǒng)計量的零假設(shè)是若按目標變量(類別)分組,則連續(xù)變量的總體均值是相同的。故我們應(yīng)選擇具有高ANOVA-F統(tǒng)計量的連續(xù)變量,因為這些連續(xù)變量與目標變量的關(guān)聯(lián)性強。?
公式:
其中,SS(between)為組間的平方和,即組均值和總體均值之間的平方和。SS(error)是組內(nèi)的平方和,即數(shù)據(jù)與組均值之間的平方和。m是目標變量的總類別數(shù),n是觀測數(shù)。
import numpy as np from sklearn.feature_selection import f_classif from sklearn.feature_selection import SelectKBest# 直接載入數(shù)據(jù)集 from sklearn.datasets import load_iris # 利用iris數(shù)據(jù)作為演示數(shù)據(jù)集 iris = load_iris() X, y = iris.data, iris.target # 此數(shù)據(jù)集中,X為連續(xù)變量,y為類別變量 # 滿足ANOVA-F的使用條件# iris 數(shù)據(jù)集使用前需要被打亂順序 np.random.seed(1234) idx = np.random.permutation(len(X)) X = X[idx] y = y[idx]# 選擇前100個觀測點作為訓(xùn)練集 # 剩下的作為測試集 train_set = X[0:100,:] test_set = X[100:,] train_y = y[0:100]# sklearn 中直接提供了方程用于計算ANOVA-F # SelectKBest 將會基于一個判別方程自動選擇得分高的變量 # 這里的判別方程為F統(tǒng)計量 selector = SelectKBest(f_classif, k=2) # k => 我們想要選擇的變量數(shù) selector.fit(train_set, train_y) # 在訓(xùn)練集上訓(xùn)練 transformed_train = selector.transform(train_set) # 轉(zhuǎn)換訓(xùn)練集 transformed_train.shape #(100, 2), 其選擇了第三個及第四個變量 assert np.array_equal(transformed_train, train_set[:,[2,3]])transformed_test = selector.transform(test_set) # 轉(zhuǎn)換測試集 assert np.array_equal(transformed_test, test_set[:,[2,3]]); # 可見對于測試集,其依然選擇了第三個及第四個變量 # 驗證上述結(jié)果 for idx in range(train_set.shape[1]):score, p_value = f_classif(train_set[:,idx].reshape(-1,1), train_y)print(f"第{idx + 1}個變量與因變量的ANOVA-F統(tǒng)計量為{round(score[0],2)},p值為{round(p_value[0],3)}") # 故應(yīng)選擇第三個及第四個變量 第1個變量與因變量的ANOVA-F統(tǒng)計量為91.39,p值為0.0 第2個變量與因變量的ANOVA-F統(tǒng)計量為33.18,p值為0.0 第3個變量與因變量的ANOVA-F統(tǒng)計量為733.94,p值為0.0 第4個變量與因變量的ANOVA-F統(tǒng)計量為608.95,p值為0.01.1.1.7?Mutual Information (classification problem) 互信息 (分類問題)
【與1.1.1.5一樣】互信息(Mutual Information)衡量變量間的相互依賴性。其本質(zhì)為熵差,即 ????(????)?????(????|????),即知道另一個變量信息后混亂的降低程度 。當且僅當兩個隨機變量獨立時MI等于零。MI值越高,兩變量之間的相關(guān)性則越強。與Pearson相關(guān)和F統(tǒng)計量相比,它還捕獲了非線性關(guān)系。?
公式:?
若兩個變量均為離散變量:
p(????,????)(????,????) 為x和y的聯(lián)合概率質(zhì)量函數(shù) (PMF), p????(????)則為x的的聯(lián)合概率質(zhì)量函數(shù) (PMF)。?
若兩個變量均為連續(xù)變量:
p(????,????)(????,????) 為x和y的聯(lián)合概率密度函數(shù) (PDF),p????(????)則為x的的聯(lián)合概率密度函數(shù) (PDF)。連續(xù)變量情形下,在實際操作中,往往先對數(shù)據(jù)離散化分桶,然后逐個桶進行計算。?
但是實際上,一種極有可能的情況是,x和y中的一個可能是離散變量,而另一個是連續(xù)變量。因此在sklearn中,它基于[1]和[2]中提出的基于k最臨近算法的熵估計非參數(shù)方法。?
[1] A. Kraskov, H. Stogbauer and P. Grassberger, “Estimating mutual information”. Phys. Rev. E 69, 2004.?
[2] B. C. Ross “Mutual Information between Discrete and Continuous Data Sets”. PLoS ONE 9(2), 2014.
import numpy as np from sklearn.feature_selection import mutual_info_classif from sklearn.feature_selection import SelectKBest# 直接載入數(shù)據(jù)集 from sklearn.datasets import load_iris # 利用iris數(shù)據(jù)作為演示數(shù)據(jù)集 iris = load_iris() X, y = iris.data, iris.target # 此數(shù)據(jù)集中,X為連續(xù)變量,y為類別變量 # 滿足MI的使用條件# iris 數(shù)據(jù)集使用前需要被打亂順序 np.random.seed(1234) idx = np.random.permutation(len(X)) X = X[idx] y = y[idx]# 選擇前100個觀測點作為訓(xùn)練集 # 剩下的作為測試集 train_set = X[0:100,:] test_set = X[100:,] train_y = y[0:100]# KNN中的臨近數(shù)是一個非常重要的參數(shù) # 故我們重寫了一個新的MI計算方程更好的來控制這一參數(shù) def udf_MI(X, y):result = mutual_info_classif(X, y, n_neighbors = 5) # 用戶可以輸入想要的臨近數(shù)return result# SelectKBest 將會基于一個判別方程自動選擇得分高的變量 # 這里的判別方程為F統(tǒng)計量 selector = SelectKBest(udf_MI, k=2) # k => 我們想要選擇的變量數(shù) selector.fit(train_set, train_y) # 在訓(xùn)練集上訓(xùn)練 transformed_train = selector.transform(train_set) # 轉(zhuǎn)換訓(xùn)練集 transformed_train.shape #(100, 2), 其選擇了第三個及第四個變量 assert np.array_equal(transformed_train, train_set[:,[2,3]])transformed_test = selector.transform(test_set) # 轉(zhuǎn)換測試集 assert np.array_equal(transformed_test, test_set[:,[2,3]]); # 可見對于測試集,其依然選擇了第三個及第四個變量 # 驗算上述結(jié)果 for idx in range(train_set.shape[1]):score = mutual_info_classif(train_set[:,idx].reshape(-1,1), train_y, n_neighbors = 5)print(f"第{idx + 1}個變量與因變量的互信息為{round(score[0],2)}") # 故應(yīng)選擇第三個及第四個變量 第1個變量與因變量的互信息為0.56 第2個變量與因變量的互信息為0.28 第3個變量與因變量的互信息為0.99 第4個變量與因變量的互信息為1.02專欄系列:
專欄 | 基于 Jupyter 的特征工程手冊:數(shù)據(jù)預(yù)處理(一)
專欄 | 基于 Jupyter 的特征工程手冊:數(shù)據(jù)預(yù)處理(二)
專欄 | 基于 Jupyter 的特征工程手冊:數(shù)據(jù)預(yù)處理(三)
專欄 | 基于 Jupyter 的特征工程手冊:數(shù)據(jù)預(yù)處理(四)
專欄 | 基于 Jupyter 的特征工程手冊:特征選擇(一)
目前該項目完整中文版正在制作中,請持續(xù)關(guān)注哦~
中文版 Jupyter 地址:
https://github.com/YC-Coder-Chen/feature-engineering-handbook/blob/master/中文版/2.%20特征選擇.ipynb
推薦閱讀
(點擊標題可跳轉(zhuǎn)閱讀)
干貨 | 公眾號歷史文章精選
我的深度學(xué)習(xí)入門路線
我的機器學(xué)習(xí)入門路線圖
算法工程師必備!
AI有道年度技術(shù)文章電子版PDF來啦!
掃描下方二維碼,添加?AI有道小助手微信,可申請入群,并獲得2020完整技術(shù)文章合集PDF(一定要備注:入群?+ 地點 + 學(xué)校/公司。例如:入群+上海+復(fù)旦。?
長按掃碼,申請入群
(添加人數(shù)較多,請耐心等待)
?
最新 AI 干貨,我在看?
總結(jié)
以上是生活随笔為你收集整理的专栏 | 基于 Jupyter 的特征工程手册:特征选择(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 专栏 | 基于 Jupyter 的特征工
- 下一篇: 135 页的《机器学习速查手册》,公式、