一.概述
1. 數據預處理
數據預處理是從數據中檢測,修改或刪除不准確或不適用於模型的記錄的過程
可能面對的問題有:數據類型不同,比如有的是文字,有的是數字,有的含時間序列,有的連續,有的間斷。 也可能,數據的質量不行,有噪聲,有異常,有缺失,數據出錯,量綱不一,有重復,數據是偏態,數據量太大或太小 。
數據預處理的目的:讓數據適應模型,匹配模型的需求 。
2.sklearn中的數據預處理
sklearn中包含眾多數據預處理相關的模塊,包括:
- 模塊preprocessing:幾乎包含數據預處理的所有內容
- 模塊Impute:填補缺失值專用
二 . 數據預處理
1.歸一化
在機器學習算法實踐中,我們往往有着將不同規格的數據轉換到同一規格,或不同分布的數據轉換到某個特定分布 的需求,這種需求統稱為將數據“無量綱化”。
譬如梯度和矩陣為核心的算法中,譬如邏輯回歸,支持向量機,神經 網絡,無量綱化可以加快求解速度;而在距離類模型,譬如K近鄰,K-Means聚類中,無量綱化可以幫我們提升模型精度,避免某一個取值范圍特別大的特征對距離計算造成影響。
(1)preprocessing.MinMaxScaler
當數據(x)按照最小值中心化后,再按極差(最大值 - 最小值)縮放,數據移動了最小值個單位,並且會被收斂到 [0,1]之間,而這個過程,就叫做數據歸一化(Normalization,又稱Min-Max Scaling)。
歸一化之后的數據服從正態分 布,公式如下:
在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),公式如下:
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.]])