數據準備<3>:數據預處理

<font size='+0.5'>數據預處理是指由於算法或者分析須要,對通過數據質量檢查後的數據進行轉換、衍生、規約等操做的過程。整個數據預處理工做主要包括五個方面內容:簡單函數變換、標準化、衍生虛擬變量、離散化、降維。本篇文章將做展開介紹,並提供基於Python的代碼實現。</font>python

0. 示例數據集說明

/labcenter/python/dataset2.xlsx 算法

import pandas as pd
import numpy as np
# 讀取數據
dataset = pd.read_excel("/labcenter/python/dataset2.xlsx")
# 打印數據集
dataset
Out[58]: 
   col1  col2 col3  col4                col5   col6  col7
0   101    96  aaa  3.85 2017-04-05 17:39:08   0-10   800
1   102    13  bbb  2.78 2017-04-04 03:00:14  10-20  1000
2   103   160  aaa  4.40 2017-04-03 14:45:29  10-20   600
3   104   128  ccc  2.49 2017-04-22 11:17:12  20-30  2400
4   105    10  ccc  3.70 2017-04-22 16:42:08  30-40  1300
5   106    16  bbb  2.78 2017-04-19 12:26:58   0-10  1500
6   107   -31  aaa  3.34 2017-04-04 12:50:28  40-50   700
7   108    87  ccc  5.69 2017-04-13 10:15:24  20-30  1200
8   109  -221  bbb  3.35 2017-04-28 13:32:30  30-40  1900
9   110   115  aaa  5.10 2017-04-24 22:28:55  10-20  2000

<br> ## 1. 簡單函數變換 **簡單函數變換**是指對原始數據直接使用某些數學函數進行轉換,主要用於**將不具備正態分佈的數據變換成具備正態分佈**,同時也能夠用於**對數據進行壓縮**,好比$10^8和10^9$更關注的是相對差距而不是絕對差距,能夠經過取對數變換實現。 經常使用的函數包括:$log(x)、x^k、e^x、\frac {1}{x}、\sqrt{x}、sinx$等。 簡單函數變換會改變的原始數據的分佈特徵,所以使用前必須深刻了解數據特徵變化是否會影響到後續的分析。dom

# 簡單函數變換
## 取10爲底的對數    
np.log10(dataset['col2'])
Out[72]: 
0    1.982271
1    1.113943
2    2.204120
3    2.107210
4    1.000000
5    1.204120
6         NaN
7    1.939519
8         NaN
9    2.060698
Name: col2, dtype: float64

## 取e爲的底的指數
np.exp(dataset['col2'])
Out[73]: 
0    4.923458e+41
1    4.424134e+05
2    3.069850e+69
3    3.887708e+55
4    2.202647e+04
5    8.886111e+06
6    3.442477e-14
7    6.076030e+37
8    1.049348e-96
9    8.787502e+49
Name: col2, dtype: float64

## 取倒數
1 / dataset['col2']     
Out[74]: 
0    0.010417
1    0.076923
2    0.006250
3    0.007812
4    0.100000
5    0.062500
6   -0.032258
7    0.011494
8   -0.004525
9    0.008696
Name: col2, dtype: float64

## 開方
np.sqrt(dataset['col2'])
Out[75]: 
0     9.797959
1     3.605551
2    12.649111
3    11.313708
4     3.162278
5     4.000000
6          NaN
7     9.327379
8          NaN
9    10.723805
Name: col2, dtype: float64

## 取正弦
np.sin(dataset['col2'])
Out[76]: 
0    0.983588
1    0.420167
2    0.219425
3    0.721038
4   -0.544021
5   -0.287903
6    0.404038
7   -0.821818
8   -0.885939
9    0.945435
Name: col2, dtype: float64

