day3:今天進(jìn)行的是特征工程部分,也就是對(duì)一些特征進(jìn)行處理,構(gòu)造適合各種模型的數(shù)據(jù)。
特征工程的目標(biāo)
對(duì)于特征進(jìn)行進(jìn)一步分析,并對(duì)于數(shù)據(jù)進(jìn)行處理
完成對(duì)于特征工程的分析,并對(duì)于數(shù)據(jù)進(jìn)行一些圖表或者文字總結(jié)并打卡。
數(shù)據(jù)預(yù)處理
空值處理:數(shù)據(jù)清洗包括,total_rooms 用中位數(shù)代替,ocean_proximity 用one-hot-encode編碼轉(zhuǎn)為數(shù)值型,one-hot-encode與直接編碼為 [0,1,2,3..] 相比,不會(huì)引入相似或者相近的意義。比如 2 和 3 在數(shù)值上相近,但是它們各自表示的NEAR BAY與INLAND屬性項(xiàng)并不存在實(shí)質(zhì)的相似關(guān)系(盡管有這個(gè)可能)。
構(gòu)造特征:數(shù)據(jù)集里因?yàn)椴煌琩istrict里戶數(shù)不同,所以 total_rooms、total_bedrooms、population 這些屬性可能沒(méi)有太大的意義,而每戶的房間數(shù)、臥室數(shù)、人數(shù)相關(guān)性則較,所有在數(shù)據(jù)集中可以新增這些特征。
歸一化:數(shù)值型特征的一個(gè)特點(diǎn)是,不同的屬性取值范圍差別極大,對(duì)模型學(xué)習(xí)、正則項(xiàng)設(shè)置不利。有兩種簡(jiǎn)單的方法可以應(yīng)對(duì)這種情況:變換到0-1區(qū)間的歸一化或者標(biāo)準(zhǔn)正態(tài)分布。前者對(duì)異常點(diǎn)非常敏感,如果存在某個(gè)值比正常值區(qū)間距離很遠(yuǎn),這種處理會(huì)導(dǎo)致正常數(shù)據(jù)點(diǎn)分布過(guò)于集中;后者對(duì)過(guò)于集中的數(shù)據(jù)分布敏感,如果原數(shù)據(jù)集方差很小,除以近0的數(shù)值容易導(dǎo)致精度失真。
偏度處理:在大部分?jǐn)?shù)值型特征中,通常分布不符合正態(tài)分布,而是類似于“長(zhǎng)尾”。例如房?jī)r(jià)中,低價(jià)占大部分,豪宅屬于小部分。應(yīng)對(duì)這種數(shù)據(jù)分布,一般可以通過(guò)神奇的log化處理轉(zhuǎn)化類近似正態(tài)分布
異常值處理:
對(duì)于不同的特征,我們會(huì)用箱形圖或者小提琴圖可視化數(shù)據(jù)的分布情況。如果數(shù)據(jù)是滿足正態(tài)分布的話,我們還可以按照3δ準(zhǔn)則,將3δ以外的數(shù)據(jù)去除。本次學(xué)習(xí)中去除的方法是將數(shù)據(jù)的25%-75%范圍擴(kuò)展box_scale倍,在此范圍以外的進(jìn)行去除。
常用處理:
通過(guò)箱線圖(或 3-Sigma)分析刪除異常值;
就是下面代碼的中的示例,采用箱線圖進(jìn)行觀察異常值。BOX-COX 轉(zhuǎn)換(處理有偏分布);
對(duì)于一些非正態(tài)的分布,及偏值較大或者較小的時(shí)候,使用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)
- 長(zhǎng)尾截?cái)啵?br> 長(zhǎng)尾截?cái)嘀饕彩欠植疾环险龖B(tài)分布,而是類似于“長(zhǎng)尾”。例如房?jī)r(jià)中,低價(jià)占大部分,豪宅屬于小部分。應(yīng)對(duì)這種數(shù)據(jù)分布,一般可以通過(guò)神奇的log化處理轉(zhuǎn)化類近似正態(tài)分布。
對(duì)于### 箱盒圖共有兩個(gè)用途,分別 1. 直觀地識(shí)別數(shù)據(jù)中異常值(離群點(diǎn));
- 直觀地判斷數(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)
# 對(duì)于刪除后 從新整理一下
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
問(wèn)題:
在處理異常值的時(shí)候一般去點(diǎn)什么范圍的值?
隊(duì)友的回答 :
image.png
特征歸一化和標(biāo)準(zhǔn)化
數(shù)據(jù)標(biāo)準(zhǔn)化
數(shù)據(jù)標(biāo)準(zhǔn)化,我的理解是將數(shù)據(jù)的分布進(jìn)行線性變化,簡(jiǎn)單的說(shuō)就是將數(shù)據(jù)的轉(zhuǎn)化成特定的分布狀態(tài)。數(shù)據(jù)標(biāo)準(zhǔn)化的方法有很多種,常用的有“最小—最大標(biāo)準(zhǔn)化”、“Z-score標(biāo)準(zhǔn)化”和“取對(duì)數(shù)模式”等。
-
Min-max 標(biāo)準(zhǔn)化:
Min-max標(biāo)準(zhǔn)化方法是對(duì)原始數(shù)據(jù)進(jìn)行線性變換。設(shè)minA和maxA分別為屬性A的最小值和最大值,將A的一個(gè)原始值x通過(guò)min-max標(biāo)準(zhǔn)化映射成在區(qū)間[0,1]中的值x',其公式為:新數(shù)據(jù)=(原數(shù)據(jù)-極小值)/(極大值-極小值)
用svm對(duì)數(shù)據(jù)進(jìn)行訓(xùn)練前一般采用此方法對(duì)數(shù)據(jù)進(jìn)行標(biāo)準(zhǔn)化。 -
Z-score 標(biāo)準(zhǔn)化:
這種方法基于原始數(shù)據(jù)的均值(mean)和標(biāo)準(zhǔn)差(standard deviation)進(jìn)行數(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)差
-
取對(duì)數(shù)模式:
對(duì)于一些長(zhǎng)尾數(shù)據(jù)進(jìn)行取對(duì)數(shù)處理,可以使得分布更接近正態(tài)分布。對(duì)數(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ù)縮小到一點(diǎn)的范圍,但是不改變數(shù)據(jù)的分布情況,目的是為了更好的計(jì)算,防止太大的數(shù)據(jù),計(jì)算復(fù)雜。 為了數(shù)據(jù)處理方便提出來(lái)的,把數(shù)據(jù)映射到0~1(或-1~1)范圍之內(nèi)處理,更加便捷快速,應(yīng)該歸到數(shù)字信號(hào)處理范疇之內(nèi)。
至于具體的方法這里存在著爭(zhēng)議。
因?yàn)闃?biāo)準(zhǔn)化和歸一化都屬于四種Feature scaling(特征縮放),這四種分別是:
Rescaling (min-max normalization) 有時(shí)簡(jiǎn)稱normalization(有點(diǎn)坑)
image.png
2.Mean normalization
image.png
3.Standardization(Z-score normalization)
image.png
4.Scaling to unit length
image.png
為什么數(shù)據(jù)要進(jìn)行歸一化呢,因?yàn)閿?shù)據(jù)做歸一化的好處 1.加快了梯度下降求最優(yōu)解的速度和有可能提高精度。2.提高精度,這在涉及到一些距離計(jì)算的算法時(shí)效果顯著,比如算法要計(jì)算歐氏距離,所以歸一化很有必要,他可以讓各個(gè)特征對(duì)結(jié)果做出的貢獻(xiàn)相同。
問(wèn)題:
- 什么模型需要?dú)w一化?
概率模型不需要?dú)w一化,因?yàn)樗鼈儾魂P(guān)心變量的值,而是關(guān)心變量的分布和變量之間的條件概率。像svm、線性回歸之類的最優(yōu)化問(wèn)題就需要?dú)w一化。決策樹(shù)屬于前者。歸一化也是提升算法應(yīng)用能力的必備能力之一。
數(shù)據(jù)分桶:
數(shù)據(jù)分桶主要是對(duì)于連續(xù)值的一個(gè)處理,因?yàn)橛行┠P托枰赡懿恢苯犹幚磉B續(xù)值,那么我們可以將數(shù)據(jù)進(jìn)行分桶,是的連續(xù)值變成分類值,可以進(jìn)一步給模型學(xué)習(xí)。或者在存在一些缺失值的時(shí)候,用分桶的方法,這時(shí)候我們的缺失值也進(jìn)桶了,
為什么要做數(shù)據(jù)分桶呢,原因有很多?
- 離散后稀疏向量?jī)?nèi)積乘法運(yùn)算速度更快,計(jì)算結(jié)果也方便存儲(chǔ),容易擴(kuò)展;
- 離散后的特征對(duì)異常值更具魯棒性,如 age>30 為 1 否則為 0,對(duì)于年齡為 200 的也不會(huì)對(duì)模型造成很大的干擾;
- LR 屬于廣義線性模型,表達(dá)能力有限,經(jīng)過(guò)離散化后,每個(gè)變量有單獨(dú)的權(quán)重,這相當(dāng)于引入了非線性,能夠提升模型的表達(dá)能力,加大擬合;
- 離散后特征可以進(jìn)行特征交叉,提升表達(dá)能力,由 M+N 個(gè)變量編程 M*N 個(gè)變量,進(jìn)一步引入非線形,提升了表達(dá)能力;
- 特征離散后模型更穩(wěn)定,如用戶年齡區(qū)間,不會(huì)因?yàn)橛脩裟挲g長(zhǎng)了一歲就變化
等頻分桶;
區(qū)間的邊界值要經(jīng)過(guò)選擇,使得每個(gè)區(qū)間包含大致相等的實(shí)例數(shù)量。比如說(shuō) N=10 ,每個(gè)區(qū)間應(yīng)該包含大約10%的實(shí)例
等距分桶;
從最小值到最大值之間,均分為 N 等份。 如果 A,B 為最小最大值, 則每個(gè)區(qū)間的長(zhǎng)度為 W=(B?A)/N , 則區(qū)間邊界值為A+W,A+2W,….A+(N?1)W 。這里只考慮邊界,每個(gè)等份的實(shí)例數(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í)候數(shù)據(jù)有很多的特征,維度過(guò)大,無(wú)法將所有的特征進(jìn)行學(xué)習(xí),同時(shí)有些特征對(duì)于模型的學(xué)習(xí)預(yù)測(cè)并不是很少,甚至?xí)窃胍?。所以我們要挑選出一些好的特征進(jìn)行用。當(dāng)然最好的特征篩選就是了解行業(yè)背景,這是最有效的辦法。如果這個(gè)特征集合有時(shí)候也可能很大,在嘗試降維之前,我們有必要用特征工程的方法去選擇出較重要的特征結(jié)合,這些方法不會(huì)用到領(lǐng)域知識(shí),而僅僅是統(tǒng)計(jì)學(xué)的方法。
最簡(jiǎn)單的方法就是方差篩選。方差越大的特征,那么我們可以認(rèn)為它是比較有用的。如果方差較小,比如小于1,那么這個(gè)特征可能對(duì)我們的算法作用沒(méi)有那么大。最極端的,如果某個(gè)特征方差為0,即所有的樣本該特征的取值都是一樣的,那么它對(duì)我們的模型訓(xùn)練沒(méi)有任何作用,可以直接舍棄。在實(shí)際應(yīng)用中,我們會(huì)指定一個(gè)方差的閾值,當(dāng)方差小于這個(gè)閾值的特征會(huì)被我們篩掉。sklearn中的VarianceThreshold類可以很方便的完成這個(gè)工作。
過(guò)濾式(filter):先對(duì)數(shù)據(jù)進(jìn)行特征選擇,然后在訓(xùn)練學(xué)習(xí)器,常見(jiàn)的方法有
方差選擇發(fā):就是上面說(shuō)的,計(jì)算特征的方差,把方差小于閾值的特征篩掉。
相關(guān)系數(shù)法:指的是通過(guò)熱力圖的方式,計(jì)算特征和標(biāo)簽之間的相關(guān)系數(shù),小于某個(gè)閾值的特征篩掉。
# 相關(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)
卡方檢驗(yàn)法 :卡方檢驗(yàn)可以檢驗(yàn)?zāi)硞€(gè)特征分布和輸出值分布之間的相關(guān)性。個(gè)人覺(jué)得它比比粗暴的方差法好用。如果大家對(duì)卡方檢驗(yàn)不熟悉,可以參看這篇卡方檢驗(yàn)原理及應(yīng)用,這里就不展開(kāi)了。在sklearn中,可以使用chi2這個(gè)類來(lái)做卡方檢驗(yàn)得到所有特征的卡方值與顯著性水平P臨界值,我們可以給定卡方值閾值, 選擇卡方值較大的部分特征。
互信息法;即從信息熵的角度分析各個(gè)特征和輸出值之間的關(guān)系評(píng)分。在決策樹(shù)算法中我們講到過(guò)互信息(信息增益)。互信息值越大,說(shuō)明該特征和輸出值之間的相關(guān)性越大,越需要保留。在sklearn中,可以使用mutual_info_classif(分類)和mutual_info_regression(回歸)來(lái)計(jì)算各個(gè)輸入特征和輸出值之間的互信息。
包裹式(wrapper):直接把最終將要使用的學(xué)習(xí)器的性能作為特征子集的評(píng)價(jià)準(zhǔn)則,常見(jiàn)方法有 LVM(Las Vegas Wrapper) ;
最常用的包裝法是遞歸消除特征法(recursive feature elimination,以下簡(jiǎn)稱RFE)。遞歸消除特征法使用一個(gè)機(jī)器學(xué)習(xí)模型來(lái)進(jìn)行多輪訓(xùn)練,每輪訓(xùn)練后,消除若干權(quán)值系數(shù)的對(duì)應(yīng)的特征,再基于新的特征集進(jìn)行下一輪訓(xùn)練。在sklearn中,可以使用RFE函數(shù)來(lái)選擇特征。
LVM的實(shí)戰(zhàn)案例
包裹式好還包括SFS和SBS算法對(duì)特征進(jìn)行過(guò)濾,不過(guò)不詳細(xì)展開(kāi)了,直接附上優(yōu)秀的大佬博客、博客2
# k_feature 太大會(huì)很難跑,沒(méi)服務(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é)合過(guò)濾式和包裹式,學(xué)習(xí)器訓(xùn)練過(guò)程中自動(dòng)進(jìn)行了特征選擇,常見(jiàn)的有 lasso 回歸;
嵌入法也是用機(jī)器學(xué)習(xí)的方法來(lái)選擇特征,但是它和RFE的區(qū)別是它不是通過(guò)不停的篩掉特征來(lái)進(jìn)行訓(xùn)練,而是使用的都是特征全集。在sklearn中,使用SelectFromModel函數(shù)來(lái)選擇特征。
最常用的是使用L1正則化和L2正則化來(lái)選擇特征。我們知道正則化懲罰項(xiàng)越大,那么模型的系數(shù)就會(huì)越小。當(dāng)正則化懲罰項(xiàng)大到一定的程度的時(shí)候,部分特征系數(shù)會(huì)變成0,當(dāng)正則化懲罰項(xiàng)繼續(xù)增大到一定程度時(shí),所有的特征系數(shù)都會(huì)趨于0. 但是我們會(huì)發(fā)現(xiàn)一部分特征系數(shù)會(huì)更容易先變成0,這部分系數(shù)就是可以篩掉的。也就是說(shuō),我們選擇特征系數(shù)較大的特征。常用的L1正則化和L2正則化來(lái)選擇特征的基學(xué)習(xí)器是邏輯回歸。
此外也可以使用決策樹(shù)或者GBDT。那么是不是所有的機(jī)器學(xué)習(xí)方法都可以作為嵌入法的基學(xué)習(xí)器呢?也不是,一般來(lái)說(shuō),可以得到特征系數(shù)coef或者可以得到特征重要度(feature importances)的算法才可以做為嵌入法的基學(xué)習(xí)器。
特征構(gòu)造:
- 構(gòu)造統(tǒng)計(jì)量特征,報(bào)告計(jì)數(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')
- 時(shí)間特征,包括相對(duì)時(shí)間和絕對(duì)時(shí)間,節(jié)假日,雙休日等;
# 使用時(shí)間:data['creatDate'] - data['regDate'],反應(yīng)汽車使用時(shí)間,一般來(lái)說(shuō)價(jià)格與使用時(shí)間成反比
# 不過(guò)要注意,數(shù)據(jù)里有時(shí)間出錯(cuò)的格式,所以我們需要 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)于加入了先驗(yàn)知識(shí)
data['city'] = data['regionCode'].apply(lambda x : str(x)[:-3])
data = data
非線性變換,包括 log/ 平方/ 根號(hào)等;
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;之后打算專門去詳細(xì)了解一下PCA進(jìn)行降維的問(wèn)題。
特征選擇也是一種降維。




