【機器學習】scikit-learn中的數據預處理小結(歸一化、缺失值填充、離散特征編碼、連續值分箱)


一.概述

1. 數據預處理

數據預處理是從數據中檢測,修改或刪除不准確或不適用於模型的記錄的過程
可能面對的問題有:數據類型不同,比如有的是文字,有的是數字,有的含時間序列,有的連續,有的間斷。 也可能,數據的質量不行,有噪聲,有異常,有缺失,數據出錯,量綱不一,有重復,數據是偏態,數據量太大或太小 。
數據預處理的目的:讓數據適應模型,匹配模型的需求 。

2.sklearn中的數據預處理

sklearn中包含眾多數據預處理相關的模塊,包括:

  • 模塊preprocessing:幾乎包含數據預處理的所有內容
  • 模塊Impute:填補缺失值專用

二 . 數據預處理

1.歸一化

在機器學習算法實踐中,我們往往有着將不同規格的數據轉換到同一規格,或不同分布的數據轉換到某個特定分布 的需求,這種需求統稱為將數據“無量綱化”。
譬如梯度和矩陣為核心的算法中,譬如邏輯回歸,支持向量機,神經 網絡,無量綱化可以加快求解速度;而在距離類模型,譬如K近鄰,K-Means聚類中,無量綱化可以幫我們提升模型精度,避免某一個取值范圍特別大的特征對距離計算造成影響。

(1)preprocessing.MinMaxScaler

當數據(x)按照最小值中心化后,再按極差(最大值 - 最小值)縮放,數據移動了最小值個單位,並且會被收斂到 [0,1]之間,而這個過程,就叫做數據歸一化(Normalization,又稱Min-Max Scaling)。
歸一化之后的數據服從正態分 布,公式如下:

\[x^{*} = \frac{x-min(x)}{max(x)-min(x)} \]

在sklearn當中,我們使用preprocessing.MinMaxScaler來實現這個功能。

  • MinMaxScaler有一個重要參數, feature_range,控制我們希望把數據壓縮到的范圍,默認是[0,1].
from sklearn.preprocessing import MinMaxScaler
 
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
#實現歸一化 
scaler = MinMaxScaler(feature_range=[5,10])         #實例化,feature_range默認是[0,1]
# scaler = scaler.fit(data)                         #fit,在這里本質是生成min(x)和max(x) 
# result = scaler.transform(data)                   #通過接口導出結果 result
result_ = scaler.fit_transform(data)                #訓練和導出結果一步達成,可以替代上兩行數據
result_
array([[ 5.  ,  5.  ],
       [ 6.25,  6.25],
       [ 7.5 ,  7.5 ],
       [10.  , 10.  ]])

(2) preprocessing.StandardScale

當數據(x)按均值(μ)中心化后,再按標准差(σ)縮放,數據就會服從為均值為0,方差為1的正態分布(即標准正態分 布),而這個過程,就叫做數據標准化(Standardization,又稱Z-score normalization),公式如下:

\[x^{*} = \frac{x-μ}{σ} \]

from sklearn.preprocessing import StandardScaler 
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
 
scaler = StandardScaler()                           #實例化 
# scaler = scaler.fit(data)                                    #fit,本質是生成均值和方差
# result = scaler.transform(data)                      #通過接口導出結果
 
result_ = scaler.fit_transform(data)                #使用fit_transform(data)一步達成結果
result_
array([[-1.18321596, -1.18321596],
       [-0.50709255, -0.50709255],
       [ 0.16903085,  0.16903085],
       [ 1.52127766,  1.52127766]])

(3) StandardScaler和MinMaxScaler選哪個?

看情況。大多數機器學習算法中,會選擇StandardScaler來進行特征縮放,因為MinMaxScaler對異常值非常敏 感。在PCA,聚類,邏輯回歸,支持向量機,神經網絡這些算法中,StandardScaler往往是最好的選擇。
MinMaxScaler在不涉及距離度量、梯度、協方差計算以及數據需要被壓縮到特定區間時使用廣泛,比如數字圖像 處理中量化像素強度時,都會使用MinMaxScaler將數據壓縮於[0,1]區間之中。

2.缺失值

數據預處理中非常重要的一項就是處理缺失值。

  • 對於連續值,常常使用均值或者中位數去填充缺失值;
  • 對於離散值,常常使用眾數填充,或者把缺失值當作一個單獨的類別。
  • 對於缺失率超過一定閾值的特征,可以考慮直接舍棄。
import pandas as pd 
data = pd.read_csv(r"./../Narrativedata.csv",index_col=0)
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 0 to 890
Data columns (total 4 columns):
Age         714 non-null float64
Sex         891 non-null object
Embarked    889 non-null object
Survived    891 non-null object
dtypes: float64(1), object(3)
memory usage: 34.8+ KB