<br> ## 2. 標準化 標準化,是爲了處理**不一樣規模和量綱**的數據,使其縮放到相同的數據區間和範圍,以減小規模、量綱、分佈差別等對分析建模的影響。**經常使用的標準化方法有如下三種**: ###2.1 離差標準化 離差標準化,又稱**最大值最小值標準化(Max-Min)**,即基於原始數據的最大值、最小值對數據進行線性變換,**變換後,數據徹底落入[0,1]區間內**。 **公式:** $$x'= \frac {(x-min)}{(max-min)}$$ 其中,原始數據x,其最大值、最小值分別爲max、min,轉換後數據爲x'。 **優勢:**可以將數據歸一化,同時能較好的保持原始數據的分佈結構; **缺點:**容易受極端值的影響,極端值會使大部分數據接近於0而且差距很小,同時在出現最值範圍之外的數據時變換結果會產生錯誤; **適用場景**:適合數據比較集中的狀況。 ###2.2 標準差標準化 標準差標準化,即**Z-Score標準化**,即基於原始數據的均值和標準差對數據進行標準化,**標準化後,數據呈正態分佈**。 **公式:** $$x'= \frac {(x-μ)}{σ}$$ 其中,原始數據x,其均值、標準差分別爲μ、σ,轉換後數據爲x'。 **缺點:**是一種中心化方法,會改變原始數據的分佈結構。 **適用場景**:適合數據的最值未知,且可能出現離羣點的狀況。 ###2.3 絕對值最大標準化 絕對值最大標準化,即**MaxAbs標準化**,即基於原始數據絕對值的最大值對數據進行標準化,**變換後,數據徹底落入[-1,1]區間內**。 **公式:** $$x'= \frac {x}{maxAbs}$$ 其中,原始數據x,其絕對值的最大值爲maxAbs,轉換後數據爲x'。 **優勢:**不會破壞原始數據的分佈結構; **適用場景:**可用於稀疏矩陣、稀疏數據。機器學習

# 標準化處理
## 方法1:使用numpy
### 離差標準化
(dataset['col2'] - dataset['col2'].min()) / (dataset['col2'].max() - dataset['col2'].min())
Out[77]: 
0    0.832021
1    0.614173
2    1.000000
3    0.916010
4    0.606299
5    0.622047
6    0.498688
7    0.808399
8    0.000000
9    0.881890
Name: col2, dtype: float64

### 標準差標準化
(dataset['col2'] - dataset['col2'].mean()) / dataset['col2'].std()
Out[78]: 
0    0.534846
1   -0.221410
2    1.117982
3    0.826414
4   -0.248744
5   -0.194075
6   -0.622316
7    0.452842
8   -2.353503
9    0.707964
Name: col2, dtype: float64

### 絕對值最大標準化
dataset['col2'] / np.abs(dataset['col2']).max()
Out[79]: 
0    0.434389
1    0.058824
2    0.723982
3    0.579186
4    0.045249
5    0.072398
6   -0.140271
7    0.393665
8   -1.000000
9    0.520362
Name: col2, dtype: float64

## 方法2:使用sklearn
from sklearn import preprocessing
### 離差標準化
minmax_scaler = preprocessing.MinMaxScaler()
minmax_scaler.fit_transform(dataset['col2'])
Out[81]: 
array([ 0.832021  ,  0.61417323,  1.        ,  0.9160105 ,  0.60629921,
        0.62204724,  0.49868766,  0.80839895,  0.        ,  0.88188976])

### 標準差標準化
zsocre_scaler = preprocessing.StandardScaler()
zsocre_scaler.fit_transform(dataset['col2'])
Out[82]: 
array([ 0.56377684, -0.23338633,  1.17845688,  0.87111686, -0.26219945,
       -0.2045732 , -0.65597885,  0.47733746, -2.4808102 ,  0.74625998])

### 絕對值最大標準化
maxabs_scaler = preprocessing.MaxAbsScaler()
maxabs_scaler.fit_transform(dataset['col2'])
Out[83]: 
array([ 0.43438914,  0.05882353,  0.7239819 ,  0.57918552,  0.04524887,
        0.07239819, -0.14027149,  0.39366516, -1.        ,  0.52036199])

<br> ## 3. 衍生虛擬變量 在數據建模過程當中,不少算法都不能處理非數值型數據,必須首先將這些數據轉化爲數值型。可是,即便轉化爲數值型數據,也不能直接應用到算法計算中,爲何?這須要從離散型數據的分類提及。 **離散型數據也就是分類數據,主要分爲兩類,一類是無序分類,一類是有序分類。** **無序分類**,是指各個類別之間沒有明顯的高、低、大、小等包含等級、順序、優劣、好壞等邏輯的劃分,只是用來區分兩個或多個具備相同或至關價值的屬性。例如性別的男和女,顏色的紅、黃、藍等等。 **有序分類**,是指各個類別之間有必定的順序關係。例如用戶價值的高、中、低,學歷的博士、碩士、學士等。 對於無序分類來講,不管用什麼數值來表示都沒法表達出價值相等但有區分的屬性,好比性別的男和女,若是分別用1和2表示,那麼1和2自己就已經帶有距離爲1的差別,但實際上兩者之間是沒有這種差別的,一樣,不論用任何數據都沒法到達這種區分的目的。 而對於有序分類來講,不管用什麼有序的數字序列都沒法準確表達出有序類別之間的差別性,好比學歷的博士、碩士、學士,若是用3-2-1來表示這種順序關係,那這種差別爲何不能用30-20-10來表示呢? 因此,非數值型的離散型數據要想參與到算法計算中,不能簡單的認爲轉化成用數值就表示就能夠,而是必須使用其餘的方法,這種方法就是**衍生虛擬變量**,也叫作**建立虛擬變量(dummy variable)、建立啞變量、建立名義變量、one-hot編碼(one-hot encoding)、N取一編碼(one-out-of encoding)**。它是指,將一個離散型變量衍生出多個真值變量(用0和1,或者True和False表示的變量),好比性別這一變量取值有男、女兩個,那麼將衍生出性別是否男、性別是否女兩個變量;而後使用這些衍生出來的真值變量替換原始變量參與後續的計算。 ![](https://images2018.cnblogs.com/blog/554583/201805/554583-20180511233627044-2082116409.png)函數

