python資料分析及特徵工程(實戰)
python資料分析及特徵工程(實戰)
-
* 1.資料分析
-
* 1.1單屬性分析
-
* 1.1.1 異常值分析
- 1.1.2 分佈分析
- 1.1.3 對比分析
- 1.1.4 結構分析
- 1.2多屬性分析
-
* 1.2.1假設檢驗
- 1.2.2 相關係數
- 1.2.3 主成分分析PCA
-
- 2.特徵工程
-
* 2.1 資料清洗
- 2.2 特徵選擇
- 2.3 特徵變換
- 2.4 特徵構造
- 2.5 特徵降維
-
本文以天池上面的二手車交易價格預測為例,比賽連結:
https://tianchi.aliyun.com/competition/entrance/231784/introduction?spm=5176.12281925.0.0.336a7137z6Duwb
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020081017354142.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcnJ5THZhbg==,size_16,color_FFFFFF,t_70)
1.資料分析
資料分析階段也稱為 探索性資料分析(Exploratory Data Analysis, EDA)
。主要用到pandas進行資料分析,相比numpy來說處理資料更加方便。當我們那到資料之後,一般會先通過pandas自帶的info,describe,head方法來大致瞭解一下資料整體結構,例如特徵的資料型別,均值方差,以及數值是離散型還是連續性,是否含有空值等。
data = pd.read_csv("../../data/used_car_train_20200313.csv", sep=' ')
#檢視特徵空值資訊,以及資料型別
print(data.info(verbose=True))
#獲取特徵的均值,標準差,最大最小值,以及分位數
print(data.describe())
#輸出資料集前n個樣本,預設n=5
print(data.head(n=5)
輸出結果如下:通過info函式可以看到bodytype,fueltype,gearbox都是含有空值的,notRepairedDamage是object型別(對應了python中的string資料型別),對於非數值型別一般會轉換為數值型別,後面特徵工程部分會進行處理;然後describe可以看到特徵對應的均值標準差等;然後通過head函式輸出前5個樣本資料,可以大致瞭解一下資料整體,我們也可以看到regDate和createDate表示的是年月日的形式,也需要進行處理。看完整體的一個概況我們可以對特徵分別進行單個特徵和多個特徵的分析,得到進一步的資訊。
RangeIndex: 150000 entries, 0 to 149999
Data columns (total 31 columns):
SaleID 150000 non-null int64
name 150000 non-null int64
regDate 150000 non-null int64
model 149999 non-null float64
brand 150000 non-null int64
bodyType 145494 non-null float64
fuelType 141320 non-null float64
gearbox 144019 non-null float64
power 150000 non-null int64
kilometer 150000 non-null float64
notRepairedDamage 150000 non-null object
regionCode 150000 non-null int64
......
SaleID name regDate model brand bodyType fuelType gearbox power kilometer regionCode seller offerType creatDate price v_0 v_1 v_2 v_3 v_4 v_5 v_6 v_7 v_8 v_9 v_10 v_11 v_12 v_13 v_14
count 150000.000000 150000.000000 1.500000e+05 149999.000000 150000.000000 145494.000000 141320.000000 144019.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.0 1.500000e+05 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000 150000.000000
mean 74999.500000 68349.172873 2.003417e+07 47.129021 8.052733 1.792369 0.375842 0.224943 119.316547 12.597160 2583.077267 0.000007 0.0 2.016033e+07 5923.327333 44.406268 -0.044809 0.080765 0.078833 0.017875 0.248204 0.044923 0.124692 0.058144 0.061996 -0.001000 0.009035 0.004813 0.000313 -0.000688
std 43301.414527 61103.875095 5.364988e+04 49.536040 7.864956 1.760640 0.548677 0.417546 177.168419 3.919576 1885.363218 0.002582 0.0 1.067328e+02 7501.998477 2.457548 3.641893 2.929618 2.026514 1.193661 0.045804 0.051743 0.201410 0.029186 0.035692 3.772386 3.286071 2.517478 1.288988 1.038685
min 0.000000 0.000000 1.991000e+07 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.500000 0.000000 0.000000 0.0 2.015062e+07 11.000000 30.451976 -4.295589 -4.470671 -7.275037 -4.364565 0.000000 0.000000 0.000000 0.000000 0.000000 -9.168192 -5.558207 -9.639552 -4.153899 -6.546556
25% 37499.750000 11156.000000 1.999091e+07 10.000000 1.000000 0.000000 0.000000 0.000000 75.000000 12.500000 1018.000000 0.000000 0.0 2.016031e+07 1300.000000 43.135799 -3.192349 -0.970671 -1.462580 -0.921191 0.243615 0.000038 0.062474 0.035334 0.033930 -3.722303 -1.951543 -1.871846 -1.057789 -0.437034
50% 74999.500000 51638.000000 2.003091e+07 30.000000 6.000000 1.000000 0.000000 0.000000 110.000000 15.000000 2196.000000 0.000000 0.0 2.016032e+07 3250.000000 44.610266 -3.052671 -0.382947 0.099722 -0.075910 0.257798 0.000812 0.095866 0.057014 0.058484 1.624076 -0.358053 -0.130753 -0.036245 0.141246
75% 112499.250000 118841.250000 2.007111e+07 66.000000 13.000000 3.000000 1.000000 0.000000 150.000000 15.000000 3843.000000 0.000000 0.0 2.016033e+07 7700.000000 46.004721 4.000670 0.241335 1.565838 0.868758 0.265297 0.102009 0.125243 0.079382 0.087491 2.844357 1.255022 1.776933 0.942813 0.680378
max 149999.000000 196812.000000 2.015121e+07 247.000000 39.000000 7.000000 6.000000 1.000000 19312.000000 15.000000 8120.000000 1.000000 0.0 2.016041e+07 99999.000000 52.304178 7.320308 19.035496 9.854702 6.829352 0.291838 0.151420 1.404936 0.160791 0.222787 12.357011 18.819042 13.847792 11.147669 8.658418
SaleID name regDate model brand bodyType fuelType gearbox power kilometer notRepairedDamage regionCode seller offerType creatDate price v_0 v_1 v_2 v_3 v_4 v_5 v_6 v_7 v_8 v_9 v_10 v_11 v_12 v_13 v_14
0 0 736 20040402 30.0 6 1.0 0.0 0.0 60 12.5 0.0 1046 0 0 20160404 1850 43.357796 3.966344 0.050257 2.159744 1.143786 0.235676 0.101988 0.129549 0.022816 0.097462 -2.881803 2.804097 -2.420821 0.795292 0.914762
1 1 2262 20030301 40.0 1 2.0 0.0 0.0 0 15.0 - 4366 0 0 20160309 3600 45.305273 5.236112 0.137925 1.380657 -1.422165 0.264777 0.121004 0.135731 0.026597 0.020582 -4.900482 2.096338 -1.030483 -1.722674 0.245522
2 2 14874 20040403 115.0 15 1.0 0.0 0.0 163 12.5 0.0 2806 0 0 20160402 6222 45.978359 4.823792 1.319524 -0.998467 -0.996911 0.251410 0.114912 0.165147 0.062173 0.027075 -4.846749 1.803559 1.565330 -0.832687 -0.229963
3 3 71865 19960908 109.0 10 0.0 0.0 1.0 193 15.0 0.0 434 0 0 20160312 2400 45.687478 4.492574 -0.050616 0.883600 -2.228079 0.274293 0.110300 0.121964 0.033395 0.000000 -4.509599 1.285940 -0.501868 -2.438353 -0.478699
4 4 111080 20120103 110.0 5 1.0 0.0 0.0 68 5.0 0.0 6977 0 0 20160313 5200 44.383511 2.031433 0.572169 -1.571239 2.246088 0.228036 0.073205 0.091880 0.078819 0.121534 -1.896240 0.910783 0.931110 2.834518 1.923482
1.1單屬性分析
1.1.1 異常值分析
(1)連續值的異常值
對於連續值來說,對於那些過大的資料值或太小的資料值對整體都會有很大的影響,不能很好地表現真實情況。例如在反映公司的人均收入時,少部分人的年薪可能達到1000w甚至更高,而大部分公司職員的收入都在中等水平,而這些較大值在統計公司人均收入水平時就會起到一個很大的拔高作用,使得失去了意義。
對於連續異常值的判斷標準是我們通常會設定一個上界和下界,在上下界之外的都屬於異常值,而這個上下界的設定如下:設q_low和q_high分別為資料的下四分位數和上四分位數,value_low和value_high分別為下界和上界,那麼value_low=q_low-k(q_high-
q_low),value_high=q_high+k(q_high-q_low),k通常取值1.5或3。
對於連續異常值的處理可以直接去掉(異常值不多的情況),也可以用邊界值代替異常值。空值也可以作為異常值進行處理,對於連續值屬性可以用均值來代替空值。
現在對上面的power特徵進行異常值分析,這裡加入視覺化展示,通過視覺化可以讓資料更加直觀,這裡用到的是seaborn中的箱線圖。
plt.figure()
sns.boxplot(y=data["power"])
plt.show()
q_low = data["power"].quantile(q=0.25)
q_high = data["power"].quantile(q=0.75)
q_interval = q_high - q_low
index_high = data["power"] <= (q_high + 1.5 * q_interval)
index_low = data["power"] >= (q_low - 1.5 * q_interval)
print("異常值樣本數量:",len(data)-len(data[index_low & index_high]))
plt.figure()
sns.boxplot(y=data["power"][index_low & index_high])
plt.show()
結果如下:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200617160826147.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcnJ5THZhbg==,size_16,color_FFFFFF,t_70,,#pic_center)
(2)離散值的異常值
離散異常值主要為那些在我們特徵屬性定義之外的值,例如空值,或者我們定義了工資水平為高中低三種值但卻出現了其他值,這些都是異常值。對於異常值在數量不大的時候我們都可以直接刪除掉,對於空值和異常值可以使用眾數來代替也可以把控制當做一個單獨的值進行處理。
現在我們對上面的notRepairedDamage進行分析:我們通過value_counts檢視離散屬性每個值的數量,我們發現除了0和1之外我們發現出現了“-”,而通過題目給出的含義我們知道notRepairedDamage只有0
1兩類取值,因此我們可以用眾數來替換,也可以直接用nan空值來替換,因為如果建模階段採用樹模型是可以不對空值處理的
print(data['notRepairedDamage'].value_counts())
結果:
0.0 111361
- 24324
1.0 14315
Name: notRepairedDamage, dtype: int64
(3)知識異常值
知識異常值是指在嘗試範圍或限定知識範圍外的值都是知識異常值,例如身高出現了3米4米,或者題目給定了某些特徵的取值範圍,例如題目給出了power發動機的範圍是0~600,那麼我們對power的處理其實不需要向上面那樣來確定上下界,因為題目已經給出了。
1.1.2 分佈分析
1.如何判斷是不是正態分佈?
對於獲得的資料我們可以直接獲得其概率分佈,但是這樣的得到的分佈往往意義不大,所以我們通產常會判斷它是否服從正態分佈。通常我們會通過資料的 偏度 和
峰度
來判斷是否是正態分佈,偏度係數是資料平均值偏離狀態的一種衡量,對於對稱性分佈而言平均值和中位數是比較接近的(例如正態分佈),而非對稱分佈中位數和均值相差較大,這種分佈也叫做有偏態的分佈。
(1)偏度係數大於0,正偏(右偏),均值較大(相對於中位數而言)
(2)偏度係數小於0,負偏(左偏),均值較小
正態分佈的峰度係數為3,如果某個分佈的峰度係數與正態分佈的峰度係數相差超過2,那麼這個分佈就不是正太分佈。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200617201200572.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcnJ5THZhbg==,size_16,color_FFFFFF,t_70)
我們可以看一下上面的price屬性,通過峰度係數和直方圖能明顯看出是一個正偏的分佈。
print("偏度係數:",data["price"].skew())
print("峰度係數:",data["price"].kurtosis())
plt.figure()
sns.distplot(data["price"])
plt.show()
輸出結果:
偏度係數: 3.3464867626369608
峰度係數: 18.995183355632562
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200617202228636.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcnJ5THZhbg==,size_16,color_FFFFFF,t_70,#pic_center)
通常我們會將偏態的分佈轉為正態分佈,但是也並不是什麼時候都需要,下面可以看一下什麼情況比較適合做正態分佈在轉換。
2.為什麼要轉換為正態分佈?
(1)首先有些模型的應用條件就是要求你的資料滿足正態性分佈的,比如說貝葉斯、邏輯迴歸、KNN、Kmean等涉及到概率分佈、引數距離比較等,轉換為正態分佈,模型條件更充足。但並不意味著你的模型結果會更好一點。
(2)其次正態分佈,資料的泛化性高。因為自然界很多事物的概率密度很大是正態分佈,而偏態分佈(資料不均衡),會導致機器學歪了,用這個“歪”模型測試資料不會很靠譜。
(3)從目標分佈來說,偏態分佈會導致label資料的MSE出現誤導,或許結果看著很小,但實際結果很大,可以考慮糾正一下分佈正態性。
3.什麼情況下做正態分佈轉換?
(1)並不是說所有分佈都要轉換為正態分佈的,因為你不能保證正態分佈就很有效,其次不是所有資料分佈都類似於正態分佈,可能是其他亂七八槽的分佈,又或許是大數定理下的分佈漸進正態性。(比如泊松分佈、卡方分佈等),這些非正態分佈就沒必要轉換。
(2)偏態分佈最好糾正,或許有用(理論上有用,實際上可能沒用,因為資料量的分佈限制)。這就好比於“你打個噴嚏可以吃板藍根,或許有用,或許沒用,但絕對不會導致你噴嚏更加嚴重”,就是說針對偏態分佈,你可以正態化一下。
4.常見的轉換方式
接著分析一下上面的price屬性,這裡我們可以通過seaborn中的distplot函式中的fit引數擬合更符合哪種分佈,方便後面進行轉換。
plt.figure()
sns.distplot(data["price"],fit=ss.lognorm)
plt.show()
plt.figure()
sns.distplot(data["price"],fit=ss.norm)
plt.show()
plt.figure()
sns.distplot(data["price"],fit=ss.johnsonsu)
plt.show()
輸出結果:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200617211517616.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcnJ5THZhbg,size_16,color_FFFFFF,t_70,#pic_center)
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200617211541345.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcnJ5THZhbg,size_16,color_FFFFFF,t_70,#pic_center)
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/202006172116358.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcnJ5THZhbg==,size_16,color_FFFFFF,t_70,#pic_center)
通過上面的結果可以看到無界約翰遜分佈的擬合效果更好,因此在做分佈轉換時,可以利用sklearn.preprocessing包裡的power_transform進行轉換,如下:
yc_data = power_transform(X=np.array(data["price"]).reshape(-1,1),method="yeo-johnson")
plt.figure()
sns.distplot(yc_data)
plt.show()
輸出結果:可以看到轉換後的正態分佈如下
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200617212148180.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcnJ5THZhbg==,size_16,color_FFFFFF,t_70,#pic_center)
1.1.3 對比分析
(1)絕對數比較 :例如比較收入或身高等屬性。
(2)相對數比較 :把幾個有聯絡的指標聯合構成新的數,主要有以下幾種類別:
- 結構相對數:部分與整體進行相比,例如用產品合格率評估產品質量。
- 比例相對數:總體內用不同部分的數值進行比較,例如傳統三大產業:農業、重工業和服務業之間的比例可以進行相互比較。
- 比較相對數:同一時空下相似或同質的指標進行對比,例如不同網際網路公司的待遇水平或者不同時期同一商品的價格比較。
- 動態相對數:一般具有時間概念,例如速度、使用者數量的增速。
- 強度相對數:性質不同但又相互聯絡的屬性進行聯合,例如人均GDP、密度等。
那麼有了這些基本的概念屬性之後如何進行比較呢?大致可以從以下幾個維度進行比較分析:
- 時間維度
:現在和過去進行比較等進而推斷未來的走勢。其中有“同比”和“環比”兩種方式,“同比”是和去年同期進行比較,例如今年某汽車銷量同比增加百分之幾;“環比”:例如今年6月份汽車銷量環比是和今年5月份進行比較。- 空間維度 :可指現實方位上的空間,例如不同城市、不同國家·;也可指邏輯上的空間,例如一家公司的不同部門進行比較。
- 經驗和計劃
:經驗比較:例如歷史上失業率發生百分之幾就可能發生暴亂,我們把自己國家的失業率和這個進行比較就是經驗比較;計劃比較:是指實時速度和計劃排期進行比較。
例如我們對不同燃油型別汽車的二手價格進行對比以及不同車型二手車價格對比來判斷屬性(燃油型別、車型等)與目標值的關係,以及屬性不同取值之間的對比:
tmp = data.groupby("fuelType")["price"].agg({"price_fuelType_mean":"mean"})
print(tmp.sort_values(by='price_fuelType_mean',ascending=False))
tmp = data.groupby("gearbox")["price"].agg({"price_gearbox_mean":"mean"})
print(tmp.sort_values(by='price_gearbox_mean',ascending=False))
tmp = data.groupby(["gearbox","fuelType"])["price"].agg({"price_gearbox_fuelType_mean":"mean"})
print(tmp)
輸出結果:我們可以看到燃油型別為4(混合動力)和變速箱為1(自動)的二手車價格更高。
price_fuelType_mean
fuelType
4.0 12169.703390
6.0 9613.694444
1.0 8698.679854
3.0 5171.603053
5.0 4709.755556
0.0 4647.500558
2.0 4556.420434
price_gearbox_mean
gearbox
1.0 10645.259878
0.0 4622.591587
price_gearbox_fuelType_mean
gearbox fuelType
0.0 0.0 3823.474866
1.0 6749.562793
2.0 3418.595572
3.0 4905.851240
4.0 5931.333333
5.0 2864.837838
6.0 5027.666667
1.0 0.0 8802.664378
1.0 12945.676972
2.0 6355.431739
3.0 8387.200000
4.0 13520.278351
5.0 13242.500000
6.0 11142.370370
1.1.4 結構分析
研究一個總體的組成結構方面的差異和相關性。
(1)靜態結構分析:直接分析總體的組成。例如直接分析第一產業、第二產業、第三產業的比例為13%,46%,41%,這樣就確定了我國的產業結構,同時和美國、日本等進行比較,來衡量我們三大產業是否均衡,下一步如何決策等。
(2)動態結構分析:以時間為軸分析結構變化的趨勢。例如知道十五期間三大產業的佔比,那麼對於十一五期間三大產業的結構是如何變化的就能夠反映國家性質上的反應方向。
例如對於bodyType屬性我們可以分析各個車型數量:
plt.figure()
sns.countplot(x='bodyType',data=data)
plt.show()
print(data['bodyType'].value_counts())
輸出:
0.0 45926
1.0 35272
2.0 30324
3.0 13491
4.0 9609
5.0 7607
6.0 6482
7.0 1289
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200810182553156.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcnJ5THZhbg==,size_16,color_FFFFFF,t_70)
1.2多屬性分析
1.2.1假設檢驗
原理:
(1)假設檢驗含有原假設和備擇假設,一般是將我們想拒絕的假設作為原假設。這個時候引出我們的推斷原理:小概率事件在一次實驗中實際是不可能發生的,如果小概率事件發生了,則與推斷原理矛盾,那麼就拒絕原假設。
(2)那麼概率小於多少時可認為小概率事件?在假設檢驗中,設這個概率為 α \alpha α ,也稱為顯著性水平, α \alpha α
常取的值為0.01,0.05,0.1。
假設檢驗的流程:
- 提出假設:H0(原假設),H1(備擇假設)
- 構造檢驗統計量(這也是不同假設檢驗的區別之處):例如 u u u 檢驗統計量: u = x − μ 0 σ / n u=\frac{x-\mu_0}{\sigma/\sqrt{}n} u = σ / n x − μ 0
- 給定顯著性水平 α \alpha α
- 作出假設:若原假設H0為真,那麼備擇假設H1就是小概率事件。稱w: { ∣ u ∣ > u α / 2 } {|u|>u_{\alpha/2}} { ∣ u ∣ > u α / 2 } 為拒絕域,若我們的檢驗統計量落在區域w內,則拒絕H0,否則沒有充分的證據拒絕H0,其中 u α / 2 u_{\alpha/2} u α / 2 通過查表獲得。
python中scipy.stats給我們提供了假設檢驗的一些方法,我們可以檢驗是否為正態分佈、利用卡方檢驗來進行兩個及兩個以上樣本率(
構成比)以及兩個分類變數的關聯性分析、利用t檢驗來檢驗兩組資料均值是否有差異、以及通過f檢驗多組資料的方差是否有差異。
例如我們可以利用卡方檢驗來進行判斷公司中員工是否離職與部門之間是否有關係,更多卡方檢驗的知識可以參考 統計學——卡方檢驗和卡方分佈
,舉個栗子:
import numpy as np
import pandas as pd
import scipy.stats as ss
norm_dist = ss.norm.rvs(size=1000)
# 檢驗是否為正態分佈
print(ss.normaltest(norm_dist))
# 卡方檢驗
print(ss.chi2_contingency([[15,95],[85,5]]))
# 檢驗兩組分佈的均值是否有差異
print(ss.ttest_ind(ss.norm.rvs(size=10), ss.norm.rvs(size=20)))
# 方差檢驗,檢驗多組資料的方差是否有差別
print(ss.f_oneway([49,50,39,40,43],[28,32,30,26,34],[38,40,45,42,48]))
1.2.2 相關係數
相關係數是用來衡量兩組資料或者兩組樣本的分佈趨勢、變化趨勢、一致性程度的因子,分為正相關、負相關、不相關。
常見的相關係數有:
Pearson相關係數: r ( X , Y ) = C o v ( X , Y ) σ x σ y
r(X,Y)=\frac{Cov(X,Y)}{\sigma_x\sigma_y} r ( X , Y ) = σ x σ y
C o v ( X , Y ) ,Cov(X,Y)為X,Y的協方差,σ為標準差。
Spearman相關係數: ρ s = 1 − 6 ∗ ∑ d i 2 n ( n 2 − 1 )
\rho_s=1-\frac{6*\sum{d_i2}}{n(n2-1)} ρ s = 1 − n ( n 2 − 1 )
6 ∗ ∑ d i 2 ,n是資料數量,d是兩組資料排名的名次差。例如x={6,11,8}
y={7,4,3},rank_x={1,3,2},rank_y={3,2,1},d={-2,1,1}。
我們通常會分析label資料和特徵之間的相關性,對於那些不怎麼相關的特徵可以去掉。例如我們分析price和特徵之間的關係。
correlation = data.corr()
print(correlation['price'].sort_values(ascending = False))
plt.figure()
sns.heatmap(correlation,square = True)
plt.show()
輸出結果:
price 1.000000
v_12 0.692823
v_8 0.685798
v_0 0.628397
regDate 0.611959
gearbox 0.329075
bodyType 0.241303
power 0.219834
fuelType 0.200536
v_5 0.164317
model 0.136983
v_2 0.085322
v_6 0.068970
......
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200618120339857.png?x-oss-
process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcnJ5THZhbg==,size_16,color_FFFFFF,t_70,#pic_center)
1.2.3 主成分分析PCA
PCA通常也是特徵降維的一種手段。我們可以直接使用sklearn提供的pca進行降維處理。就先放到後面特徵工程中的降維一起講。
2.特徵工程
2.1 資料清洗
這部分其實就和分析部分的異常值分析一樣,我們會對異常值和空值進行處理,其實資料分析部分和特徵工程並不是完全獨立的,分析就是為了更好地瞭解資料,更好的完成特徵工程。對於異常值的處理就跟前面所說一樣,空值可以刪除也可以進行填充。
#缺失值處理:離散值用眾數處理,連續值用均值填充。
values = {"bodyType": data["bodyType"].value_counts().index[0], "fuelType":data["fuelType"].value_counts().index[0],
"gearbox": data["gearbox"].value_counts().index[0], "model": data["model"].mean()}
data.fillna(value=values, inplace=True)
2.2 特徵選擇
在我們拿到資料之後,除了向開頭那樣瞭解資料大致結構之外,我們還通常會檢視離散值的value_counts,除了檢視是否存在異常值之外,還會看離散值的分佈是否均衡。
例如通過檢視offerType和seller的value_counts我們發現這兩個特徵其實對於預測價格並沒有什麼作用,所以可以直接刪掉。
print(data["offerType"].value_counts(),"\n")
print(data["seller"].value_counts())
輸出結果:
0 150000
Name: offerType, dtype: int64
0 149999
1 1
Name: seller, dtype: int64
當然會有一些常用的特徵選擇思路:過濾思想(Filter),包裹思想(Wrapper),嵌入思想(Embedding)。
- 過濾思想:直接評價特徵與標註的相關性,如果相關性比較小就去掉。
- 包裹思想:設特徵集合為X:{x1, x2, …, xn}。最佳特徵組合是其子集,目的就是找到這個子集,首先需要設定一個評價指標,例如準確率,然後遍歷特徵子集找到準確率最高的。也可以進行迭代操作,例如先確定幾個較大的子集,然後確定一個較大子集之後對其進行繼續拆分,直到評價指標下降過快或低於閾值時停止。
- 嵌入思想:常用的方式就是正則化。
from sklearn.svm import SVR
from sklearn.tree import DecisionTreeRegressor
from sklearn.feature_selection import SelectKBest,RFE, SelectFromModel
#SelectKBest是過濾思想常用的包, RFE是包裹思想, SelectFromModel是嵌入思想
features = [col for col in data.columns if col not in ['price','SaleID']]
X = data.loc[:,features]
Y = data.loc[:,"price"]
#過濾思想
skb = SelectKBest(k=8)
skb.fit(X,Y)
print(skb.transform(X).shape)
#包裹思想
rfe = RFE(estimator=SVR(kernel="linear"), n_features_to_select=8, step=1)
print(rfe.fit_transform(X,Y).shape)
#嵌入思想
#特徵重要性或特徵係數小於threshold將被捨棄
sfm = SelectFromModel(estimator=DecisionTreeRegressor(), threshold=0.1)
print(sfm.fit_transform(X,Y))
2.3 特徵變換
- 指數化: f ( x ) = e x f(x)=e^x f ( x ) = e x ,當大於0範圍內原始資料差距很小,經過指數變換後資料差距邊大。
- 對數化: f ( x ) = l n x f(x)=lnx f ( x ) = l n x ,當在大於1的範圍內資料變化很大時,變換後差距變小,可以將一個大的數縮放到我們容量計算的範圍內。例如,月收入1億、10000,1000直接通過乘除比較容易丟失精度,經過log10X變換後得到8,4,3,然後可以得到資料的大小關係。
- 離散化:將連續變數分成幾段(bins),資料分桶。
- 歸一化:min-max: x ′ = x − x m i n x m a x − x m i n x'=\frac{x-x_{min}}{x_{max}-x_{min}} x ′ = x m a x − x m i n x − x m i n ;z-score: x ′ = x − μ σ x'=\frac{x-\mu}{\sigma} x ′ = σ x − μ
- 數值化:(1)標籤化,例如將收入水平low, medium, high轉換為0,1,2;(2)獨熱編碼:例如對顏色特徵進行獨熱編碼,red:[1,0,0,0],yellow:[0,1,0,0],blue:[0,0,1,0],green:[0,0,0,1]
例如我們對題目中的model和power進行分桶操作:
#將連續值離散化:資料分桶
bin = [i * 10 for i in range(-1,61)]
print(bin)
data['power_bin'] = pd.cut(data['power'], bin, labels=False)
bin = [i * 10 for i in range(-1,26)]
print(bin)
data['model_bin'] = pd.cut(data['model'], bin, labels=False)
2.4 特徵構造
特徵構造部分也是特徵工程的一個難點,沒有固定的一些流程方法,但是有一些經驗性的東西可以也借鑑一下。例如對於時間特徵我們可以進行相減得到時間差,也可以進行不同特徵之間的交叉。
例如,通過特徵交叉,使用分類特徵“brand”、“model”、等與“price”、“days”、“power”進行特徵交叉,在最終使用的時候我們會選擇真正有用的特徵:
data_gb = data.groupby("brand")
all_info = {}
for kind, kind_data in data_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_mean'] = kind_data.price.mean()
info['brand_price_skew'] = kind_data.price.skew()
info['brand_price_kurt'] = kind_data.price.kurt()
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')
2.5 特徵降維
除了PCA降維之外,我們還可以用LDA降維(Linear Discriminant
Analysis,線性判別式)。降維並不等同於特徵選擇,降維本質上是從一個維度空間對映到另一個維度空間,特徵的多少別沒有減少,當然在對映的過程中特徵值也會相應的變化。
features = [col for col in data.columns if col not in ['price','SaleID']]
X = data.loc[:,features]
Y = data.loc[:,"price"]
from sklearn.decomposition import PCA
lower_dim = PCA(n_components=8)
print(lower_dim.fit_transform(X))
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
print(LinearDiscriminantAnalysis(n_components=8).fit_transform(X,Y))
參考部落格:
統計學——卡方檢驗和卡方分佈
機器學習中特徵降維和特徵選擇的區別