day3:今天進行的是特征工程部分,也就是對一些特征進行處理懊烤,構(gòu)造適合各種模型的數(shù)據(jù)梯醒。
特征工程的目標(biāo)
對于特征進行進一步分析,并對于數(shù)據(jù)進行處理
完成對于特征工程的分析腌紧,并對于數(shù)據(jù)進行一些圖表或者文字總結(jié)并打卡茸习。
數(shù)據(jù)預(yù)處理
空值處理:數(shù)據(jù)清洗包括,total_rooms 用中位數(shù)代替寄啼,ocean_proximity 用one-hot-encode編碼轉(zhuǎn)為數(shù)值型逮光,one-hot-encode與直接編碼為 [0,1,2,3..] 相比,不會引入相似或者相近的意義墩划。比如 2 和 3 在數(shù)值上相近涕刚,但是它們各自表示的NEAR BAY與INLAND屬性項并不存在實質(zhì)的相似關(guān)系(盡管有這個可能)。
構(gòu)造特征:數(shù)據(jù)集里因為不同district里戶數(shù)不同乙帮,所以 total_rooms杜漠、total_bedrooms、population 這些屬性可能沒有太大的意義察净,而每戶的房間數(shù)驾茴、臥室數(shù)、人數(shù)相關(guān)性則較氢卡,所有在數(shù)據(jù)集中可以新增這些特征锈至。
歸一化:數(shù)值型特征的一個特點是,不同的屬性取值范圍差別極大译秦,對模型學(xué)習(xí)峡捡、正則項設(shè)置不利。有兩種簡單的方法可以應(yīng)對這種情況:變換到0-1區(qū)間的歸一化或者標(biāo)準(zhǔn)正態(tài)分布筑悴。前者對異常點非常敏感们拙,如果存在某個值比正常值區(qū)間距離很遠,這種處理會導(dǎo)致正常數(shù)據(jù)點分布過于集中阁吝;后者對過于集中的數(shù)據(jù)分布敏感砚婆,如果原數(shù)據(jù)集方差很小,除以近0的數(shù)值容易導(dǎo)致精度失真突勇。
偏度處理:在大部分數(shù)值型特征中装盯,通常分布不符合正態(tài)分布坷虑,而是類似于“長尾”。例如房價中验夯,低價占大部分猖吴,豪宅屬于小部分。應(yīng)對這種數(shù)據(jù)分布挥转,一般可以通過神奇的log化處理轉(zhuǎn)化類近似正態(tài)分布
異常值處理:
對于不同的特征海蔽,我們會用箱形圖或者小提琴圖可視化數(shù)據(jù)的分布情況。如果數(shù)據(jù)是滿足正態(tài)分布的話绑谣,我們還可以按照3δ準(zhǔn)則党窜,將3δ以外的數(shù)據(jù)去除。本次學(xué)習(xí)中去除的方法是將數(shù)據(jù)的25%-75%范圍擴展box_scale倍借宵,在此范圍以外的進行去除幌衣。
常用處理:
通過箱線圖(或 3-Sigma)分析刪除異常值;
就是下面代碼的中的示例壤玫,采用箱線圖進行觀察異常值豁护。BOX-COX 轉(zhuǎn)換(處理有偏分布);
對于一些非正態(tài)的分布欲间,及偏值較大或者較小的時候楚里,使用Box-Cox轉(zhuǎn)換將數(shù)據(jù)轉(zhuǎn)換為正態(tài)。具體的數(shù)學(xué)原理就不解釋了猎贴,詳情了解班缎。
具體使用:
from scipy.special import boxcox1p
all_data[feat] = boxcox1p(all_data[feat], lam)
- 長尾截斷;
長尾截斷主要也是分布不符合正態(tài)分布她渴,而是類似于“長尾”达址。例如房價中,低價占大部分趁耗,豪宅屬于小部分沉唠。應(yīng)對這種數(shù)據(jù)分布,一般可以通過神奇的log化處理轉(zhuǎn)化類近似正態(tài)分布苛败。
對于### 箱盒圖共有兩個用途右冻,分別 1. 直觀地識別數(shù)據(jù)中異常值(離群點);
- 直觀地判斷數(shù)據(jù)離散分布情況著拭,了解數(shù)據(jù)分布狀態(tài)。
代碼如下:
def outer_proc(data,column,scale=3):
"""
:param data: 接收 pandas 數(shù)據(jù)格式
:param column: pandas 列名
:param scale: 尺度
:return:
"""
def box_plot_outliers(data_ser, box_scale):
"""
利用箱線圖去除異常值,
取scale倍的四分之一到四分之三大小的范圍
:param data_ser: 接收 pandas.Series 數(shù)據(jù)格式
:param box_scale: 箱線圖尺度牍帚,
:return:
"""
iqr = box_scale * (data_ser.quantile(0.75) - data_ser.quantile(0.25))
val_low = data_ser.quantile(0.25) - iqr
val_up = data_ser.quantile(0.75) + iqr
rule_low = (data_ser < val_low)
rule_up = (data_ser > val_up)
return (rule_low, rule_up), (val_low, val_up)
data_n = data.copy()
data_series = data_n[column]
rule , value = box_plot_outliers(data_series,scale)
# 找出異常值的索引 刪出去異常值 異常偏的值
index = np.arange(data_series.shape[0])[rule[0]|rule[1]]
print("Delete number is: {}".format(len(index)))
data_n = data_n.drop(index)
# 對于刪除后 從新整理一下
data_n.reset_index(drop=True, inplace=True)
print("Now column number is: {}".format(data_n.shape[0]))
index_low = np.arange(data_series.shape[0])[rule[0]]
outliers = data_series.iloc[index_low]
print("Description of data less than the lower bound is:")
print(pd.Series(outliers).describe())
index_up = np.arange(data_series.shape[0])[rule[1]]
outliers = data_series.iloc[index_up]
print("Description of data larger than the upper bound is:")
print(pd.Series(outliers).describe())
fig, ax = plt.subplots(1, 2, figsize=(10, 7))
# 處理前分布狀態(tài)
sns.boxplot(y=data[column], data=data, palette="Set1", ax=ax[0])
# 處理后分布狀態(tài)
sns.boxplot(y=data_n[column], data=data_n, palette="Set1", ax=ax[1])
return data_n
問題:
在處理異常值的時候一般去點什么范圍的值儡遮?
隊友的回答 :
image.png
特征歸一化和標(biāo)準(zhǔn)化
數(shù)據(jù)標(biāo)準(zhǔn)化
數(shù)據(jù)標(biāo)準(zhǔn)化,我的理解是將數(shù)據(jù)的分布進行線性變化暗赶,簡單的說就是將數(shù)據(jù)的轉(zhuǎn)化成特定的分布狀態(tài)鄙币。數(shù)據(jù)標(biāo)準(zhǔn)化的方法有很多種肃叶,常用的有“最小—最大標(biāo)準(zhǔn)化”、“Z-score標(biāo)準(zhǔn)化”和“取對數(shù)模式”等十嘿。
-
Min-max 標(biāo)準(zhǔn)化:
Min-max標(biāo)準(zhǔn)化方法是對原始數(shù)據(jù)進行線性變換因惭。設(shè)minA和maxA分別為屬性A的最小值和最大值,將A的一個原始值x通過min-max標(biāo)準(zhǔn)化映射成在區(qū)間[0,1]中的值x'绩衷,其公式為:新數(shù)據(jù)=(原數(shù)據(jù)-極小值)/(極大值-極小值)
用svm對數(shù)據(jù)進行訓(xùn)練前一般采用此方法對數(shù)據(jù)進行標(biāo)準(zhǔn)化蹦魔。 -
Z-score 標(biāo)準(zhǔn)化:
這種方法基于原始數(shù)據(jù)的均值(mean)和標(biāo)準(zhǔn)差(standard deviation)進行數(shù)據(jù)的標(biāo)準(zhǔn)化。將A的原始值x使用Z-score標(biāo)準(zhǔn)化到x'咳燕。 Z-score標(biāo)準(zhǔn)化方法適用于屬性A的最大值和最小值未知的情況勿决,或有超出取值范圍的離群數(shù)據(jù)的情況。新數(shù)據(jù)=(原數(shù)據(jù)-均值)/標(biāo)準(zhǔn)差
-
取對數(shù)模式:
對于一些長尾數(shù)據(jù)進行取對數(shù)處理招盲,可以使得分布更接近正態(tài)分布低缩。對數(shù)Logistic模式:新數(shù)據(jù)=1/(1+e^(-原數(shù)據(jù)))
模糊量化模式:新數(shù)據(jù)=1/2+1/2sin[ pi /(極大值-極小值)(原數(shù)據(jù) -(極大值-極小值)/2)]
數(shù)據(jù)歸一化
數(shù)據(jù)歸一化指的是將數(shù)據(jù)縮小到一點的范圍,但是不改變數(shù)據(jù)的分布情況曹货,目的是為了更好的計算咆繁,防止太大的數(shù)據(jù),計算復(fù)雜顶籽。 為了數(shù)據(jù)處理方便提出來的玩般,把數(shù)據(jù)映射到0~1(或-1~1)范圍之內(nèi)處理,更加便捷快速蜕衡,應(yīng)該歸到數(shù)字信號處理范疇之內(nèi)壤短。
至于具體的方法這里存在著爭議。
因為標(biāo)準(zhǔn)化和歸一化都屬于四種Feature scaling(特征縮放),這四種分別是:
Rescaling (min-max normalization) 有時簡稱normalization(有點坑)
image.png
2.Mean normalization
image.png
3.Standardization(Z-score normalization)
image.png
4.Scaling to unit length
image.png
為什么數(shù)據(jù)要進行歸一化呢慨仿,因為數(shù)據(jù)做歸一化的好處 1.加快了梯度下降求最優(yōu)解的速度和有可能提高精度久脯。2.提高精度,這在涉及到一些距離計算的算法時效果顯著镰吆,比如算法要計算歐氏距離帘撰,所以歸一化很有必要,他可以讓各個特征對結(jié)果做出的貢獻相同万皿。
問題:
- 什么模型需要歸一化摧找?
概率模型不需要歸一化,因為它們不關(guān)心變量的值牢硅,而是關(guān)心變量的分布和變量之間的條件概率蹬耘。像svm、線性回歸之類的最優(yōu)化問題就需要歸一化减余。決策樹屬于前者综苔。歸一化也是提升算法應(yīng)用能力的必備能力之一。
數(shù)據(jù)分桶:
數(shù)據(jù)分桶主要是對于連續(xù)值的一個處理,因為有些模型需要可能不直接處理連續(xù)值如筛,那么我們可以將數(shù)據(jù)進行分桶堡牡,是的連續(xù)值變成分類值,可以進一步給模型學(xué)習(xí)杨刨∥畋或者在存在一些缺失值的時候,用分桶的方法妖胀,這時候我們的缺失值也進桶了芥颈,
為什么要做數(shù)據(jù)分桶呢,原因有很多?
- 離散后稀疏向量內(nèi)積乘法運算速度更快做粤,計算結(jié)果也方便存儲浇借,容易擴展;
- 離散后的特征對異常值更具魯棒性怕品,如 age>30 為 1 否則為 0妇垢,對于年齡為 200 的也不會對模型造成很大的干擾;
- LR 屬于廣義線性模型肉康,表達能力有限闯估,經(jīng)過離散化后,每個變量有單獨的權(quán)重吼和,這相當(dāng)于引入了非線性涨薪,能夠提升模型的表達能力,加大擬合炫乓;
- 離散后特征可以進行特征交叉刚夺,提升表達能力,由 M+N 個變量編程 M*N 個變量末捣,進一步引入非線形侠姑,提升了表達能力;
- 特征離散后模型更穩(wěn)定箩做,如用戶年齡區(qū)間莽红,不會因為用戶年齡長了一歲就變化
等頻分桶;
區(qū)間的邊界值要經(jīng)過選擇,使得每個區(qū)間包含大致相等的實例數(shù)量邦邦。比如說 N=10 ,每個區(qū)間應(yīng)該包含大約10%的實例
等距分桶燃辖;
從最小值到最大值之間,均分為 N 等份黔龟。 如果 A,B 為最小最大值, 則每個區(qū)間的長度為 W=(B?A)/N , 則區(qū)間邊界值為A+W,A+2W,….A+(N?1)W 。這里只考慮邊界,每個等份的實例數(shù)量可能不等观谦。
Best-KS 分桶豁状、卡方分桶:比較復(fù)雜 可參考
bin = [i*10 for i in range(31)]
data['power_bin'] = pd.cut(data['power'], bin, labels=False)
data[['power_bin', 'power']].head()
特征篩選
由于有的時候數(shù)據(jù)有很多的特征泻红,維度過大谊路,無法將所有的特征進行學(xué)習(xí)缠劝,同時有些特征對于模型的學(xué)習(xí)預(yù)測并不是很少惨恭,甚至?xí)窃胍簟K晕覀円暨x出一些好的特征進行用脱羡。當(dāng)然最好的特征篩選就是了解行業(yè)背景锉罐,這是最有效的辦法贯莺。如果這個特征集合有時候也可能很大家肯,在嘗試降維之前玫鸟,我們有必要用特征工程的方法去選擇出較重要的特征結(jié)合煞茫,這些方法不會用到領(lǐng)域知識,而僅僅是統(tǒng)計學(xué)的方法绩聘。
最簡單的方法就是方差篩選凿菩。方差越大的特征衅谷,那么我們可以認為它是比較有用的获黔。如果方差較小玷氏,比如小于1,那么這個特征可能對我們的算法作用沒有那么大渗蟹。最極端的,如果某個特征方差為0耻陕,即所有的樣本該特征的取值都是一樣的,那么它對我們的模型訓(xùn)練沒有任何作用诗宣,可以直接舍棄。在實際應(yīng)用中召庞,我們會指定一個方差的閾值岛心,當(dāng)方差小于這個閾值的特征會被我們篩掉篮灼。sklearn中的VarianceThreshold類可以很方便的完成這個工作忘古。
過濾式(filter):先對數(shù)據(jù)進行特征選擇,然后在訓(xùn)練學(xué)習(xí)器诅诱,常見的方法有
方差選擇發(fā):就是上面說的髓堪,計算特征的方差娘荡,把方差小于閾值的特征篩掉炮沐。
相關(guān)系數(shù)法:指的是通過熱力圖的方式,計算特征和標(biāo)簽之間的相關(guān)系數(shù),小于某個閾值的特征篩掉轻要。
# 相關(guān)性分析
print(data['power'].corr(data['price'], method='spearman'))
print(data['kilometer'].corr(data['price'], method='spearman'))
print(data['brand_amount'].corr(data['price'], method='spearman'))
print(data['brand_price_average'].corr(data['price'], method='spearman'))
print(data['brand_price_max'].corr(data['price'], method='spearman'))
print(data['brand_price_median'].corr(data['price'], method='spearman'))
#畫圖法
data_numeric = data[['power', 'kilometer', 'brand_amount', 'brand_price_average',
'brand_price_max', 'brand_price_median']]
correlation = data_numeric.corr()
f , ax = plt.subplots(figsize = (7, 7))
plt.title('Correlation of Numeric Features with Price',y=1,size=16)
sns.heatmap(correlation,square = True, vmax=0.8)
卡方檢驗法 :卡方檢驗可以檢驗?zāi)硞€特征分布和輸出值分布之間的相關(guān)性冲泥。個人覺得它比比粗暴的方差法好用。如果大家對卡方檢驗不熟悉柏蘑,可以參看這篇卡方檢驗原理及應(yīng)用,這里就不展開了粹庞。在sklearn中咳焚,可以使用chi2這個類來做卡方檢驗得到所有特征的卡方值與顯著性水平P臨界值,我們可以給定卡方值閾值庞溜, 選擇卡方值較大的部分特征革半。
互信息法;即從信息熵的角度分析各個特征和輸出值之間的關(guān)系評分流码。在決策樹算法中我們講到過互信息(信息增益)又官。互信息值越大漫试,說明該特征和輸出值之間的相關(guān)性越大六敬,越需要保留。在sklearn中驾荣,可以使用mutual_info_classif(分類)和mutual_info_regression(回歸)來計算各個輸入特征和輸出值之間的互信息外构。
包裹式(wrapper):直接把最終將要使用的學(xué)習(xí)器的性能作為特征子集的評價準(zhǔn)則审编,常見方法有 LVM(Las Vegas Wrapper) ;
最常用的包裝法是遞歸消除特征法(recursive feature elimination,以下簡稱RFE)歧匈。遞歸消除特征法使用一個機器學(xué)習(xí)模型來進行多輪訓(xùn)練垒酬,每輪訓(xùn)練后,消除若干權(quán)值系數(shù)的對應(yīng)的特征件炉,再基于新的特征集進行下一輪訓(xùn)練勘究。在sklearn中,可以使用RFE函數(shù)來選擇特征妻率。
LVM的實戰(zhàn)案例
包裹式好還包括SFS和SBS算法對特征進行過濾乱顾,不過不詳細展開了,直接附上優(yōu)秀的大佬博客宫静、博客2
# k_feature 太大會很難跑走净,沒服務(wù)器券时,所以提前 interrupt 了
from mlxtend.feature_selection import SequentialFeatureSelector as SFS
from sklearn.linear_model import LinearRegression
sfs = SFS(LinearRegression(),
k_features=10,
forward=True,
floating=False,
scoring = 'r2',
cv = 0)
x = data.drop(['price'], axis=1)
x = x.fillna(0)
y = data['price']
sfs.fit(x, y)
sfs.k_feature_names_
嵌入式(embedding):結(jié)合過濾式和包裹式,學(xué)習(xí)器訓(xùn)練過程中自動進行了特征選擇伏伯,常見的有 lasso 回歸橘洞;
嵌入法也是用機器學(xué)習(xí)的方法來選擇特征,但是它和RFE的區(qū)別是它不是通過不停的篩掉特征來進行訓(xùn)練说搅,而是使用的都是特征全集炸枣。在sklearn中,使用SelectFromModel函數(shù)來選擇特征弄唧。
最常用的是使用L1正則化和L2正則化來選擇特征适肠。我們知道正則化懲罰項越大,那么模型的系數(shù)就會越小候引。當(dāng)正則化懲罰項大到一定的程度的時候侯养,部分特征系數(shù)會變成0,當(dāng)正則化懲罰項繼續(xù)增大到一定程度時澄干,所有的特征系數(shù)都會趨于0. 但是我們會發(fā)現(xiàn)一部分特征系數(shù)會更容易先變成0逛揩,這部分系數(shù)就是可以篩掉的。也就是說麸俘,我們選擇特征系數(shù)較大的特征辩稽。常用的L1正則化和L2正則化來選擇特征的基學(xué)習(xí)器是邏輯回歸。
此外也可以使用決策樹或者GBDT从媚。那么是不是所有的機器學(xué)習(xí)方法都可以作為嵌入法的基學(xué)習(xí)器呢逞泄?也不是,一般來說静檬,可以得到特征系數(shù)coef或者可以得到特征重要度(feature importances)的算法才可以做為嵌入法的基學(xué)習(xí)器炭懊。
特征構(gòu)造:
- 構(gòu)造統(tǒng)計量特征,報告計數(shù)拂檩、求和侮腹、比例、標(biāo)準(zhǔn)差等稻励;
Train_gb = Train_data.groupby("brand")
all_info = {}
for kind, kind_data in Train_gb:
info = {}
kind_data = kind_data[kind_data['price'] > 0]
info['brand_amount'] = len(kind_data)
info['brand_price_max'] = kind_data.price.max()
info['brand_price_median'] = kind_data.price.median()
info['brand_price_min'] = kind_data.price.min()
info['brand_price_sum'] = kind_data.price.sum()
info['brand_price_std'] = kind_data.price.std()
info['brand_price_average'] = round(kind_data.price.sum() / (len(kind_data) + 1), 2)
all_info[kind] = info
brand_fe = pd.DataFrame(all_info).T.reset_index().rename(columns={"index": "brand"})
data = data.merge(brand_fe, how='left', on='brand')
- 時間特征父阻,包括相對時間和絕對時間,節(jié)假日望抽,雙休日等加矛;
# 使用時間:data['creatDate'] - data['regDate'],反應(yīng)汽車使用時間煤篙,一般來說價格與使用時間成反比
# 不過要注意斟览,數(shù)據(jù)里有時間出錯的格式,所以我們需要 errors='coerce'
data['used_time'] = (pd.to_datetime(data['creatDate'], format='%Y%m%d', errors='coerce') -
pd.to_datetime(data['regDate'], format='%Y%m%d', errors='coerce')).dt.days
- 地理信息辑奈,包括分箱苛茂,分布編碼等方法已烤;
# 從郵編中提取城市信息,相當(dāng)于加入了先驗知識
data['city'] = data['regionCode'].apply(lambda x : str(x)[:-3])
data = data
非線性變換妓羊,包括 log/ 平方/ 根號等胯究;
data[numeric_features] = np.log(data[numeric_features] + 1)
data[numeric_features] = np.squre(data[numeric_features] + 1)
data[numeric_features] = np.sqrt(data[numeric_features] + 1)
降維
PCA/ LDA/ ICA;之后打算專門去詳細了解一下PCA進行降維的問題躁绸。
特征選擇也是一種降維裕循。