# 衍生虛擬變量
## 方法1:自定義函數
def dummyCreate(ser):
    valueSet = ser.unique()
    resDf = pd.DataFrame()
    colName = ser.name
    for value in valueSet:
        colNameNew = colName + '_' + value
        colDataTmp = ser.values
        colData = (colDataTmp == value)
        resDf[colNameNew] = colData
    return resDf

dummyCreate(dataset['col3'])
Out[85]: 
  col3_aaa col3_bbb col3_ccc
0     True    False    False
1    False     True    False
2     True    False    False
3    False    False     True
4    False    False     True
5    False     True    False
6     True    False    False
7    False    False     True
8    False     True    False
9     True    False    False

## 方法2:使用sklearn
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import LabelEncoder
### 標準標籤化
labelEnc = LabelEncoder()   
### one-hot編碼
ohEnc = OneHotEncoder()   
### 將類別值用0.1.2……編碼
dataset['new_col'] = labelEnc.fit_transform(dataset['col3'])   
### one-hot編碼
ohEncRes = ohEnc.fit_transform(dataset['new_col'].reshape(-1,1)).toarray()  
### 合併結果
newDf = pd.concat((dataset, pd.DataFrame(ohEncRes)), axis=1)    
### 重命名列名
dataset = newDf.rename(columns={0:'col3_aaa',1:'col3_bbb',2:'col3_ccc'})    
### 打印數據集
dataset
Out[87]: 
   col1  col2 col3  col4                col5   col6  col7  new_col  col3_aaa  col3_bbb  col3_ccc
0   101    96  aaa  3.85 2017-04-05 17:39:08   0-10   800        0       1.0       0.0       0.0
1   102    13  bbb  2.78 2017-04-04 03:00:14  10-20  1000        1       0.0       1.0       0.0
2   103   160  aaa  4.40 2017-04-03 14:45:29  10-20   600        0       1.0       0.0       0.0
3   104   128  ccc  2.49 2017-04-22 11:17:12  20-30  2400        2       0.0       0.0       1.0
4   105    10  ccc  3.70 2017-04-22 16:42:08  30-40  1300        2       0.0       0.0       1.0
5   106    16  bbb  2.78 2017-04-19 12:26:58   0-10  1500        1       0.0       1.0       0.0
6   107   -31  aaa  3.34 2017-04-04 12:50:28  40-50   700        0       1.0       0.0       0.0
7   108    87  ccc  5.69 2017-04-13 10:15:24  20-30  1200        2       0.0       0.0       1.0
8   109  -221  bbb  3.35 2017-04-28 13:32:30  30-40  1900        1       0.0       1.0       0.0
9   110   115  aaa  5.10 2017-04-24 22:28:55  10-20  2000        0       1.0       0.0       0.0

**注意:**建立虛擬變量和虛擬變量編碼(dummy encoding)是不一樣的概念,前者如上所述,若是一個離散變量有N個取值,就會衍生出N個真值變量,然後者——虛擬變量編碼,則是衍生出N-1個變量,它認爲這個N-1個變量都爲0時即表示原始數據取第N個值。 學習

<br> ## 4. 離散化 **離散化就是將一份數據從細粒度轉化爲粗粒度,實質就是數據的集中化。**主要包括**兩方面**:將連續型數據轉化成離散型數據、將離散型數據進行類別合併,具體又分爲如下幾點: ###4.1 針對時間數據的離散化 針對時間數據的離散化主要是指對時間數據進行粒度上的上卷(反之是下鑽): 時間戳——>小時——>上午\下午 日期型——>星期——>週數——>季度——>年份編碼

