数据挖掘流程(三):特征工程
數據和特征決定了機器學習的上限,而模型和算法只是逼近這個上限而已。
特征工程是利用數據領域的相關知識來創建能夠使機器學習算法達到最佳性能的特征的過程。
特征工程流程:這些過程不是必須全部要有,需要根據業務需求和數據格式特點,適宜調整!
-
- 數據理解EDA
- 特征清洗
- 特征構造
- 特征選擇
- 特征降維
- 特征類別不平衡
目錄
特征工程
1. 數據理解EDA
1.1 數據簡略觀測
1.2 數據統計
1.3 數據正態性檢驗
1.4 繪圖
2. 特征清洗
2.1 特征分類不平衡
2.2 缺失值處理
2.3 異常值處理
2.4 數據轉換
2.5 數據分桶
2.6 一人多次
3. 特征構造(特征生成)
4. 特征選擇
4.1 Filter(過濾式),單變量特征選擇
4.2 Wrapper(包裝法)
4.3 Embedded(嵌入法)
5. 特征降維
5.1 PCA
5.2 LDA
6. 直接預測
特征工程
1. 數據理解EDA
這一步最重要的是形成分類變量名列表和連續變量名列表。這樣做的好處:
1)方便查看分類變量數據分布。分類變量正負樣本比例全是1或95%是1的,沒意義,可以刪去;連續變量缺失率大于50%和數值分布范圍
2)方便后面的相關性檢測。分類變量用卡方檢驗;連續變量用t檢驗或方差分析。
1.1 數據簡略觀測
- head()
- shape
- unique()、nunique()
- 相關統計量。describe()
- 數據類型。info()
- pandas_profiling數據報告,不建議。因為在數據量大時,pandas_profiling生成的數據報告可能出錯、生成的圖較大較慢。
1.2 數據統計
print('----------------全體變量數據統計描述----------------------') # 統計全變量體系各變量的平均數、上下四分位數、缺失率 feature_list=[] mean_list=[] up_quarter_list=[] down_quarter_list=[] miss_list=[]for i in df_model.columns:data = df_model[i]stat_result = pd.DataFrame(data.describe())# print(stat_result)mean_value=stat_result.loc['mean',i]up_quarter=stat_result.loc['25%',i]down_quarter=stat_result.loc['75%',i]num=stat_result.loc['count',i]miss_rate=1-num/df_model.shape[0]miss_rate="%.2f%%" % (miss_rate * 100) # 百分數輸出feature_list.append(i)mean_list.append(round(mean_value,2))up_quarter_list.append(round(up_quarter,2))down_quarter_list.append(round(down_quarter,2))miss_list.append(miss_rate)df_stat=pd.DataFrame({'特征':feature_list,'平均值':mean_list,'上四分位':up_quarter_list,'下四分位':down_quarter_list,'缺失率':miss_list}) df_stat=df_stat.reset_index(drop=True)writer=pd.ExcelWriter(project_path+'/data/v2.0/df_全體變量數據統計.xlsx') df_stat.to_excel(writer) writer.save()1.3 數據正態性檢驗
數據正態性檢驗,是為了方便相關性分析和顯著性分析。當樣本量巨大時,可以近似認為數據符合正態分布,不用做正態性檢驗。
- SPSS。
- P-P圖/Q-Q圖
- k-s和s-w檢驗。
- 直方圖。Analysis--統計描述--頻率
- python。詳見特征選擇-相關性分析
- 查看特征的偏度和峰度
1.4 繪圖
- 畫出原始的數據
- 畫出他們的簡單的統計特征(mean plots, box plots, residual plots)
- 畫出不同的數據間的相關性
- 小提琴圖。相當于進階版箱線圖,可以看出某個值附近分布的頻率。
- 直方圖。便于觀察數據分布
- 箱線圖。便于觀察數據的異常情況,以及不同數據間的對比。
- 時序圖。便于觀察數據特點,例如是否具有周期性、震蕩幅度等
2. 特征清洗
2.1 特征分類不平衡
分類變量正負樣本分類不平衡,少類別提供信息太少,沒有學會如何判別少數類。
- 刪除。分類變量正負樣本比例全是1或95%是1的,沒意義,可以刪去
- 重采樣
過采樣是針對minority樣本,欠采樣是針對majority樣本;而綜合采樣是既對minority樣本,又對majority樣本,同時進行操作的方法
- 過采樣over-sampling。smote,adasyn,TabGan,CTGAN(github)
- 欠采樣under-sampling。cluster centrolds,Tomek's links,Edited Nearest Neighbours,AllKNN,Condensed Nearest Neighbour,MearMiss-1,2,3
- 嘗試其他評價指標。AUC
- 調整θ值
- 選擇其他模型:決策樹等;
例:
原始數據(Original):未經過任何采樣處理(1831X21)每條數據有21個特征。其中正例176個(9.6122%),反例1655個(90.3878%)
欠采樣(Undersampling):從反例中隨機選擇176個數據,與正例合并(352X21)
過采樣(Oversampling):從正例中反復抽取并生成1655個數據(勢必會重復),并與反例合并(3310X21)
SMOTE:也是一種過采樣方法。SMOTE通過找到正例中數據的近鄰,來合成新的1655-176=1479個“新正例”,并與原始數據合并(3310X21)
欠采樣
from imblearn.under_sampling import TomekLinksX_train = train_df.drop(['id', 'type'], axis=1) y = train_df['label'] tl = TomekLinks() X_us, y_us = tl.fit_sample(X_train, y) print(X_us.groupby(['label']).size()) # label # 0 36069 # 1 2757SMOTE
from imblearn.over_sampling import SMOTE smote = SMOTE(k_neighbors=5, random_state=42) X_res, y_res = smote.fit_resample(X_train, y) X_res.groupby(['label']).size() # label # 0 37243 # 1 37243ADASYN
from imblearn.over_sampling import ADASYN adasyn = ADASYN(n_neighbors=5, random_state=42) X_res, y_res = adasyn.fit_resample(X_train, y) X_res.groupby(['label']).size()# label # 0 37243 # 1 36690綜合采樣
from imblearn.combine import SMOTETomeksmote_tomek = SMOTETomek(random_state=0) X_res, y_res = smote_tomek.fit_sample(X_train, y) X_res.groupby(['label']).size() # label # 0 36260 # 1 36260結果:
1)過采樣(右上)只是單純的重復了正例,因此會過分強調已有的正例。如果其中部分點標記錯誤或者是噪音,那么錯誤也容易被成倍的放大。因此最大的風險就是對正例過擬合
2)欠采樣(左下)拋棄了大部分反例數據,從而弱化了中間部分反例的影響,可能會造成偏差很大的模型。當然,如果數據不平衡但兩個類別基數都很大,或許影響不大。數據總是寶貴的,拋棄數據是很奢侈的,因此另一種常見的做法是反復做欠采樣,生成1655/176=9
3)SMOTE(右下)可以看出和過采樣(右上)有了明顯的不同,因為不單純是重復正例了,而是在局部區域通過K-近鄰生成了新的正例。
2.2 缺失值處理
- 刪除。缺失率超過50%的變量刪除
- 傳統方法。(均值、中位數)
- 機器學習。(隨機森林rf插補、xgboost)
- GAN。偽造數據、fake sample
2.3 異常值處理
- 刪除。箱線圖分析刪除異常值box-plot
- 孤立森林
- 長尾截斷
2.4 數據轉換
一般是用于連續變量不滿足正態分布的時候
最重要的一點:如果對因變量進行數據轉換,要記得對模型預測結果進行恢復!
- 正態糾偏(修復偏斜特征),box-cox轉換
Box-Cox變換通過對因變量進行變換,使得變換過的向量與回歸自變量具有線性相依關系,誤差也服從正態分布.誤差各分量是等方差且相互獨立。Box-Cox變換兼顧了變量在時間序列維度上的回歸特性,所以也可以用于時間序列方面的預測。
from scipy.stats import boxcox boxcox_transformed_data = boxcox(original_data)在一些情況下(P值正態化處理,所以優先使用BOX-COX轉換,但是當P值>0.003時兩種方法均可,優先考慮普通的平方變換
- 其他非正態數據轉換
- 對數變換(log)
- 平方根轉換
- 倒數轉換
- 平方根后取到數,平方根后再取反余弦,冪轉換
- 中心化。把數據整體移動到以0為中心點的位置,將數據減去這個數據集的平均值
- 標準化(Z-score)。(x-mean)/std
- 歸一化(Max-min)。(x-min)/(max-min)。從經驗上說,歸一化是讓不同維度之間的特征在數值上有一定的比較性,可以大大提高分類器的準確性。
- 轉換數據類型(astype)
- 獨熱編碼(one-hot Encoder)
- 標簽編碼(Label Encoder)
2.5 數據分桶
醫學數據挖掘里用處不大
- 等頻分桶
- 等距分桶
- Best-KS分桶
- 卡方分桶
2.6 一人多次
3. 特征構造(特征生成)
在特征構造的時候,需要借助一些業務知識(比如醫學中的BMI、肌酐轉化率),遵循的一般原則就是需要發揮想象力,盡可能多的創造特征,不用先考慮哪些特征可能好,可能不好,先彌補這個廣度。
醫學數據挖掘一般不需要考慮數值、類別和時間特征。
- 數值特征
- 類別特征
- 時間特征
4. 特征選擇
filter--主要對應單變量特征選擇;wrapper--主要對應多個特征選擇。
特征選擇原因:對于一個特定的學習算法來說,哪一個特征是有效的是未知的。因此,需要從所有特征中選擇出對于學習算法有益的相關特征。而且在實際應用中,經常會出現維度災難問題。如果只選擇所有特征中的部分特征構建模型,那么可以大大減少學習算法的運行時間,也可以增加模型的可解釋性
特征選擇原則:獲取盡可能小的特征子集,不顯著降低分類精度、不影響分類分布以及特征子集應具有穩定、適應性強等特點
4.1 Filter(過濾式),單變量特征選擇
filter按照發散性或相關性對各個特征進行評分,設定閾值或者待選擇閾值的個數,選擇特征。
優點:運行速度快,是一種非常流行的特征選擇方法。
缺點:無法提供反饋,特征選擇的標準/規范的制定是在特征搜索算法中完成,學習算法無法向特征搜索算法傳遞對特征的需求。另外,可能處理某個特征時由于任意原因表示該特征不重要,但是該特征與其他特征結合起來則可能變得很重要
- 相關性檢驗。分別計算每個特征與輸出值之間的相關系數,設定一個閾值,選擇相關系數大于閾值的部分特征
https://note.youdao.com/s/9HR1GEQG
- 顯著性檢驗
- ?t檢驗
- 卡方檢驗
- 方差檢驗。
- 非參數檢驗
https://note.youdao.com/s/aTVlqmDy
- 互信息
- Relief
獨立樣本t檢驗和Mann-Whitney U test
discrete_list = ['gender'] continuous_list = [x for x in df_model.columns if x not in discrete_list] # 高低劑量組利伐沙班服藥前后WBC顯著性檢驗 from scipy.stats import kstest,shapiro import scipy.stats as st from scipy.stats import chi2_contingency ##檢驗是否正態 def norm_test(data):if len(data) > 30:norm, p = kstest(data, 'norm')else:norm, p = shapiro(data)#print(t,p)if p>=0.05:return Trueelse:return Falsedef test2(data_b, data_p):if norm_test(data_b) and norm_test(data_p):x = 1y = '獨立樣本T檢驗't, p = st.ttest_ind(list(data_b),list(data_p), nan_policy='omit')else:x = 0y = 'Mann-Whitney U檢驗't,p = st.mannwhitneyu(list(data_b),list(data_p))return x,y,t,p def sig_test(df_high,df_low,list):field_list=[]y_list=[]t_list=[]p_list=[]result_list=[]high_mean_list=[]low_mean_list=[]# high_num_list=[]# high_rate_list=[]# low_num_list=[]# low_rate_list=[]for i in range(len(list)):field=list[i]df_high_nt=df_high[df_high[field].notnull()]data_high=df_high_nt[field]high_mean=round(data_high.mean(),2)# high_num=df_high_nt.shape[0]# all_num=df_high.shape[0] + df_low.shape[0]# high_rate = "%.2f%%" % (round(high_num/all_num) * 100)df_low_nt=df_low[df_low[field].notnull()]data_low=df_low_nt[field]low_mean=round(data_low.mean(),2)# low_num=df_low_nt.shape[0]# low_rate="%.2f%%" % (round(low_num/all_num) * 100)if data_high.shape[0] >= 10 and data_low.shape[0]>=10:x,y,t,p = test2(data_high, data_low)if p <=0.05:sig='顯著'else:sig='不顯著'field_list.append(field)y_list.append(y)t_list.append(t)p_list.append(p)result_list.append(sig)high_mean_list.append(high_mean)low_mean_list.append(low_mean)df_result=pd.DataFrame({'特征':field_list,'高劑量均值':high_mean_list,'低劑量均值':low_mean_list,'檢驗指標':y_list,'t值':t_list,'p值':p_list,'顯著性結果':result_list})return df_result # 住院時長到用藥時長的顯著性檢驗 df_inp_time=sig_test(df_lfsb_high,df_lfsb_low,['住院時長']) df_inp_time=df_inp_time.reset_index(drop=True)writer=pd.ExcelWriter(project_path+r'/data/result/df_高低劑量組住院時長顯著性檢驗.xlsx') df_inp_time.to_excel(writer) writer.save()卡方檢驗
## 卡方檢驗 print('----------------------卡方檢驗-------------------------') from scipy.stats import chi2_contingencyr1 = [] r2 = [] tran_test['MPA類藥物'] = tran_test['MPA類藥物'].astype('str') for i in range(len(np.unique(tran_test['MPA類藥物']))):r1.append(tran_test[(tran_test['group'] == 0) & (tran_test['MPA類藥物'] == np.unique(tran_test['MPA類藥物'])[i])].shape[0])r2.append(tran_test[(tran_test['group'] == 1) & (tran_test['MPA類藥物'] == np.unique(tran_test['MPA類藥物'])[i])].shape[0])abcd = np.array([r1, r2]) print(abcd) result = chi2_contingency(abcd) print(result)tran_x_1 = tran_x_1.drop(['group'], axis=1) test_x_1 = test_x_1.drop(['group'], axis=1)print(tran_x_1.columns)pearsonr檢驗
from scipy import stats r, p = stats.pearsonr(x,y)spearmanr檢驗
# 連續變量,spearmanr相關性檢驗(統計量r); print('--------------------------計算連續變量的spearmanr相關性系數---------------------------------') from scipy import stats t_list = [] p_list = [] q_list = []for i in continuous_list:# 刪除連續變量中的<、>號tdm_7_other_filter[i] = tdm_7_other_filter[i].astype('str').apply(lambda x: re.sub(r'<|>', '',x))x= tdm_7_other_filter[tdm_7_other_filter[i].astype('float').notnull()][i]y= tdm_7_other_filter[tdm_7_other_filter[i].astype('float').notnull()]['test_result']t, p = stats.spearmanr(x,y)t = round(t, 2)p = round(p, 3)q = '斯皮爾曼'# print(i, t, p)t_list.append(t)p_list.append(p)q_list.append(q) df_spearmanr= pd.DataFrame(data={'連續檢測指標': continuous_list,'t值': t_list,'p值': p_list,'方法': q_list}) df_spearmanr_1 = df_spearmanr[df_spearmanr['p值'] <= 0.05] df_spearmanr_2 = df_spearmanr[df_spearmanr['p值'] >= 0.05] # 顯著性不成立 df_spearmanr = pd.concat([df_spearmanr_1,df_spearmanr_2], axis=0)df_spearmanr=df_spearmanr.sort_values(by=['p值'],ascending=True) df_spearmanr = df_spearmanr.reset_index() del df_spearmanr['index']writer = pd.ExcelWriter(project_path + '/result/df_12_其他檢測指標連續變量的spearmanr相關性檢測.xlsx') df_spearmanr.to_excel(writer) writer.save()4.2 Wrapper(包裝法)
根據目標函數(通常是預測效果評分)作為評價函數,每次選擇若干特征,排除若干特征。
主要方法:遞歸特征消除算法。
優點:對特征進行搜索時圍繞學習算法展開的,對特征選擇的標準/規范是在學習算法的需求中展開的,能夠考慮學習算法所屬的任意學習偏差,從而確定最佳子特征,真正關注的是學習問題本身。由于每次嘗試針對特定子集時必須運行學習算法,所以能夠關注到學習算法的學習偏差/歸納偏差,因此封裝能夠發揮巨大的作用。
缺點:運行速度遠慢于過濾算法,實際應用用封裝方法沒有過濾方法流行。
- 逐步向前(Forward stepwise)
- 逐步向后(Backward stepwise)
4.3 Embedded(嵌入法)
用model進行訓練,得到各個特征的權值系數,根據系數從大到小選擇特征。
特征選擇完成后,還能基于特征選擇完成的特征和模型訓練出的超參數,再次訓練優化。
主要思想:在模型既定的情況下學習出對提高模型準確性最好的特征。也就是在確定模型的過程中,挑選出那些對模型的訓練有重要意義的特征。
# 重要性評分 import catboost,xgboost model_boost=xgboost.XGBRegressor() model_boost.fit(tran_x,tran_y) importance = model_boost.feature_importances_ print(tran_x.columns) print(importance)df_importance= pd.DataFrame(data={'特征':tran_x.columns,'重要性評分':importance}) df_importance['重要性評分']=df_importance['重要性評分'].apply(lambda x: round(x,3)) df_importance=df_importance.sort_values(['重要性評分'],ascending=False) df_importance=df_importance.reset_index(drop=True) writer = pd.ExcelWriter(project_path + '/result/df_19_模型重要性評分.xlsx') df_importance.to_excel(writer) writer.save()- L1正則化/Lasso regression
L1正則化將系數w的l1范數作為懲罰項加到損失函數上。Lasso能夠挑出一些優質特征,同時讓其他特征的系數趨于0。當如需要減少特征數的時候它很有用,但是對于數據理解來說不是很好用。
- L2正則化/Ridge regression
L2正則化對于特征選擇來說一種穩定的模型,不像L1正則化那樣,系數會因為細微的數據變化而波動。所以L2正則化和L1正則化提供的價值是不同的,L2正則化對于特征理解來說更加有用:表示能力強的特征對應的系數是非零。
5. 特征降維
5.1 PCA
5.2 LDA
6. 直接預測
boosting體系用于基因分析挖掘
SVM體系(kernel函數進行更改),適用于缺失值和異常值存在的情況
DeepLearning,aml-net,tabnet,TabTransformer
- 不均衡:loss入手,focal loss,teacher-student-network(多網絡互學習)
- 不均衡+小樣本:GAN體系,對比學習體系(學習特征的表征向量+下游任務預測)
- 小樣本:建議使用傳統機器學習,svm優先(穩定性強);加了正則化的線性模型(L1正則--Lasso回歸,L2正則--Ridge回歸-->導致的問題是泛化,正則是學習的時候盡量不要給它強規則,而重點學習數據分布和推理邏輯,有一定的降維效果)
總結
以上是生活随笔為你收集整理的数据挖掘流程(三):特征工程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据挖掘流程(二):数据预处理
- 下一篇: 数据挖掘流程(四):建模调参