特徵工程(feature engineering):利用領域知識和現有數據,創造出新的特徵,用於機器學習算法;能夠手動(manual)或自動(automated)。神經網絡的自動特徵工程,經常不適用於現實中其餘的複雜任務。所以,本文主要針對數據挖掘以及傳統的機器學習,不會涉及圖像識別、天然語言處理等深度學習領域。
俗話說:數據與特徵工程決定了模型的上限,改進算法只不過是逼近這個上限而已。
在豆瓣圖書頻道,搜索‘特徵工程’關鍵詞,搜索結果是僅有兩本評分在7分以上數據,分別是精通特徵工程、特徵工程入門與實踐
當數據維度有限,那‘特徵工程’是很是重要的,但發表這方面文章較少,更沒全局化、系統化講解特徵工程理論和實戰等,因此我想結合工做經驗、相關書籍、優秀文章等總結的一套通用的數據科學-特徵工程方法論。
從這 newcommandcolorful{color{red}} colorful{特徵理解、特徵清洗、特徵構造、特徵變換} 等維度展開,逐步講解理論和代碼實現等,針對代碼實現部分 不能公開公司相關數據,因此選擇用泰坦尼克號公開數據。html
import pandas as pd import numpy as np import seaborn as sns df_titanic = sns.load_dataset('titanic')
數據字段描述以下:
python
1.1 區分結構化數據與非結構化數據
如一些以表格形式進行存儲的數據,都是結構化數據;而非結構化數據就是一堆數據,相似於文本、報文、日誌之類的。
1.2 區分定量和定性數據git
目標是提升數據質量,下降算法錯誤建模的風險。
現實的業務建模過程當中,數據經常存在各類問題,數據存在不徹底的、有噪聲的、不一致的等各類狀況。而這些帶有錯誤信息的數據會對模型形成不利的影響。
數據清洗過程包括數據對齊、缺失值處理、異常值處理、數據轉化等數據處理。
2.1 數據對齊
主要有時間、字段以及相關量綱的對齊。
1) 時間:github
2) 字段:算法
3) 量綱:編程
主要包括少許缺失的狀況下,考慮不處理或刪除缺失數據或者採用均值、中位數、衆數、同類均值填充。
當缺失值對模型影響比較大,存在比較多的不缺失數據的狀況下,能夠採用模型預測或者插值的方式。當缺失值過多時,能夠對缺失值進行編碼操做。
對每一個字段都計算其缺失值比例,而後按照缺失比例和字段重要性,分別制定策略,可用下圖表示:
空值彙總分佈segmentfault
df_titanic.isnull().sum() survived 0 pclass 0 sex 0 age 177 sibsp 0 parch 0 fare 0 embarked 2 class 0 who 0 adult_male 0 deck 688 embark_town 2 alive 0 alone 0
1) 刪除元組
將存在遺漏信息屬性值的對象(元組,記錄)刪除,從而獲得一個完備的信息表。
優勢:
簡單易行,在對象有多個屬性缺失值、被刪除的含缺失值的對象與初始數據集的數據量相比很是小的狀況下有效;
不足:
當缺失數據所佔比例較大,特別當遺漏數據非隨機分佈時,這種方法可能致使數據發生偏離,從而引出錯誤的結論。
代碼實現
embark_town 字段 有 2 個空值,能夠考慮刪除缺失處理下數組
df_titanic[df_titanic["embark_town"].isnull()] df_titanic.dropna(axis=0,how='any',subset=['embark_town'],inplace=True)
2) 數據填充
用必定的值去填充空值,從而使信息表完備化。一般基於統計學原理,根據初始數據集中其他對象取值的分佈狀況來對一個缺失值進行填充。
(a) 人工填充(filling manually)
根據業務知識來進行人工填充。
(b) 特殊值填充(Treating Missing Attribute values as Special values)
將空值做爲一種特殊的屬性值來處理,它不一樣於其餘的任何屬性值。如全部的空值都用「unknown」填充。通常做爲臨時填充或中間過程。
代碼實現網絡
df_titanic['embark_town'].fillna('unknown', inplace=True)
(c) 統計量填充
若缺失率較低(小於 95%)且重要性較低,則根據數據分佈的狀況進行填充。
經常使用填充統計量:
平均值:對於數據符合均勻分佈,用該變量的均值填補缺失值。
中位數:對於數據存在傾斜分佈的狀況,採用中位數填補缺失值。
衆數:離散特徵可以使用衆數進行填充缺失值。數據結構
fare:缺失值較多,使用中位數填充。
df_titanic['fare'].fillna(df_titanic['fare'].median(), inplace=True)
embarked:只有兩個缺失值,使用衆數填充
df_titanic['embarked'].isnull().sum() 執行結果:2 df_titanic['embarked'].fillna(df_titanic['embarked'].mode(), inplace=True) df_titanic['embarked'].value_counts() 執行結果: S 64
imputer 類提供了缺失數值處理的基本策略,好比使用缺失數值所在行或列的均值、中位數、衆數來替代缺失值。該類也兼容不一樣的缺失值編碼。
填補缺失值:sklearn.preprocessing.Imputer(missing_values=’NaN’, strategy=’mean’, axis=0, verbose=0, copy=True)
主要參數說明:
missing_values:缺失值,能夠爲整數或 NaN(缺失值 numpy.nan 用字符串‘NaN’表示),默認爲 NaN strategy:替換策略,字符串,默認用均值‘mean’替換 ① 若爲mean時,用特徵列的均值替換 ② 若爲median時,用特徵列的中位數替換 ③ 若爲most_frequent時,用特徵列的衆數替換 axis:指定軸數,默認 axis=0 表明列,axis=1 表明行 copy:設置爲 True 表明不在原數據集上修改,設置爲 False 時,就地修改,存在以下狀況時,即便設置爲 False 時,也不會就地修改 ① X不是浮點值數組 ② X是稀疏且missing_values=0 ③ axis=0且X爲CRS矩陣 ④ axis=1且X爲CSC矩陣 statistics_屬性:axis 設置爲 0 時,每一個特徵的填充值數組,axis=1 時,報沒有該屬性錯誤
age:根據 sex、pclass 和 who 分組,若是落在相同的組別裏,就用這個組別的均值或中位數填充。
df_titanic.groupby(['sex', 'pclass', 'who'])['age'].mean() 執行結果: sex pclass who female 1 child 10.333333 woman 35.500000 2 child 6.600000 woman 32.179688 3 child 7.100000 woman 27.854167 male 1 child 5.306667 man 42.382653 2 child 2.258889 man 33.588889 3 child 6.515000 man 28.995556 Name: age, dtype: float64 age_group_mean = df_titanic.groupby(['sex', 'pclass', 'who'])['age'].mean().reset_index() age_group_mean 執行結果: sex pclass who age 0 female 1 child 10.333333 1 female 1 woman 35.500000 2 female 2 child 6.600000 3 female 2 woman 32.179688 4 female 3 child 7.100000 5 female 3 woman 27.854167 6 male 1 child 5.306667 7 male 1 man 42.382653 8 male 2 child 2.258889 9 male 2 man 33.588889 10 male 3 child 6.515000 11 male 3 man 28.995556 def select_group_age_median(row): condition = ((row['sex'] == age_group_mean['sex']) & (row['pclass'] == age_group_mean['pclass']) & (row['who'] == age_group_mean['who'])) return age_group_mean[condition]['age'].values[0] df_titanic['age'] =df_titanic.apply( lambda x: select_group_age_median(x) if np.isnan(x['age']) else x['age'],axis=1) 執行結果: 0 22.000000 1 38.000000 2 26.000000 3 35.000000 4 35.000000 ... 886 27.000000 887 19.000000 888 27.854167 889 26.000000 890 32.000000 sns.distplot(df_titani
(d) 模型預測填充
使用待填充字段做爲 Label,沒有缺失的數據做爲訓練數據,創建分類/迴歸模型,對待填充的缺失字段進行預測並進行填充。
最近距離鄰法(KNN)
先根據歐式距離或相關分析來肯定距離具備缺失數據樣本最近的 K 個樣本,將這 K 個值加權平均/投票來估計該樣本的缺失數據。
迴歸(Regression)
基於完整的數據集,創建迴歸方程。對於包含空值的對象,將已知屬性值代入方程來估計未知屬性值,以此估計值來進行填充。當變量不是線性相關時會致使有誤差的估計,經常使用線性迴歸。
代碼實現
age:缺失量較大,用 sex、pclass、who、fare、parch、sibsp 六個特徵構建隨機森林模型,填充年齡缺失值。
df_titanic_age = df_titanic[['age', 'pclass', 'sex', 'who','fare', 'parch', 'sibsp']] df_titanic_age = pd.get_dummies(df_titanic_age) df_titanic_age.head() 執行結果爲 age pclass fare parch sibsp sex_female sex_male who_child who_man who_woman 0 22.0 3 7.2500 0 1 0 1 0 1 0 1 38.0 1 71.2833 0 1 1 0 0 0 1 2 26.0 3 7.9250 0 0 1 0 0 0 1 3 35.0 1 53.1000 0 1 1 0 0 0 1 4 35.0 3 8.0500 0 0 0 1 0 1 0 # 乘客分紅已知年齡和未知年齡兩部分 known_age = df_titanic_age[df_titanic_age.age.notnull()] unknown_age = df_titanic_age[df_titanic_age.age.isnull()] # y 即目標年齡 y_for_age = known_age['age'] # X 即特徵屬性值 X_train_for_age = known_age.drop(['age'], axis=1) X_test_for_age = unknown_age.drop(['age'], axis=1) from sklearn.ensemble import RandomForestRegressor rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1) rfr.fit(X_train_for_age, y_for_age) # 用獲得的模型進行未知年齡結果預測 y_pred_age = rfr.predict(X_test_for_age) # 用獲得的預測結果填補原缺失數據 df_titanic.loc[df_titanic.age.isnull(), 'age'] = y_pred_age sns.distplot(df_titanic.age)
(e) 插值法填充
包括隨機插值,多重插補法,熱平臺插補,拉格朗日插值,牛頓插值等。
使用插值法能夠計算缺失值的估計值,所謂的插值法就是經過兩點(x0,y0),(x1,y1)估計中間點的值,假設 y=f(x)是一條直線,經過已知的兩點來計算函數 f(x),而後只要知道 x 就能求出 y,以此方法來估計缺失值。
.interpolate(method = 'linear', axis) 方法將經過 linear 插值使用沿着給定 axis 的值替換 NaN 值, 這個差值也就是先後或者上下的中間值
df_titanic['fare'].interpolate(method = 'linear', axis = 0)
同時,也可用行值插入
df_titanic['fare'].interpolate(method = 'linear', axis = 1)
代碼實現
df_titanic['fare'].interpolate()
多值插補的思想來源於貝葉斯估計,認爲待插補的值是隨機的,它的值來自於已觀測到的值。具體實踐上一般是估計出待插補的值,而後再加上不一樣的噪聲,造成多組可選插補值。根據某種選擇依據,選取最合適的插補值。
多重插補方法分爲三個步驟:
Step1:爲每一個空值產生一套可能的插補值,這些值反映了無響應模型的不肯定性;
每一個值均可以被用來插補數據集中的缺失值,產生若干個完整數據集合;
Step2:每一個插補數據集合都用針對完整數據集的統計方法進行統計分析;
Step3:對來自各個插補數據集的結果,根據評分函數進行選擇,產生最終的插補值。
(f) 啞變量填充
若變量是離散型,且不一樣值較少,可轉換成啞變量,例如性別 SEX 變量,存在 male,fameal,NA 三個不一樣的值,可將該列轉換成 IS_SEX_MALE、IS_SEX_FEMALE、IS_SEX_NA。若某個變量存在十幾個不一樣的值,可根據每一個值的頻數,將頻數較小的值歸爲一類’other’,下降維度。此作法可最大化保留變量的信息。
代碼實現
sex_list = ['MALE', 'FEMALE', np.NaN, 'FEMALE', 'FEMALE', np.NaN, 'MALE'] df = pd.DataFrame({'SEX': sex_list}) display(df) df.fillna('NA', inplace=True) df = pd.get_dummies(df['SEX'],prefix='IS_SEX') display(df) # 原始數據 SEX 0 MALE 1 FEMALE 2 NaN 3 FEMALE 4 FEMALE 5 NaN 6 MALE # 填充後 IS_SEX_FEMALE IS_SEX_MALE IS_SEX_NA 0 0 1 0 1 1 0 0 2 0 0 1 3 1 0 0 4 1 0 0 5 0 0 1 6 0 1
(g) 當特徵值缺失超過 80%以上,建議刪除【或變成是否變量】,容易影響模型效果
df_titanic.drop(["deck"],axis=1)
2.3 異常處理:
1) 異常值識別
sns.catplot(y="fare",x="survived", kind="box", data=df_titanic,palette="Set2");
sns.distplot(df_titanic.age)
(a) 基於統計分析
一般用戶用某個統計分佈對數據點進行建模,再以假定的模型,根據點的分佈來肯定是否異常。
如經過分析統計數據的散度狀況,即數據變異指標,對數據的分佈狀況有所瞭解,進而經過數據變異指標來發現數據中的異常點數據。
經常使用的數據變異指標有極差、四分位數間距、均差、標準差、變異係數等等,如變異指標的值大表示變異大、散佈廣;值小表示離差小,較密集。
譬如最大最小值能夠用來判斷這個變量的取值是否超過了合理的範圍,如客戶的年齡爲-20 歲或 200 歲,爲異常值。
(b) 3σ原則
若數據存在正態分佈,在 3σ原則下,異常值爲一組測定值中與平均值的誤差超過3倍標準差的值。若是數據服從正態分佈,距離平均值3σ以外的值出現的機率爲P(|x - μ| > 3σ) <= 0.003,屬於極個別的小几率事件。若是數據不服從正態分佈,也能夠用遠離平均值的多少倍標準差來描述。
(c) 箱線圖分析
箱線圖提供了識別異常值的一個標準:若是一個值小於 Q1-1.5IQR 或大於 Q3+1.5IQR 的值,則被稱爲異常值。
Q1 爲下四分位數,表示所有觀察值中有四分之一的數據取值比它小;
Q4 爲上四分位數,表示所有觀察值中有四分之一的數據取值比它大;
IQR 爲四分位數間距,是上四分位數 Q1 與下四分位數 Q3 的差值,包含了所有觀察值的一半。
箱型圖判斷異常值的方法以四分位數和四分位距爲基礎,四分位數具備魯棒性:25%的數據能夠變得任意遠而且不會干擾四分位數,因此異常值不能對這個標準施加影響。所以箱型圖識別異常值比較客觀,在識別異常值時有必定的優越性。
(d) 基於模型檢測
首先創建一個數據模型,異常是那些同模型不能完美擬合的對象;若是模型是簇的集合,則異常是不顯著屬於任何簇的對象;在使用迴歸模型時,異常是相對遠離預測值的對象。
優勢:
有堅實的統計學理論基礎,當存在充分的數據和所用的檢驗類型的知識時,這些檢驗可能很是有效。
缺點:
對於多元數據,可用的選擇少一些,而且對於高維數據,這些檢測可能性不好。
(e) 基於距離
基於距離的方法是基於下面這個假設:即若一個數據對象和大多數點距離都很遠,那這個對象就是異常。經過定義對象之間的臨近性度量,根據距離判斷異常對象是否遠離其餘對象,主要使用的距離度量方法有絕對距離(曼哈頓距離)、歐氏距離和馬氏距離等方法。
優勢:
基於距離的方法比基於統計類方法要簡單得多;
由於爲一個數據集合定義一個距離的度量要比肯定數據集合的分佈容易的多。
缺點:
基於鄰近度的方法須要 O(m2)時間,大數據集不適用;
該方法對參數的選擇也是敏感的;
不能處理具備不一樣密度區域的數據集,由於它使用全局閾值,不能考慮這種密度的變化。
(f) 基於密度
考察當前點周圍密度,能夠發現局部異常點,離羣點的局部密度顯著低於大部分近鄰點,適用於非均勻的數據集。
優勢:
給出了對象是離羣點的定量度量,而且即便數據具備不一樣的區域也可以很好的處理。
缺點:
與基於距離的方法同樣,這些方法必然具備 O(m2)的時間複雜度。
對於低維數據使用特定的數據結構能夠達到 O(mlogm);
參數選擇困難。
雖然算法經過觀察不一樣的 k 值,取得最大離羣點得分來處理該問題,可是,仍然須要選擇這些值的上下界。
(g) 基於聚類
對象是否被認爲是異常點可能依賴於簇的個數(如 k 很大時的噪聲簇)。該問題也沒有簡單的答案。一種策略是對於不一樣的簇個數重複該分析。另外一種方法是找出大量小簇,其想法是:
較小的簇傾向於更加凝聚;
若是存在大量小簇時一個對象是異常點,則它多半是一個真正的異常點。
不利的一面是一組異常點可能造成小簇而逃避檢測。
優勢:
基於線性和接近線性複雜度(k 均值)的聚類技術來發現離羣點多是高度有效的;
簇的定義一般是離羣點的補,所以可能同時發現簇和離羣點。
缺點:
產生的離羣點集和它們的得分可能很是依賴所用的簇的個數和數據中離羣點的存在性;
聚類算法產生的簇的質量對該算法產生的離羣點的質量影響很是大。
(h) 基於鄰近度的異常點檢測
一個對象是異常的,若是它遠離大部分點。這種方法比統計學方法更通常、更容易使用,由於肯定數據集的有意義的鄰近性度量比肯定它的統計分佈更容易。一個對象的異常點得分由到它的 k-最近鄰的距離給定。異常點得分對 k 的取值高度敏感。若是 k 過小(例如 1),則少許的鄰近異常異常點可能致使較異常低的異常點得分;若是 K 太大,則點數少於 k 的簇中全部的對象可能都成了異常異常點。爲了使該方案對於 k 的選取更具備魯棒性,可使用 k 個最近鄰的平均距離。
優勢:
簡單
缺點:
基於鄰近度的方法須要 O(m2)時間,大數據集不適用;
該方法對參數的選擇也是敏感的;
不能處理具備不一樣密度區域的數據集,由於它使用全局閾值,不能考慮這種密度的變化。
總結:
在數據處理階段將離羣點做爲影響數據質量的異常點考慮,而不是做爲一般所說的異常檢測目標點,通常採用較爲簡單直觀的方法,結合箱線圖和 MAD 的統計方法判斷變量的離羣點。
sns.scatterplot(x="fare", y="age", hue="survived",data=df_titanic,palette="Set1")
2) 處理方法
對異常值處理,須要具體狀況具體分析,異常值處理的方法經常使用有四種:
目標是加強數據表達,添加先驗知識。
若是咱們對變量進行處理以後,效果仍不是很是理想,就須要進行特徵構建了,也就是衍生新變量。
1) 基於業務規則、先驗知識等構建新特徵
2) 四分位數、中位數、平均值、標準差、誤差、偏度、偏鋒、離散系統
3) 構造長、短時間統計量(如 周、月)
4) 時間衰減(越靠近觀測權重值高)
def age_bin(x): if x <= 18: return 'child' elif x <= 30: return 'young' elif x <= 55: return 'midlife' else: return 'old' df_titanic['age_bin'] = df_titanic['age'].map(age_bin) df_titanic['age_bin'].unique() 執行結果: array(['young', 'midlife', 'child', 'old'], dtype=object)
df_titanic['title'] = df_titanic['name'].map( lambda x: x.split(',')[1].split('.')[0].strip()) df_titanic['title'].value_counts() 執行結果: Mr 757 Miss 260 Mrs 197 Master 61 Rev 8 Dr 8 Col 4 Ms 2 Major 2 Mlle 2 Dona 1 Sir 1 Capt 1 Don 1 Lady 1 Mme 1 the Countess 1 Jonkheer 1 # 再根據這些 title 細分,是官員,仍是皇室,仍是女士、先生、小姐 df_titanic['title'].unique() 執行結果: array(['Mr', 'Mrs', 'Miss', 'Master', 'Don', 'Rev', 'Dr', 'Mme', 'Ms', 'Major', 'Lady', 'Sir', 'Mlle', 'Col', 'Capt', 'the Countess', 'Jonkheer', 'Dona'], dtype=object) title_dictionary = { "Mr": "Mr", "Mrs": "Mrs", "Miss": "Miss", "Master": "Master", "Don": "Royalty", "Rev": "Officer", "Dr": "Officer", "Mme": "Mrs", "Ms": "Mrs", "Major": "Officer", "Lady": "Royalty", "Sir": "Royalty", "Mlle": "Miss", "Col": "Officer", "Capt": "Officer", "the Countess": "Royalty", "Jonkheer": "Royalty", "Dona": 'Mrs' } df_titanic['title'] = df_titanic['title'].map(title_dictionary) df_titanic['title'].value_counts() 執行結果: Mr 757 Miss 262 Mrs 201 Master 61 Officer 23 Royalty 5
df_titanic['family_size'] = df_titanic['sibsp'] + df_titanic['parch'] + 1 df_titanic['family_size'].head() 執行結果: 0 2 1 2 2 1 3 2 4 1
1) 前n個週期/天/月/年的週期值,如過去5天分位數、平均值等
2) 同比/環比
(a) 自定義分箱
指根據業務經驗或者常識等自行設定劃分的區間,而後將原始數據歸類到各個區間中。
(b) 等距分箱
按照相同寬度將數據分紅幾等份。
從最小值到最大值之間,均分爲 N 等份, 這樣, 若是 A,B 爲最小最大值, 則每一個區間的長度爲 W=(B−A)/N , 則區間邊界值爲A+W,A+2W,….A+(N−1)W 。這裏只考慮邊界,每一個等份裏面的實例數量可能不等。
缺點是受到異常值的影響比較大
(c) 等頻分箱
將數據分紅幾等份,每等份數據裏面的個數是同樣的。
區間的邊界值要通過選擇,使得每一個區間包含大體相等的實例數量。好比說 N=10 ,每一個區間應該包含大約10%的實例。
# qcut 等頻率分箱 df_titanic['fare_bin'], bins = pd.qcut(df_titanic['fare'], 5, retbins=True) df_titanic['fare_bin'].value_counts() (7.854, 10.5] 184 (21.679, 39.688] 180 (-0.001, 7.854] 179 (39.688, 512.329] 176 (10.5, 21.679] 172 bins #array([ 0. , 7.8542, 10.5 , 21.6792, 39.6875, 512.3292]) def fare_cut(age): if age <= 7.8958: return 0 if age <= 10.5: return 1 if age <= 21.6792: return 2 if age <= 39.6875: return 3 return 4 df_titanic['fare_bin'] = df_titanic['fare'].map(fare_cut) # cut 等距離分箱 bins = [0, 12, 18, 65, 100] pd.cut(df_titanic['age'], bins).value_counts
1.將特徵值值進行從小到大的排序。
2.計算出KS最大的那個值,即爲切點,記爲D。而後把數據切分紅兩部分。
3.重複步驟2,進行遞歸,D左右的數據進一步切割。直到KS的箱體數達到咱們的預設閾值便可。
4.連續型變量:分箱後的KS值<=分箱前的KS值
5.分箱過程當中,決定分箱後的KS值是某一個切點,而不是多個切點的共同做用。這個切點的位置是原始KS值最大的位置。
注:代碼實現請從網上查閱
3) 卡方分桶
自底向上的(即基於合併的)數據離散化方法。它依賴於卡方檢驗:具備最小卡方值的相鄰區間合併在一塊兒,直到知足肯定的中止準則。
基本思想
對於精確的離散化,相對類頻率在一個區間內應當徹底一致。所以,若是兩個相鄰的區間具備很是相似的類分佈,則這兩個區間能夠合併;不然,它們應當保持分開。而低卡方值代表它們具備類似的類分佈。
實現步驟
Step 1:預先定義一個卡方的閾值;
Step 2:初始化;根據要離散的屬性對實例進行排序,每一個實例屬於一個區間;
Step 3:合併區間;
計算每一對相鄰區間的卡方值;
將卡方值最小的一對區間合併;
Aij:第i區間第j類的實例的數量;Eij:Aij的指望頻率(=(Ni*Cj)/N),N是總樣本數,Ni是第i組的樣本數,Cj是第j類樣本在全體中的比例;
閾值的意義
類別和屬性獨立時,有90%的可能性,計算獲得的卡方值會小於4.6。大於閾值4.6的卡方值就說明屬性和類不是相互獨立的,不能合併。若是閾值選的大,區間合併就會進行不少次,離散後的區間數量少、區間大。
注意
ChiMerge算法推薦使用0.90、0.9五、0.99置信度,最大區間數取10到15之間; 也能夠不考慮卡方閾值,此時能夠考慮最小區間數或者最大區間數。 指定區間數量的上限和下限,最多幾個區間,最少幾個區間; 對於類別型變量,須要分箱時須要按照某種方式進行排序。
代碼實現
https://github.com/tatsumiw/C...
須要使總熵值達到最小,也就是使分箱可以最大限度地區分因變量的各種別。
熵是信息論中數據無序程度的度量標準,提出信息熵的基本目的是找出某種符號系統的信息量和冗餘度之間的關係,以便能用最小的成本和消耗來實現最高效率的數據存儲、管理和傳遞。
數據集的熵越低,說明數據之間的差別越小,最小熵劃分就是爲了使每箱中的數據具備最好的類似性。給定箱的個數,若是考慮全部可能的分箱狀況,最小熵方法獲得的箱應該是具備最小熵的分箱。
注:有限考慮強特徵維度
1) 離散+離散:笛卡爾積
2) 離散+連續:連續特徵分桶後進行笛卡爾積或基於類別特徵 group by,相似於聚類特徵構造
3) 連續+連續:加減乘除,二階差分等
df_titanic_numerical = df_titanic[['age','sibsp','parch','fare','family_size']] df_titanic_numerical.head() 執行結果: age sibsp parch fare family_size 0 22.0 1 0 7.2500 2 1 38.0 1 0 71.2833 2 2 26.0 0 0 7.9250 1 3 35.0 1 0 53.1000 2 4 35.0 0 0 8.0500 1 # 擴展數值特徵 from sklearn.preprocessing import PolynomialFeatures poly = PolynomialFeatures(degree=2, include_bias=False, interaction_only=False) df_titanic_numerical_poly = poly.fit_transform(df_titanic_numerical) pd.DataFrame(df_titanic_numerical_poly, columns=poly.get_feature_names()).hea
查看下衍生新變量後的相關性狀況,顏色越深相關性越大:
sns.heatmap(pd.DataFrame(df_titanic_numerical_poly, columns=poly.get_feature_names()).corr())
目標是下降噪聲,平滑預測能力和計算複雜度,加強模型預測性能
當數據預處理完成後,咱們須要選擇有意義的特徵輸入機器學習的算法和模型進行訓練。
一般來講,從兩個方面考慮來選擇特徵:
根據特徵選擇的形式又能夠將特徵選擇方法分爲 3 種:
1) 方差過濾
這是經過特徵自己的方差來篩選特徵的類。好比一個特徵自己的方差很小,就表示樣本在這個特徵上基本沒有差別,可能特徵中的大多數值都同樣,甚至整個特徵的取值都相同,那這個特徵對於樣本區分沒有什麼做用。因此不管接下來的特徵工程要作什麼,都要優先消除方差爲 0 的特徵。VarianceThreshold 有重要參數 threshold,表示方差的閾值,表示捨棄全部方差小於 threshold 的特徵,不填默認爲 0,即刪除全部的記錄都相同的特徵。
from sklearn.feature_selection import VarianceThreshold variancethreshold = VarianceThreshold() #實例化,默認方差爲 0.方差<=0 的過濾掉 df_titanic_numerical = df_titanic[['age','sibsp','parch','fare','family_size']] X_var = variancethreshold.fit_transform(df_titanic_numerical) #獲取刪除不合格特徵後的新特徵矩陣 variancethreshold.variances_ array([ 79.58,1.21467827,0.64899903,512.3292,2.60032675]) del_list = df_titanic_numerical.columns[variancethreshold.get_support()==0].to_list() #得到刪除
然 而,若是咱們知道咱們須要多少個特徵,方差也能夠幫助咱們將特徵選擇一步到位。
好比說,咱們但願留下一半的特徵,那能夠設定一個讓特徵總數減半的方差閾值,只要找到特徵方差的中位數,再將這個中位數做爲參數 threshold 的值輸入就行了:
df_titanic_numerical_fsvar = VarianceThreshold(np.median(df_titanic_numerical.var().values)).fit_transform(df_titanic_numerical)
當特徵是二分類時,特徵的取值就是伯努利隨機變,假設 p=0.8,即二分類特徵中某種分類佔到 80%以上的時候刪除特徵
X_bvar = VarianceThreshold(.8 * (1 - .8)).fit_transform(df_titanic_numerical) X_bvar.shape 執行結果: (891, 5)
2) 卡方過濾
卡方檢驗,專用於分類算法,捕捉相關性 追求 p 小於顯著性水平的特徵
卡方過濾是專門針對離散型標籤(即分類問題)的相關性過濾。
卡方檢驗類 feature_selection.chi2 計算每一個非負特徵和標籤之間的卡方統計量,並依照卡方統計量由高到低爲特徵排名
df_titanic_categorical = df_titanic[['sex', 'class', 'embarked', 'who', 'age_bin','adult_male','alone','fare_bin']] df_titanic_numerical = df_titanic[['age','sibsp','parch','fare','family_size','pclass']] df_titanic_categorical_one_hot = pd.get_dummies( df_titanic_categorical, columns=['sex', 'class', 'embarked', 'who', 'age_bin','adult_male','alone','fare_bin'], drop_first=True) df_titanic_combined = pd.concat([df_titanic_numerical,df_titanic_categorical_one_hot],axis=1) y = df_titanic['survived'] X = df_titanic_combined.iloc[:,1:] from sklearn.feature_selection import chi2 from sklearn.feature_selection import SelectKBest chi_value, p_value = chi2(X,y) #根據 p 值,得出 k 值 k = chi_value.shape[0] - (p_value > 0.05).sum() #要保留的特徵的數量 14 #根據卡方值,選擇前幾特徵,篩選後特徵 X_chi = SelectKBest(chi2, k=14).fit_transform(X, y) X_chi.shape (89
3) F 檢驗
只能捕捉線性相關性 要求數據服從正態分佈,追求 P 值小於顯著性水平特徵。
F 檢驗,又稱 ANOVA,方差齊性檢驗,是用來捕捉每一個特徵與標籤之間的線性關係的過濾方法。它便可以作迴歸也能夠作分類,所以包含 feature_selection.f_classif(F 檢驗分類)和 feature_selection.f_regression(F 檢驗迴歸)兩個類。其中 F 檢驗分類用於標籤是離散型變量的數據,而 F 檢驗迴歸用於標籤是連續型變量的數據。
F 檢驗的本質是尋找兩組數據之間的線性關係,其原假設是」數據不存在顯著的線性關係「。
from sklearn.feature_selection import f_classif f_value, p_value = f_classif(X,y) #根據 p 值,得出 k 值 k = f_value.shape[0] - (p_value > 0.05).sum() #篩選後特徵 X_classif = SelectKBest(f_classif, k=14).fit_transform(X, y)
4) 互信息法
能夠捕捉任何相關性 不能用於稀疏矩陣,追求互信息大於 0 的特徵
互信息法是用來捕捉每一個特徵與標籤之間的任意關係(包括線性和非線性關係)的過濾方法。和 F 檢驗類似,它既能夠作迴歸也能夠作分類,而且包含兩個類:
feature_selection.mutual_info_classif(互信息分類)feature_selection.mutual_info_regression(互信息迴歸)
這兩個類的用法和參數都和 F 檢驗如出一轍,不過 互信息法比 F 檢驗更增強大,F 檢驗只可以找出線性關係,而互信息法能夠找出任意關係。 互信息法不返回 p 值或 F 值相似的統計量,它返回「每一個特徵與目標之間的互信息量的估計」,這個估計量在[0,1]之間取值,爲 0 則表示兩個變量獨立,爲 1 則表示兩個變量徹底相關。
from sklearn.feature_selection import mutual_info_classif as MIC #互信息法 mic_result = MIC(X,y) #互信息量估計 k = mic_result.shape[0] - sum(mic_result <= 0) #16 X_mic = SelectKBest(MIC, k=16).fit_transform(X, y) X_mic.shape (891, 16)
1) 遞歸特徵消除法
遞歸消除特徵法使用一個基模型來進行多輪訓練,每輪訓練後,消除若干權值係數的特徵,再基於新的特徵集進行下一輪訓練。使用 feature_selection 庫的 RFE 類來選擇特徵的代碼以下:
from sklearn.feature_selection import RFE from sklearn.linear_model import LogisticRegression #遞歸特徵消除法,返回特徵選擇後的數據 #參數 estimator 爲基模型 #參數 n_features_to_select 爲選擇的特徵個數 X_ref = RFE(estimator=LogisticRegression(), n_features_to_select=10).fit_transform(X, y)
2) 重要性評估
from sklearn.ensemble import ExtraTreesClassifier # feature extraction model = ExtraTreesClassifier() model.fit(X, y) print(model.feature_importances_) feature=list(zip(X.columns,model.feature_importances_)) feature=pd.DataFrame(feature,columns=['feature','importances']) feature.sort_values(by='importances',ascending=False).head(20) feature importances 2 fare 0.227659 15 adult_male_True 0.130000 10 who_man 0.108939 5 sex_male 0.078065 11 who_woman 0.059090 7 class_Third 0.055755 4 pclass 0.048733 3 family_size 0.038347 0 sibsp 0.035489 9 embarked_S 0.029512 1 parch 0.023778 20 fare_bin_(39.688, 512.329] 0.022985 14 age_bin_young 0.021404 12 age_bin_midlife 0.019379 6 class_Second 0.019301 17 fare_bin_(7.854, 10.5] 0.016448 19 fare_bin_(21.679, 39.688] 0.016006 18 fare_bin_(10.5, 21.679] 0.014871 16 alone_True 0.013093 13 age_bin_old 0.0112
3) 排列重要性評估
優勢:快速計算;易於使用和理解;特徵重要性度量的屬性;追求特徵穩定性
原理:在訓練機器學習模型以後計算置換重要性。這種方法在向模型提出假設,若是在保留目標和全部其餘列的同時隨機打亂一列驗證集特徵數據,對預測機器學習模型的準確性的影響程度。對於一個具備高度重要性的特徵,random-reshuffle會對機器學習模型預測的準確性形成更大的損害。
結果解讀:每一行的第一個數字表示模型性能(例子中用的是準確率)衰減了多少,±後面的數字表示屢次打亂的標準差。
import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier import eli5 from eli5.sklearn import PermutationImportance my_model = RandomForestClassifier(random_state=0).fit(train_X, train_y) perm = PermutationImportance(my_model, random_state=1).fit(val_X, val_y) eli5.show_weights(perm, feature_names = val_X.columns.tolist())
1) 基於懲罰項的特徵選擇法
使用帶懲罰項的基模型,除了篩選出特徵外,同時也進行了降維。
使用 feature_selection 庫的 SelectFromModel 類結合帶 L1 懲罰項的邏輯迴歸模型,來選擇特徵的代碼以下:
from sklearn.feature_selection import SelectFromModel from sklearn.linear_model import LogisticRegression #帶 L1 和 L2 懲罰項的邏輯迴歸做爲基模型的特徵選擇,這個設置帶 L1 懲罰項的邏輯迴歸做爲基模型的特徵選擇 lr = LogisticRegression(solver='liblinear',penalty="l1", C=0.1) X_sfm = SelectFromModel(lr).fit_transform(X, y) X_sfm.shape (891, 7
使用 feature_selection 庫的 SelectFromModel 類結合 SVM 模型,來選擇特徵的代碼以下:
from sklearn.feature_selection import SelectFromModel from sklearn.svm import LinearSVC lsvc = LinearSVC(C=0.01,penalty='l1',dual=False).fit(X, y) model = SelectFromModel(lsvc,prefit=True) X_sfm_svm = model.transform(X) X_sfm_svm.shape (891, 7
2) 基於樹模型
樹模型中 GBDT 也可用來做爲基模型進行特徵選擇,使用 feature_selection 庫的 SelectFromModel 類結合 GBDT 模型,來選擇特徵的代碼以下:
from sklearn.feature_selection import SelectFromModel from sklearn.ensemble import GradientBoostingClassifier #GBDT 做爲基模型的特徵選擇 gbdt = GradientBoostingClassifier() X_sfm_gbdt = SelectFromModel(gbdt).fit_transform(X, y) X_sfm_gbdt.shape (891, 5)
總結一下,有幾點作特徵選擇的方法經驗:
(1)若是特徵是分類變量,那麼能夠從 SelectKBest 開始,用卡方或者基於樹的選擇器來選擇變量;
(2)若是特徵是定量變量,能夠直接用線性模型和基於相關性的選擇器來選擇變量;
(3)若是是二分類問題,能夠考慮使用 SelectFromModel 和 SVC;
(4)在進行特徵選擇前,仍是須要作一下 EDA。
轉換爲 Z-score,使數值特徵列的算數平均爲 0,方差(以及標準差)爲 1。難免疫 outlier。
注:若是數值特徵列中存在數值極大或極小的 outlier(經過 EDA 發現),應該使用更穩健(robust)的統計數據:用中位數而不是算術平均數,用分位數(quantile)而不是方差。這種標準化方法有一個重要的參數:(分位數下限,分位數上限),最好經過 EDA 的數據可視化肯定。免疫 outlier。
from sklearn.preprocessing import StandardScale #標準化模型訓練 Stan_scaler = StandardScaler() Stan_scaler.fit(x) x_zscore = Stan_scaler.transform(x) x_test_zscore = Stan_scaler.transform(x_test) joblib.dump(Stan_scaler,'zscore.m') #寫入文件
把每一行數據歸一化,使之有 unit norm,norm 的種類能夠選 l一、l2 或 max。難免疫 outlier。
,其中 iota 表示 norm 函數。
將一列的數值,除以這一列的最大絕對值。
MinMaxScaler:線性映射到 [ 0,1 ] ,難免疫 outlier。
MaxAbsScaler:線性映射到 [ -1,1 ] ,難免疫 outlier。
from sklearn import preprocessing min_max_scaler = preprocessing.MinMaxScaler() min_max_scaler.fit_transform(x) x_minmax = min_max_scaler.transform(x) x_test_minmax = min_max_scaler.transform(x_test) joblib.dump(min_max_scaler,'min_max_scaler.m') #寫入文件
注:若是數值特徵列中存在數值極大或極小的 outlier(經過 EDA 發現),應該使用更穩健(robust)的統計數據:用中位數而不是算術平均數,用分位數(quantile)而不是方差。這種標準化方法有一個重要的參數:(分位數下限,分位數上限),最好經過 EDA 的數據可視化肯定。免疫 outlier。
歸一化與標準化區別
(a) 目的不一樣,歸一化是爲了消除綱量壓縮到[0,1]區間;標準化只是調整特徵總體的分佈。 (b) 歸一化與最大,最小值有關;標準化與均值,標準差有關。 (c) 歸一化輸出在[0,1]之間;標準化無限制。
歸一化與標準化應用場景
(a) 在分類、聚類算法中,須要使用距離來度量類似性的時候(如 SVM、KNN)或者使用 PCA 技術進行降維的時候,標準化(Z-score standardization)表現更好。 (b) 在不涉及距離度量、協方差計算、數據不符合正太分佈的時候,可使用第一種方法或其餘歸一化方法。好比圖像處理中,將 RGB 圖像轉換爲灰度圖像後將其值限定在[0 255]的範圍。 (c) 基於樹的方法不須要進行特徵的歸一化。例如 隨機森林,bagging 與 boosting 等方法。若是是基於參數的模型或者基於距離的模型,由於須要對參數或者距離進行計算,都須要進行歸一化。
利用統計或數學變換來減輕數據分佈傾斜的影響。使本來密集的區間的值儘量的分散,本來分散的區間的值儘可能的聚合。
這些變換函數都屬於冪變換函數簇,一般用來建立單調的數據變換。它們的主要做用在於它能幫助穩定方差,始終保持分佈接近於正態分佈並使得數據與分佈的平均值無關。
log 變換一般用來建立單調的數據變換。它的主要做用在於幫助穩定方差,始終保持分佈接近於正態分佈並使得數據與分佈的平均值無關。由於 log 變換傾向於拉伸那些落在較低的幅度範圍內自變量值的範圍,傾向於壓縮或減小更高幅度範圍內的自變量值的範圍。從而使得傾斜分佈儘量的接近正態分佈。 因此針對一些數值連續特徵的方差不穩定,特徵值重尾分佈咱們須要採用 log 化來調整整個數據分佈的方差,屬於方差穩定型數據轉換。
log 變換屬於冪變換函數簇。該函數用數學表達式表示爲
天然對數使用 b=e,e=2.71828,一般叫做歐拉常數。你可使用一般在十進制系統中使用的 b=10 做爲底數。
代碼實現
sns.distplot(df_titanic.fare,kde=False)
df_titanic['fare_log'] = np.log((1+df_titanic['fare'])) sns.distplot(df_titanic.fare_log,kde=False)
box-cox 變換是另外一個流行的冪變換函數簇中的一個函數。該函數有一個前提條件,即數值型值必須先變換爲正數(與 log 變換所要求的同樣)。萬一出現數值是負的,使用一個常數對數值進行偏移是有幫助的。
box-cox 變換是 box 和 cox 在 1964 年提出的一種廣義冪變換方法,是統計建模中經常使用的一種數據變換,用於連續的響應變量不知足正態分佈的狀況。box-cox 變換以後,能夠必定程度上減少不可觀測的偏差和預測變量的相關性。box-cox 變換的主要特色是引入一個參數,經過數據自己估計該參數進而肯定應採起的數據變換形式,box-cox 變換能夠明顯地改善數據的正態性、對稱性和方差相等性,對許多實際數據都是行之有效的。
box-cox 變換函數:
生成的變換後的輸出 y 是輸入 x 和變換參數的函數;當 λ=0 時,該變換就是天然對數 log 變換,前面咱們已經提到過了。λ 的最佳取值一般由最大似然或最大對數似然肯定。
代碼實現
# 從數據分佈中移除非零值 fare_positive_value = df_titanic[(~df_titanic['fare'].isnull()) & (df_titanic['fare']>0)]['fare'] import scipy.stats as spstats # 計算最佳λ值 l, opt_lambda = spstats.boxcox(fare_positive_value) print('Optimal lambda value:', opt_lambda) # -0.5239075895755266 # 進行 Box-Cox 變換 fare_boxcox_lambda_opt = spstats.boxcox(df_titanic[df_titanic['fare']>0]['fare'],lmbda=opt_lambda) sns.distplot(fare_boxcox_lambda_opt,kde=Fal
LabelEncoder 是對不連續的數字或者文本進行編號,編碼值介於 0 和 n_classes-1 之間的標籤。
例如:好比有[dog,cat,dog,mouse,cat],咱們把其轉換爲[1,2,1,3,2]。這裏就產生了一個奇怪的現象:dog 和 mouse 的平均值是 cat。
優勢:相對於 OneHot 編碼,LabelEncoder 編碼佔用內存空間小,而且支持文本特徵編碼。
缺點:它隱含了一個假設:不一樣的類別之間,存在一種順序關係。在具體的代碼實現裏,LabelEncoder 會對定性特徵列中的全部獨特數據進行一次排序,從而得出從原始輸入到整數的映射。因此目前尚未發現標籤編碼的普遍使用,通常在樹模型中可使用。
代碼實現
from sklearn.preprocessing import LabelEncoder le = LabelEncoder() le.fit(["超一線", "一線", "二線", "三線"]) print('特徵:{}'.format(list(le.classes_))) # 輸出 特徵:['一線', '三線', '二線', '超一線'] print('轉換標籤值:{}'.format(le.transform(["超一線", "一線", "二線"]))) # 輸出 轉換標籤值:array([3 0 2]...) print('特徵標籤值反轉:{}'.format(list(le.inverse_transform([2, 2, 1])))) # 輸出 特徵標籤值反轉:['二線', '二線', '三線
OneHotEncoder 用於將表示分類的數據擴維。最簡單的理解用 N 位狀態寄存器編碼 N 個狀態,每一個狀態都有獨立的寄存器位,且這些寄存器位中只有一位有效,只能有一個狀態。
爲何要使用獨熱編碼?
獨熱編碼是由於大部分算法是基於向量空間中的度量來進行計算的,爲了使非偏序關係的變量取值不具備偏序性,而且到圓點是等距的。使用 one-hot 編碼,將離散特徵的取值擴展到了歐式空間,離散特徵的某個取值就對應歐式空間的某個點。將離散型特徵使用 one-hot 編碼,會讓特徵之間的距離計算更加合理。
爲何特徵向量要映射到歐式空間?
將離散特徵經過 one-hot 編碼映射到歐式空間,是由於在迴歸、分類、聚類等機器學習算法中,特徵之間距離或類似度的計算是很是重要的,而咱們經常使用的距離或類似度的計算都是在歐式空間的類似度計算。
舉個例子-假若有三種顏色特徵:紅、黃、藍。
在利用機器學習的算法時通常須要進行向量化或者數字化。那麼你可能想 假設 紅=1,黃=2,藍=3,那麼這樣實現了標籤編碼,即給不一樣類別以標籤。然而這意味着機器可能會學習到「紅<黃<藍」,但這並非咱們的讓機器學習的本意,只是想讓機器區分它們,並沒有大小比較之意。
因此這時標籤編碼是不夠的,須要進一步轉換。由於有三種顏色狀態,因此就有 3 個比特。即紅色:1 0 0,黃色: 0 1 0,藍色:0 0 1。如此一來每兩個向量之間的距離都是根號 2,在向量空間距離都相等,因此這樣不會出現偏序性,基本不會影響基於向量空間度量算法的效果。
優勢:獨熱編碼解決了分類器很差處理屬性數據的問題,在必定程度上也起到了擴充特徵的做用。它的值只有 0 和 1,不一樣的類型存儲在垂直的空間。
缺點:只能對數值型變量二值化,沒法直接對字符串型的類別變量編碼。當類別的數量不少時,特徵空間會變得很是大。在這種狀況下,通常能夠用 PCA 來減小維度。並且 one hot encoding+PCA 這種組合在實際中也很是有用。
代碼實現
sex_list = ['MALE', 'FEMALE', np.NaN, 'FEMALE', 'FEMALE', np.NaN, 'MALE'] df = pd.DataFrame({'SEX': sex_list}) display(df) df.fillna('NA', inplace=True) df = pd.get_dummies(df['SEX'],prefix='IS_SEX') display(df) # 原始數據 SEX 0 MALE 1 FEMALE 2 NaN 3 FEMALE 4 FEMALE 5 NaN 6 MALE # 填充後 IS_SEX_FEMALE IS_SEX_MALE IS_SEX_NA 0 0 1 0 1 1 0 0 2 0 0 1 3 1 0 0 4 1 0 0 5 0 0 1 pd.get_dummies( df_titanic, columns=[ 'sex', 'class', 'pclass', 'embarked', 'who', 'family_size', 'age_bin' ],drop_first=True)
注:當特徵是字符串類型時,須要先用 LabelEncoder() 轉換成連續的數值型變量,再用 OneHotEncoder() 二值化 sklearn.preprocessing 中的 OneHotEncoder 將 shape=(None,1)的列向量中每一個份量表示的下標(index)編碼成 one hot 行向量。 import numpy as np from sklearn.preprocessing import OneHotEncoder 行向量轉列向量: # 非負整數表示的標籤列表 labels = [0,1,0,2] # 行向量轉列向量 labels = np.array(labels).reshape(len(labels), -1) one hot 編碼: enc = OneHotEncoder() enc.fit(labels) targets = enc.transform(labels).toarray() # 若是不加 toarray() 的話,輸出的是稀疏的存儲格式,即索引加值的形式,也能夠經過參數指定 sparse = False 來達到一樣的效果 編碼結果: array([[ 1., 0., 0.], [ 0., 1., 0.], [ 1., 0., 0.], [ 0., 0., 1.]])
功能與 OneHotEncoder 同樣,可是 OneHotEncoder 只能對數值型變量二值化,沒法直接對字符串型的類別變量編碼,而 LabelBinarizer 能夠直接對字符型變量二值化。
讀取數據&數據展現
from sklearn import datasets iris_data = datasets.load_iris() X = iris_data.data y = iris_data.target def draw_result(X, y): """ X: 降維後的數據 iris: 原數據 """ plt.figure() # 提取 Iris-setosa setosa = X[y == 0] # 繪製點:參數 1 x 向量,y 向量 plt.scatter(setosa[:, 0], setosa[:, 1], color="red", label="Iris-setosa") # Iris-versicolor versicolor = X[y == 1] plt.scatter(versicolor[:, 0], versicolor[:, 1], color="orange", label="Iris-versicolor") # Iris-virginica virginica = X[y == 2] plt.scatter(virginica[:, 0], virginica[:, 1], color="blue", label="Iris-virginica") plt.legend() plt.show() draw_result(X, y
做用:降維、壓縮
步驟:
(a) 手動實現 PCA
class PCA: def __init__(self, dimension, train_x): # 降維後的維度 self.dimension = dimension # 原始數據集 self.train_x = train_x @property def result(self): '返回降維後的矩陣' # 1. 數據中心化 data_centering = self.train_x - np.mean(self.train_x, axis=0) # 2. 計算協方差矩陣 cov_matrix = np.cov(data_centering, rowvar=False) # 3. 特徵值分解 eigen_val, eigen_vec = np.linalg.eig(cov_matrix) # 4. 生成降維後的數據 p = eigen_vec[:, 0:self.dimension] # 取特徵向量矩陣的前 k 維 return np.dot(data_centering,p) 調用方法: pca = PCA(2,X) iris_2d = pca.result draw_result(iris_2d, y
(b) sklearn 的 PCA
import numpy as np from sklearn.decomposition import PCA pca = PCA(n_components=2) newX = pca.fit_transform(X) draw_result(newX, y)
做用:特徵分解、降維
步驟:
(a)手動實現 SVD
class SVD: def __init__(self, dimension, train_x): self.dimension = dimension self.train_x = train_x @property def result(self): '返回降維後的矩陣' data_centering = self.train_x - np.mean(self.train_x, axis=0) # SVD U, Sigma, VT = np.linalg.svd(data_centering) return np.dot(data_centering, np.transpose(VT)[:, :self.dimension]) 調用方法: svd = SVD(2,X) iris_svd = svd.result draw_result(iris_svd,y)
(b) sklearn 的 SVD
TruncatedSVD,截斷奇異值分解(當數據量很是大,svd 跑不出來時使用此方法)。
from sklearn.decomposition import TruncatedSVD iris_2d = TruncatedSVD(2).fit_transform(X) draw_result(iris_2d, y)
是有監督的降維,經過最小化類內離散度與最大化類間離散度來得到最優特徵子集。
LD1 經過線性斷定,能夠很好的將呈正態分佈的兩個類分開。 LD2 的線性斷定保持了數據集的較大方差,但 LD2 沒法提供關於類別的信息,所以 LD2 不是一個好的線性斷定。
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA lda = LDA(n_components=2) iris_2d = lda.fit_transform(X, y) draw_result(iris_2d, y)
LDA 與 PCA 類似:
PCA 試圖尋找到方差最大的正交的主成分份量軸 LDA 發現能夠最優化分類的特徵子空間 LDA 和 PCA 都是可用於下降數據集維度的線性轉換技巧 PCA 是無監督算法 LDA 是監督算法 LDA 是一種更優越的用於分類的特徵提取技術
from sklearn.manifold import TSNE tsne = TSNE(n_components=2) iris_2d = tsne.fit_transform(X) draw_result(iris_2d, y)
切忌:一開始就把全部的特徵一股腦地扔進模型,容易被一些沒用的特徵誤導。
1) EDA
plot,plot,plot,重要的事情說三遍
2) 特徵預處理
時間序列:把昨天的特徵加入今天的特徵,或者把和昨天相比,特徵數值的改變量加入今天的特徵。
連續特徵離散化(決策樹類型的模型沒意義):一種挺有趣的變種,就是限制浮點數特徵的精度,異常數據有很強的魯棒性,模型也會更穩定。
clipping:能夠用 pandas dataframe 的.clip(low, upper)方法,把特徵值的取值限制在必定範圍內
3) 數據清洗
要合情合理,不可盲目填充缺失值、刪除異常值,要創建在統計科學基礎上。
4) 特徵變換
除非萬不得已,不要用 PCA 或者 LDA 降維,建議直接減原始特徵。
任何針對單獨特徵列的單調變換(如對數):不適用於決策樹類算法。
對於決策樹而言, X、X^三、X^5 之間沒有差別 , |X|、X^二、X^4 之間沒有差別,除非發生了舍入偏差。
線性組合(linear combination):僅適用於決策樹以及基於決策樹的 ensemble(如 gradient boosting, random forest),由於常見的 axis-aligned split function 不擅長捕獲不一樣特徵之間的相關性;不適用於 SVM、線性迴歸、神經網絡等。
用 N1 和 N2 表示數值特徵,用 C1 和 C2 表示類別特徵,利用 pandas 的 groupby 操做,能夠創造出如下幾種有意義的新特徵:(其中,C2 還能夠是離散化了的 N1)
median(N1)_by(C1) 中位數 mean(N1)_by(C1) 算術平均數 mode(N1)_by(C1) 衆數 min(N1)_by(C1) 最小值 max(N1)_by(C1) 最大值 std(N1)_by(C1) 標準差 var(N1)_by(C1) 方差 freq(C2)_by(C1) 頻數
僅僅將已有的類別和數值特徵進行以上的有效組合,就可以大量增長優秀的可用特徵。
將這種方法和線性組合等基礎特徵工程方法結合(僅用於決策樹),能夠獲得更多有意義的特徵,如:
N1 - median(N1)_by(C1) N1 - mean(N1)_by(C1)
基於 genetic programming 的 symbolic regression【python 環境下首推基因編程庫爲 gplearn】。
基因編程的兩大用法:
轉換(transformation):把已有的特徵進行組合轉換,組合的方式(一元、二元、多元算子)能夠由用戶自行定義,也可使用庫中自帶的函數(如加減乘除、min、max、三角函數、指數、對數)。組合的目的,是創造出和目標 y 值最「相關」的新特徵。
spearman 多用於決策樹(免疫單特徵單調變換),pearson 多用於線性迴歸等其餘算法。
迴歸(regression):原理同上,只不過直接用於迴歸而已。
在決策樹系列的算法中(單棵決策樹、gbdt、隨機森林),每個樣本都會被映射到決策樹的一片葉子上。所以,咱們能夠把樣本通過每一棵決策樹映射後的 index(天然數)或 one-hot-vector(啞編碼獲得的稀疏矢量)做爲一項新的特徵,加入到模型中。
具體實現:apply()以及 decision_path()方法,在 scikit-learn 和 xgboost 裏均可以用。
5) 模型
對特徵數值幅度不敏感,能夠不進行無量綱化和統計變換處理; 因爲數模型依賴於樣本距離來進行學習,能夠不進行類別特徵編碼(但字符型特徵不能直接做爲輸入,因此須要至少要進行標籤編碼)。 LightGBM 和 XGBoost 都能將 NaN 做爲數據的一部分進行學習,因此不須要處理缺失值。其餘狀況下,咱們須要使用。
對於數值型特徵須要進行無量綱化處理; 對於一些長尾分佈的數據特徵,能夠作統計變換,使得模型能更好優化; 對於線性模型,特徵分箱能夠提高模型表達能力;
注:結合工做內容、學習總結以上內容,若有錯誤,請指出,誠心請教
光喻:【持續更新】機器學習特徵工程實用技巧大全zhuanlan.zhihu.com
特徵工程系列:數據清洗 - 大咖駕到 - 博客園www.cnblogs.com
做者:王嶽 好將來機器學習算法專家