# 離散化
## 針對時間數據的離散化
colList = list(dataset.columns)
colList.extend(['weekday_col5','week_col5','year_col5','month_col5','day_col5','hour_col5'])
dataset = dataset.reindex(columns = colList)
for index,value in enumerate(dataset['col5']):
    newValue = pd.to_datetime(value)
    dataset['weekday_col5'][index] = newValue.weekday()
    dataset['week_col5'][index] = newValue.week
    dataset['year_col5'][index] = newValue.year
    dataset['month_col5'][index] = newValue.month
    dataset['day_col5'][index] = newValue.day
    dataset['hour_col5'][index] = newValue.hour
### 打印數據集
dataset[['col5','weekday_col5','week_col5','year_col5','month_col5','day_col5','hour_col5']]
Out[101]: 
                 col5  weekday_col5  week_col5  year_col5  month_col5  day_col5  hour_col5
0 2017-04-05 17:39:08           2.0       14.0     2017.0         4.0       5.0       17.0
1 2017-04-19 12:26:58           2.0       16.0     2017.0         4.0      19.0       12.0
2 2017-04-04 03:00:14           1.0       14.0     2017.0         4.0       4.0        3.0
3 2017-04-03 14:45:29           0.0       14.0     2017.0         4.0       3.0       14.0
4 2017-04-24 22:28:55           0.0       17.0     2017.0         4.0      24.0       22.0
5 2017-04-22 11:17:12           5.0       16.0     2017.0         4.0      22.0       11.0
6 2017-04-13 10:15:24           3.0       15.0     2017.0         4.0      13.0       10.0
7 2017-04-22 16:42:08           5.0       16.0     2017.0         4.0      22.0       16.0
8 2017-04-28 13:32:30           4.0       17.0     2017.0         4.0      28.0       13.0
9 2017-04-04 12:50:28           1.0       14.0     2017.0         4.0       4.0       12.0

###4.2 針對類別數據的離散化 針對類別數據的離散化主要是指將多個類別進行合併而產生新的類別劃分。 好比將年齡區間(0-10,10-20,20-30,30-40,40-50)合併爲年齡區間(0-20,20-30,30-50)。spa

## 針對類別數據的離散化
mapDf = pd.DataFrame([['0-10','0-20'],['10-20','0-20'],['20-30','20-30'],['30-40','30-50'],['40-50','30-50']],columns=['col6','new_col2'])
dataset = dataset.merge(mapDf, left_on = 'col6', right_on = 'col6', how = 'inner')
### 打印
dataset[['col6','new_col2']]
Out[102]: 
    col6 new_col2
0   0-10     0-20
1   0-10     0-20
2  10-20     0-20
3  10-20     0-20
4  10-20     0-20
5  20-30    20-30
6  20-30    20-30
7  30-40    30-50
8  30-40    30-50
9  40-50    30-50

###4.3 針對連續數據的離散化 離散化中主要的工做是針對連續數據的離散化,包括兩方面:將連續數據分紅多個區間、將連續數據劃分紅特定的類。前者一般被稱爲分箱。具體有如下幾種實現方法: ####(1)自定義分箱 自定義分箱,是指根據業務經驗或者常識等自行設定劃分的區間,而後將原始數據歸類到各個區間中。excel

####(2)等寬分箱 等寬分箱,是指劃分的各個區間的寬度(或稱爲距離)相等,也稱爲等深分箱code

####(3)等頻分箱 等頻分箱,是指劃分的各個區間包含的數據點的個數相等,也稱爲分位數分箱

####(4)聚類法 聚類法,是指使用聚類算法自動將原始數據分紅多個類別。

####(5)二值化 二值化,是指設置一個閾值,將每一個數據點與這個閾值進行比較,大於(或等於)閾值取某一固定值A,小於(或等於)閾值則取另外一固定值B,從而將原始數據轉換爲兩個取值的離散數據。

## 針對連續數據的離散化
### (1)自定義分箱
#### 定義邊界點
points = [0,800,1500,2000,2500]
#### 未設置標籤
pd.cut(dataset['col7'], points)
Out[91]: 
0        (0, 800]
1     (800, 1500]
2     (800, 1500]
3        (0, 800]
4    (1500, 2000]
5    (2000, 2500]
6     (800, 1500]
7     (800, 1500]
8    (1500, 2000]
9        (0, 800]
Name: col7, dtype: category
Categories (4, object): [(0, 800] < (800, 1500] < (1500, 2000] < (2000, 2500]]
#### 設置標籤
pd.cut(dataset['col7'], points, labels = ['a','b','c','d'])
Out[92]: 
0    a
1    b
2    b
3    a
4    c
5    d
6    b
7    b
8    c
9    a
Name: col7, dtype: category
Categories (4, object): [a < b < c < d]

