08-05 細分構建機器學習應用程式的流程-資料預處理
- 細分構建機器學習應用程式的流程-資料預處理
- 一、1.1 缺失值處理
- 二、1.2 異常值處理
- 三、1.3 自定義資料型別編碼
- 四、1.4 通過sklearn對資料型別編碼
- 五、1.5 獨熱編碼
- 六、1.6 資料標準化
- 七、1.7 二值化資料
- 八、1.8 正則化資料
- 九、1.9 生成多項式特徵
更新、更全的《機器學習》的更新網站,更有python、go、資料結構與演算法、爬蟲、人工智慧教學等著你:
細分構建機器學習應用程式的流程-資料預處理
sklearn資料預處理官方文件地址:https://scikit-learn.org/stable/modules/classes.html#module-sklearn.preprocessing
一、1.1 缺失值處理
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from sklearn import datasets
%matplotlib inline
font = FontProperties(fname='/Library/Fonts/Heiti.ttc')
現實生活中的資料往往是不全面的,很多樣本的屬性值會有缺失,例如某個人填寫的個人資訊不完整或者對個人隱私的保護政策導致建模時可能無法得到所需要的特徵,尤其是在資料量較大時,這種缺失值的產生會對模型的效能造成很大的影響。接下來將通過鳶尾花資料討論缺失值處理的方法。
# 缺失值處理示例
from io import StringIO
iris_data = '''
5.1,,1.4,0.2
4.9,3.0,1.4,0.2
4.7,3.2,,0.2
7.0,3.2,4.7,1.4
6.4,3.2,4.5,1.5
6.9,3.1,4.9,
,,,
'''
iris = datasets.load_iris()
df = pd.read_csv(StringIO(iris_data), header=None)
df.columns = iris.feature_names
df = df.iloc[:, :4]
df
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | |
---|---|---|---|---|
0 | 5.1 | NaN | 1.4 | 0.2 |
1 | 4.9 | 3.0 | 1.4 | 0.2 |
2 | 4.7 | 3.2 | NaN | 0.2 |
3 | 7.0 | 3.2 | 4.7 | 1.4 |
4 | 6.4 | 3.2 | 4.5 | 1.5 |
5 | 6.9 | 3.1 | 4.9 | NaN |
6 | NaN | NaN | NaN | NaN |
1.1 1.1.1 刪除缺失值
# axis=0刪除有NaN值的行,axis=1刪除有NaN值的列
df.dropna(axis=0)
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | |
---|---|---|---|---|
1 | 4.9 | 3.0 | 1.4 | 0.2 |
3 | 7.0 | 3.2 | 4.7 | 1.4 |
4 | 6.4 | 3.2 | 4.5 | 1.5 |
# 刪除全為NaN值得行或列
df.dropna(how='all')
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | |
---|---|---|---|---|
0 | 5.1 | NaN | 1.4 | 0.2 |
1 | 4.9 | 3.0 | 1.4 | 0.2 |
2 | 4.7 | 3.2 | NaN | 0.2 |
3 | 7.0 | 3.2 | 4.7 | 1.4 |
4 | 6.4 | 3.2 | 4.5 | 1.5 |
5 | 6.9 | 3.1 | 4.9 | NaN |
# 刪除行不為4個值的
df.dropna(thresh=4)
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | |
---|---|---|---|---|
1 | 4.9 | 3.0 | 1.4 | 0.2 |
3 | 7.0 | 3.2 | 4.7 | 1.4 |
4 | 6.4 | 3.2 | 4.5 | 1.5 |
# 刪除花萼長度中有NaN值的資料
df.dropna(subset=['sepal length (cm)'])
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | |
---|---|---|---|---|
0 | 5.1 | NaN | 1.4 | 0.2 |
1 | 4.9 | 3.0 | 1.4 | 0.2 |
2 | 4.7 | 3.2 | NaN | 0.2 |
3 | 7.0 | 3.2 | 4.7 | 1.4 |
4 | 6.4 | 3.2 | 4.5 | 1.5 |
5 | 6.9 | 3.1 | 4.9 | NaN |
1.1.1 4.6.1.2 填充缺失值
# 填充缺失值示例
from sklearn.impute import SimpleImputer
# 對所有缺失值填充固定值0
# imputer = SimpleImputer(missing_values=np.nan, strategy='constant', fill_value=0)
# 中位數strategy=median,眾數strategy=most_frequent
imputer = SimpleImputer(missing_values=np.nan, strategy='mean')
imputer = imputer.fit_transform(df.values)
df = pd.DataFrame(imputer, columns=iris.feature_names)
df
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | |
---|---|---|---|---|
0 | 5.1 | 0.0 | 1.4 | 0.2 |
1 | 4.9 | 3.0 | 1.4 | 0.2 |
2 | 4.7 | 3.2 | 0.0 | 0.2 |
3 | 7.0 | 3.2 | 4.7 | 1.4 |
4 | 6.4 | 3.2 | 4.5 | 1.5 |
5 | 6.9 | 3.1 | 4.9 | 0.0 |
6 | 0.0 | 0.0 | 0.0 | 0.0 |
二、1.2 異常值處理
異常值有時候也會被稱為離群值,處理異常值的方式類似於缺失值處理,可以暴力刪除,也可以填充。但是異常值往往需要手動獲取,一般獲取異常值的方式有以下兩種:
-
統計分析原則
對特徵值統計後分析判斷哪些值是不符合邏輯的,拿年齡舉例,如果發現某個人的年齡是200,至少目前是不合理的,因此可以設定一個條件,把年齡大於200的資料都排除掉。
-
概率分佈原則
根據高斯分佈可知距離平均值之外的概率為0.003,這在統計學中屬於極小概率事件,因此可以把超過該距離的值當作異常值處理。當然,你也可以手動設定這個距離或概率,具體問題具體分析。
# 標準高斯分佈曲線
fig, ax = plt.subplots()
np.random.seed(1)
# 建立一組平均數為0,標準差δ為1,總個數為100000的符合標準正態分佈的資料
Gaussian = np.random.normal(0, 1, 100000)
ax.hist(Gaussian, bins=100, histtype="stepfilled",
density=True, alpha=0.5, color='r')
plt.xticks(np.arange(3, 4))
plt.title('標準高斯分佈曲線', fontproperties=font, fontsize=20)
plt.show()
三、1.3 自定義資料型別編碼
現有一個汽車樣本集,通過這個汽車樣本集可以判斷人們是否會購買該汽車。但是這個樣本集的特徵值是離散型的,為了確保計算機能正確讀取該離散值的特徵,需要給這些特徵做編碼處理,即建立一個對映表。如果特徵值分類較少,可以選擇自定義一個字典存放特徵值與自定義值的關係。接下來將直接通過scikit-learn庫轉換屬性值型別。
# 汽車樣本
car_data = '''
乘坐人數,後備箱大小,安全性,是否可以接受
4,med,high,acc
2,big,low,unacc
4,big,med,acc
4,big,high,acc
6,small,low,unacc
6,small,med,unacc
'''
df = pd.read_csv(StringIO(car_data), header=0)
df
乘坐人數 | 後備箱大小 | 安全性 | 是否可以接受 | |
---|---|---|---|---|
0 | 4 | med | high | acc |
1 | 2 | big | low | unacc |
2 | 4 | big | med | acc |
3 | 4 | big | high | acc |
4 | 6 | small | low | unacc |
5 | 6 | small | med | unacc |
safety_mapping = {
'low': 0,
'med': 1,
'high': 2,
}
df['安全性'] = df['安全性'].map(safety_mapping)
df
乘坐人數 | 後備箱大小 | 安全性 | 是否可以接受 | |
---|---|---|---|---|
0 | 4 | med | 2 | acc |
1 | 2 | big | 0 | unacc |
2 | 4 | big | 1 | acc |
3 | 4 | big | 2 | acc |
4 | 6 | small | 0 | unacc |
5 | 6 | small | 1 | unacc |
# 對上述字典做反向對映處理,即反向映射回原來的離散型別的特徵值。
inverse_safety_mapping = {v: k for k, v in safety_mapping.items()}
df['安全性'].map(inverse_safety_mapping)
0 high
1 low
2 med
3 high
4 low
5 med
Name: 安全性, dtype: object
四、1.4 通過sklearn對資料型別編碼
繼續沿用上一個汽車樣本,使用sklearn自帶庫對資料型別編碼。
# scikit-learn資料型別編碼示例
from sklearn.preprocessing import LabelEncoder
X_label_encoder = LabelEncoder()
X = df[['乘坐人數', '後備箱大小', '安全性']].values
X[:, 1] = X_label_encoder.fit_transform(X[:, 1])
X
array([[4, 1, 2],
[2, 0, 0],
[4, 0, 1],
[4, 0, 2],
[6, 2, 0],
[6, 2, 1]], dtype=object)
# 對標記進行編碼
y_label_encoder = LabelEncoder()
y = y_label_encoder.fit_transform(df['是否可以接受'].values)
y
array([0, 1, 0, 0, 1, 1])
# 對標記反向映射回原始資料
y_label_encoder.inverse_transform(y)
array(['acc', 'unacc', 'acc', 'acc', 'unacc', 'unacc'], dtype=object)
五、1.5 獨熱編碼
假設汽車安全性只是一個衡量標準,沒有特定的順序。但是計算機很有可能把這些作一個特定排序或者因此區分它們的重要性,這個時候就得考慮建立一個二進位制值分別表示low、med、high這三個屬性值,有為1,沒有為0,例如表示為med。
5.1 1.5.1 sklearn做獨熱編碼
# 獨熱編碼示例
from sklearn.preprocessing import OneHotEncoder
one_hot_encoder = OneHotEncoder(categories='auto')
one_hot_encoder.fit_transform(X).toarray()
array([[0., 1., 0., 0., 1., 0., 0., 0., 1.],
[1., 0., 0., 1., 0., 0., 1., 0., 0.],
[0., 1., 0., 1., 0., 0., 0., 1., 0.],
[0., 1., 0., 1., 0., 0., 0., 0., 1.],
[0., 0., 1., 0., 0., 1., 1., 0., 0.],
[0., 0., 1., 0., 0., 1., 0., 1., 0.]])
5.2 1.5.2 pandas做獨熱編碼
# 使用pandas對資料做獨熱編碼處理,數值型資料不做編碼處理
pd.get_dummies(df[['乘坐人數', '後備箱大小', '安全性']])
乘坐人數 | 安全性 | 後備箱大小_big | 後備箱大小_med | 後備箱大小_small | |
---|---|---|---|---|---|
0 | 4 | 2 | 0 | 1 | 0 |
1 | 2 | 0 | 1 | 0 | 0 |
2 | 4 | 1 | 1 | 0 | 0 |
3 | 4 | 2 | 1 | 0 | 0 |
4 | 6 | 0 | 0 | 0 | 1 |
5 | 6 | 1 | 0 | 0 | 1 |
使用獨熱編碼在解決特徵值無序性的同時也增加了特徵數,這無疑會給未來的計算增加難度,因此可以適當減少不必要的維度。例如當為後備箱進行獨熱編碼的時候會有後備箱大小_big、後備箱大小_med、後備箱大小_small三個特徵,可以減去一個特徵,即後備箱大小_big與後備箱大小_med都為0則代表是後備箱大小_small。在呼叫pandas的get_dummies函式時,可以新增drop_first=True引數;而使用OneHotEncoder時得自己分隔。
pd.get_dummies(df[['乘坐人數', '後備箱大小', '安全性']], drop_first=True)
乘坐人數 | 安全性 | 後備箱大小_med | 後備箱大小_small | |
---|---|---|---|---|
0 | 4 | 2 | 1 | 0 |
1 | 2 | 0 | 0 | 0 |
2 | 4 | 1 | 0 | 0 |
3 | 4 | 2 | 0 | 0 |
4 | 6 | 0 | 0 | 1 |
5 | 6 | 1 | 0 | 1 |
六、1.6 資料標準化
6.1 1.6.1 最小-最大標準化
為了解決相同權重特徵不同尺度的問題,可以使用機器學習中的最小-最大標準化做處理,把他們兩個值壓縮在區間內。
最小-最大標準化公式:
其中;為樣本個數;分別是某個的特徵最小值和最大值。
# 最小最大標準化示例
from sklearn.preprocessing import MinMaxScaler
import numpy as np
test_data = np.array([1, 2, 3, 4, 5]).reshape(-1, 1).astype(float)
min_max_scaler = MinMaxScaler()
min_max_scaler.fit_transform(test_data)
array([[0. ],
[0.25],
[0.5 ],
[0.75],
[1. ]])
6.2 1.6.2 Z-score標準化
Z-score標準化方法也可以對資料進行標準化,但是它的標準化並不能把資料限制在某個區間,它把資料壓縮成類似高斯分佈的分佈方式,並且也能處理離群值對資料的影響。
在分類、聚類演算法中,需要使用距離來度量相似性的時候應用非常好,尤其是資料本身呈正態分佈的時候。
資料標準化公式:
使用標準化後,可以把特徵列的中心設在均值為且標準差為的位置,即資料處理後特徵列符合標準正態分佈。
# Z-score標準化
from sklearn.preprocessing import StandardScaler
test_data = np.array([1, 2, 3, 4, 5]).reshape(-1, 1).astype(float)
standard_scaler = StandardScaler()
# fit_transform()=fit()+transform(), fit()方法估算平均值和方差,transform()方法對資料標準化
standard_scaler.fit_transform(test_data)
array([[-1.41421356],
[-0.70710678],
[ 0. ],
[ 0.70710678],
[ 1.41421356]])
七、1.7 二值化資料
二值化資料類似於獨熱編碼,但是不同於獨熱編碼的是它不是0就是1,即又有點類似於二分類。直接給出資料二值化的公式:
其中是手動設定的閾值,如果特徵值小於閾值為0;特徵值大於閾值為1。
# 資料二值化示例
from sklearn.preprocessing import Binarizer
test_data = np.array([1, 2, 3, 4, 5]).reshape(-1, 1).astype(float)
binarizer = Binarizer(threshold=2.5)
binarizer.fit_transform(test_data)
array([[0.],
[0.],
[1.],
[1.],
[1.]])
八、1.8 正則化資料
正則化是將每個樣本縮放到單位範數,即使得每個樣本的p範數為1,對需要計算樣本間相似度有很大的作用,通常有L1正則化和L2正則化兩種方法。
# L1正則化示例
from sklearn.preprocessing import normalize
test_data = [[1, 2, 0, 4, 5], [2, 3, 4, 5, 9]]
normalize = normalize(test_data, norm='l1')
normalize
array([[0.08333333, 0.16666667, 0. , 0.33333333, 0.41666667],
[0.08695652, 0.13043478, 0.17391304, 0.2173913 , 0.39130435]])
# L2正則化示例
from sklearn.preprocessing import Normalizer
test_data = [[1, 2, 0, 4, 5], [2, 3, 4, 5, 9]]
normalize = Normalizer(norm='l2')
normalize = normalize.fit_transform(test_data)
normalize
array([[0.14744196, 0.29488391, 0. , 0.58976782, 0.73720978],
[0.17213259, 0.25819889, 0.34426519, 0.43033148, 0.77459667]])
九、1.9 生成多項式特徵
# 生成多項式特徵圖例
import matplotlib.pyplot as plt
from sklearn import datasets
%matplotlib inline
X1, y1 = datasets.make_circles(
n_samples=1000, random_state=1, factor=0.5, noise=0.1)
plt.scatter(0, 0, s=23000, color='white', edgecolors='r')
plt.scatter(X1[:, 0], X1[:, 1], marker='*', c=y1)
plt.xlabel('\(x_1\)', fontsize=15)
plt.ylabel('\(x_2\)', fontsize=15)
plt.title('make_circles()', fontsize=20)
plt.show()
有時候可能會遇到上圖所示的資料分佈情況,如果這個時候使用簡單的特徵去擬合曲線,明顯是不可能的,但是我們可以使用,但是我們可以使用去擬合數據,可能會得到一個較好的模型,所以我們有時候會對特徵做一個多項式處理,即把特徵變成。
test_data = [[1, 2], [3, 4], [5, 6]]
test_data
[[1, 2], [3, 4], [5, 6]]
通過多項式特徵,特徵將會做如下變換
# 生成多項式特徵示例
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures()
poly = poly.fit_transform(test_data)
poly
array([[ 1., 1., 2., 1., 2., 4.],
[ 1., 3., 4., 9., 12., 16.],
[ 1., 5., 6., 25., 30., 36.]])