以Titanic數據為例,可以看到其中Age和Embarked兩列有缺失值,且分別為連續值和離散值。

(1) impute.SimpleImputer

from sklearn.impute import SimpleImputer 
# 對連續值Age進行填充    
Age = data.loc[:,"Age"].values.reshape(-1,1)            #sklearn當中特征矩陣必須是二維 
# 實例化
age_median = SimpleImputer(strategy="median")           #用中位數填補 (默認均值填補) 
age_0 = SimpleImputer(strategy="constant",fill_value=0) #使用具體值填補,用fill_value指定
# 轉化
age_median = age_median.fit_transform(Age)    #fit_transform一步完成調取結果 
age_0 = age_0.fit_transform(Age)
#把填補后的列賦值回原來的矩陣里
data.loc[:,"Age"] = age_median
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 0 to 890
Data columns (total 4 columns):
Age         891 non-null float64
Sex         891 non-null object
Embarked    889 non-null object
Survived    891 non-null object
dtypes: float64(1), object(3)
memory usage: 34.8+ KB
# 對離散值Embarked進行填充 
Embarked = data.loc[:,"Embarked"].values.reshape(-1,1) 
Embarked_most = SimpleImputer(strategy = "most_frequent")   #使用眾數填補
data.loc[:,"Embarked"] = Embarked_most.fit_transform(Embarked)
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 0 to 890
Data columns (total 4 columns):
Age         891 non-null float64
Sex         891 non-null object
Embarked    891 non-null object
Survived    891 non-null object
dtypes: float64(1), object(3)
memory usage: 34.8+ KB

(2) 用Pandas和Numpy進行填補

  • pd.fillna() 使用特定的值填充
  • pd.dropna(axis=0)刪除所有有缺失值的行/列
import pandas as pd 
data1 = pd.read_csv(r"./../Narrativedata.csv",index_col=0)
 
data1.loc[:,"Age"] = data1.loc[:,"Age"].fillna(data1.loc[:,"Age"].median()) #.fillna 在data1Frame里面直接進行填補
 
data1.dropna(axis=0,inplace=True) #.dropna(axis=0)刪除所有有缺失值的行,.dropna(axis=1)刪除所有有缺失值的列
data1.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 889 entries, 0 to 890
Data columns (total 4 columns):
Age         889 non-null float64
Sex         889 non-null object
Embarked    889 non-null object
Survived    889 non-null object
dtypes: float64(1), object(3)
memory usage: 34.7+ KB

3.處理離散特征:編碼和啞變量

在sklearn當中,除了專用來處理文字的算法,其他算法在fit的時候全部要求輸入數組或矩陣,也不能夠導 入文字型數據。
為了讓數據適 應算法和庫,我們必須將數據進行編碼,即是說,將文字型數據轉換為數值型。

(1) preprocessing.LabelEncoder:標簽專用,能夠將分類轉換為分類數值

from sklearn.preprocessing import LabelEncoder 
y = data.iloc[:,-1]                         #要輸入的是標簽,不是特征矩陣,所以允許一維
print(y[:10])
le = LabelEncoder()                         #實例化 
label = le.fit_transform(y)                 # 匹配加轉化一步到位
# le = le.fit(y)                            
# label = le.transform(y)                  
print(label[:10])
print(le.classes_)                           # 查看有多少類
data.iloc[:,-1] =label
0     No
1    Yes
2    Yes
3    Yes
4     No
5     No
6     No
7     No
8    Yes
9    Yes
Name: Survived, dtype: object
[0 2 2 2 0 0 0 0 2 2]
['No' 'Unknown' 'Yes']

(2)preprocessing.OrdinalEncoder:特征專用,能夠將分類特征轉換為分類數 (一般用於有序變量)

data_ =data.copy()
# 觀察特征的原樣式
data_.iloc[:,1:-1][:10]
Sex Embarked
0 male S
1 female C
2 female S
3 female S
4 male S
5 male Q
6 male S
7 male S
8 female S
9 female C
from sklearn.preprocessing import OrdinalEncoder
# 類似的,把實例化,匹配,轉化和賦值寫在一條語句上
data_.iloc[:,1:-1] = OrdinalEncoder().fit_transform(data_.iloc[:,1:-1])
#接口categories_對應LabelEncoder的接口classes_,一模一樣的功能 
print(OrdinalEncoder().fit(data_.iloc[:,1:-1]).categories_)
#查看數據,
data_.head() 
[array([0., 1.]), array([0., 1., 2.])]
Age Sex Embarked Survived
0 22.0 1.0 2.0 0
1 38.0 0.0 0.0 2
2 26.0 0.0 2.0 2
3 35.0 0.0 2.0 2
4 35.0 1.0 2.0 0
data_.info() 
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 0 to 890
Data columns (total 4 columns):
Age         891 non-null float64
Sex         891 non-null float64
Embarked    891 non-null float64
Survived    891 non-null int32
dtypes: float64(3), int32(1)
memory usage: 31.3 KB