### (2)等寬分箱
#### 箱數
bins = 4   
#### 未設置標籤
pd.cut(dataset['col7'], bins)
Out[94]: 
0    (598.2, 1050]
1     (1050, 1500]
2    (598.2, 1050]
3    (598.2, 1050]
4     (1950, 2400]
5     (1950, 2400]
6     (1050, 1500]
7     (1050, 1500]
8     (1500, 1950]
9    (598.2, 1050]
Name: col7, dtype: category
Categories (4, object): [(598.2, 1050] < (1050, 1500] < (1500, 1950] < (1950, 2400]]
#### 設置標籤
pd.cut(dataset['col7'], bins, labels = ['a','b','c','d'])
Out[95]: 
0    a
1    b
2    a
3    a
4    d
5    d
6    b
7    b
8    c
9    a
Name: col7, dtype: category
Categories (4, object): [a < b < c < d]

### (3)等頻分箱
#### 箱數
bins = 4   
#### 未設置標籤
pd.qcut(dataset['col7'], bins)
Out[97]: 
0      [600, 850]
1    (1250, 1800]
2     (850, 1250]
3      [600, 850]
4    (1800, 2400]
5    (1800, 2400]
6     (850, 1250]
7    (1250, 1800]
8    (1800, 2400]
9      [600, 850]
Name: col7, dtype: category
Categories (4, object): [[600, 850] < (850, 1250] < (1250, 1800] < (1800, 2400]]
#### 設置標籤
pd.qcut(dataset['col7'], bins, labels = ['a','b','c','d'])
Out[98]: 
0    a
1    c
2    b
3    a
4    d
5    d
6    b
7    c
8    d
9    a
Name: col7, dtype: category
Categories (4, object): [a < b < c < d]

### (4)聚類法
from sklearn.cluster import KMeans
data = dataset['col7']
new_data = data.reshape((data.shape[0], 1))
#### 使用K-Means聚類
kmeans = KMeans(n_clusters=4, random_state=0)  
keames_result = kmeans.fit_predict(new_data)
keames_result
Out[99]: array([1, 2, 1, 1, 3, 0, 2, 2, 3, 1], dtype=int32)

### (5)二值法
from sklearn import preprocessing
#### 使用均值做爲分割閾值
binarizer_scaler = preprocessing.Binarizer(threshold = dataset['col7'].mean())  
temp = binarizer_scaler.fit_transform(dataset['col7'])
temp.resize(dataset['col7'].shape)
temp
Out[100]: array([0, 1, 0, 0, 1, 1, 0, 0, 1, 0])

<br> ## 5. 降維 **降維**是指下降數據集的維度數量,從而下降模型的計算工做量,減小模型的運行時間,減弱噪聲變量(無關變量)對模型結果可能產生的影響。

###5.1 基於特徵選擇的降維 基於特徵選擇的降維,通俗的說,就是特徵篩選,或者叫變量篩選。從多個變量中篩選出更有用的較少個變量,從而下降數據集的維度。特徵篩選的主要方法包括:基於經驗的方法(好比專家法)、基於統計的方法(好比區分度、信息增益)和基於機器學習的方法(好比決策樹算法)。 基於特徵選擇的降維的好處是,既保留了原有維度的特徵,同時又完成了降維的目的。 因爲特徵篩選是數據準備工做的一個重要環節,所以會單獨拿出來進行總結論述,此處不作詳細討論。

###5.2 基於維度轉換的降維 基於維度轉換的降維,是指按照必定的數學變換方法,把給定的一組(相關的)變量,經過數學模型從高維空間映射到低維度空間中,而後利用映射後產生的新變量來表示原有變量的整體特徵,轉換後產生的新特徵是多個原始特徵的綜合。 具體的方法分爲線性降維和非線性降維,經常使用的算法有主成分分析、因子分析、線性判別分析等。 關於具體的算法原理,後續將在統計學或機器學習系列中進行總結論述,此處不作討論。 基於維度轉換的降維將使用轉換後產生的新變量參與後續的建模分析,所以直接影響到模型的可解釋性和可理解性,在不要求對模型進行解釋說明時可使用,不然,建議使用基於特徵選擇的降維。

<br> ## 6. 參考與感謝 \[1] [數據挖掘概念與技術](https://book.douban.com/subject/2038599/) \[2] [數據挖掘導論](https://book.douban.com/subject/5377669/) \[3] [Python機器學習基礎教程](https://book.douban.com/subject/30147778/) \[4] [Python數據分析與數據化運營](https://book.douban.com/subject/27608466/) \[5] [Python數據分析與數據挖掘實戰](https://book.douban.com/subject/26677686/) <br> <br>

相關文章
相關標籤/搜索