由以上的數據情況可以看出,Age和Embarked的缺失值已被填充,特征Sex和Embarked等分類特征已轉化為分類數值,標簽Survived也已轉化為分類數值。

(3) preprocessing.OneHotEncoder:獨熱編碼,創建啞變量

由上面的處理可以看到OrdinalEncoder會把類別變量變成數值,比如轉化為[0,1,2],這三個數字在算法看來,是連續且可以計算的,可能有大小並且有着可以相加相乘的聯系。但是有的類別變量各類別是獨立的,如果轉化成數值會給算法傳達了一些不准確的信息,而這會影響我們的建模。

因此,類別OrdinalEncoder可以用來處理有序變量,但對於名義變量,我們只有使用獨熱編碼,以啞變量的方式來處理,才能夠盡量 向算法傳達最准確的信息。

# 同樣的,觀察下原數據樣式
data.iloc[:,1:-1][:10]
Sex Embarked
0 male S
1 female C
2 female S
3 female S
4 male S
5 male Q
6 male S
7 male S
8 female S
9 female C
from sklearn.preprocessing import OneHotEncoder 
X = data.iloc[:,1:-1]
 
#可以直接一步到位
result = OneHotEncoder(categories='auto').fit_transform(X).toarray()
# # 也可以分開成兩步計算
# enc = OneHotEncoder(categories='auto').fit(X) 
# result = enc.transform(X).toarray() 
# 把獨熱編碼拼接到原數據上
newdata = pd.concat([data,pd.DataFrame(result)],axis=1)
newdata.drop(["Sex","Embarked"],axis=1,inplace=True) 
newdata.columns = ["Age","Survived","Female","Male","Embarked_C","Embarked_Q","Embarked_S"]
newdata.head() 
Age Survived Female Male Embarked_C Embarked_Q Embarked_S
0 22.0 0 0.0 1.0 0.0 0.0 1.0
1 38.0 2 1.0 0.0 1.0 0.0 0.0
2 26.0 2 1.0 0.0 0.0 0.0 1.0
3 35.0 2 1.0 0.0 0.0 0.0 1.0
4 35.0 0 0.0 1.0 0.0 0.0 1.0

(4) 使用pandas的.get_dummies函數 實現獨熱編碼

dummies_Sex = pd.get_dummies(data.Sex,prefix='Sex')                    #ptefix 是前綴,因子化之后的字段名為 前綴_類名
dummies_Embarked = pd.get_dummies(data.Embarked,prefix='Embarked')    
newdata = pd.concat([data, dummies_Sex, dummies_Embarked], axis=1)         #將啞編碼的內容拼接到data后
newdata.drop([ 'Sex', 'Embarked'], axis=1, inplace=True)                # 把編碼前的字段刪除
newdata.head() 
Age Survived Sex_female Sex_male Embarked_C Embarked_Q Embarked_S
0 22.0 0 0 1 0 0 1
1 38.0 2 1 0 1 0 0
2 26.0 2 1 0 0 0 1
3 35.0 2 1 0 0 0 1
4 35.0 0 0 1 0 0 1

4.處理連續特征:二值化和分段

(1) sklearn.preprocessing.Binarize 用於處理連續變量的二值化

根據閾值將數據二值化(將特征值設置為0或1),用於處理連續型變量。大於閾值的值映射為1,而小於或等於閾值的值映射為0。默認閾值為0。

# 將年齡二值化
data_2 = data.copy()
from sklearn.preprocessing import Binarizer 
X = data_2.iloc[:,0].values.reshape(-1,1)               #類為特征專用,所以不能使用一維數組 
Age = Binarizer(threshold=30).fit_transform(X)          # 以30為閾值,>30為1,<30為0
Age[:10]
array([[0.],
       [1.],
       [0.],
       [1.],
       [1.],
       [0.],
       [1.],
       [0.],
       [0.],
       [0.]])

(2) sklearn.preprocessing.KBinsDiscretizer 用於給連續變量分箱

這是將連續型變量划分為分類變量的類,能夠將連續型變量排序后按順序分箱后編碼。總共包含三個重要參數:

from sklearn.preprocessing import KBinsDiscretizer
 
X = data.iloc[:,0].values.reshape(-1,1) 
est = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='uniform')  # 分箱后編碼成分類數值
est.fit_transform(X)[:5]
array([[0.],
       [1.],
       [0.],
       [1.],
       [1.]])
est = KBinsDiscretizer(n_bins=3, encode='onehot', strategy='uniform') #查看轉換后分的箱:變成了啞變量 
est.fit_transform(X).toarray()
array([[1., 0., 0.],
       [0., 1., 0.],
       [1., 0., 0.],
       ...,
       [0., 1., 0.],
       [1., 0., 0.],
       [0., 1., 0.]])


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM