機器學習的數據預處理
數據預處理是在機器學習算法開始訓練之前對原始數據進行篩選,填充,去抖,類別處理,降維等操作;有的方法可以防止由於數據的原因導致的算法無法工作,有的方法可以加速機器學習算法的訓練,提高算法的精度。
1.缺失數據的處理
1.1查看數據確缺失情況
舉個例子說明如何查看數據缺失的情況:
import pandas as pd #創建一個缺失數據的DataFrame df = pd.DataFrame([\ [1,2,3,4],\ [5,6,None,8],\ [9,None,11,None,],\ [13,14,15,None,]\ ],\ columns=["A","B","C","D"]) lossDataCheck(df) #打印每列數據缺失的情況 print(df.isnull().sum())
運行結果是:
1.2.將存在缺失值的特征或樣本刪除
1.2.1按行刪除
將包含空值的行全部刪除使用DataFrame類里的dropna方法:
print(df.dropna()) print("-"*20) print(df)
結果如下所示,可知dropna方法並不對原數據進行修改。
1.2.2按列刪除
刪除包含空值的列也是dropna方法,不過需要給函數傳個參數:
print(df.dropna(axis=1))
結果如下:
1.2.3按條件刪除
只有當一行數據全空時才刪除:
df.dropna(how="all")
當一行數據缺少達到某個閾值時才刪除:
df.dropna(thresh=3)
指定的列里有數據缺失的行,才刪除
df.dropna(subset=["A"])
更多刪除操作請參考:dropna
1.3.缺失數據的填充
1.3.1 Imputer類
數據填充使用的是scikit-learn中的Impute類
最常用的技術之一是“均值插補”(meaneinputation)
from sklearn.preprocessing import Imputer imr = Imputer(missing_values="NaN",strategy="mean",axis=0) imr = imr.fit(df) imputed_data = imr.transform(df.values)
print(imputed_data)
結果:
如果把axis設置為1.則是使用行數據的均值進行填充;
strategy策略可選的有:中位數(“median”),最常出現的值(“most_frequent”)。
1.3.2 pandas自帶方法
avg_mean = df["D"].mean() df["D"] = df["D"].fillna(avg_mean)
這種方法需要自己一列一列去操作。
2.數據去抖
工業界中的過程數據可能會由於偶然誤差出現幾個特別不符合實際的數據,對數據做去抖其實就是刪除那些明顯是偶然誤差的數據。
有兩種方法去抖:
1)非常了解該數據的特性,比如常規的上限下限值,那么可以通過這些條件取出樣本中異樣的數據;
2)假定數據符合正態分布,那么僅保留大概率下的數據,對於那些分布在下概率下的數據進行丟棄處理。
2.1通過自定義條件篩選
特征數據的上下限篩選,假定temperature代表工廠溫度,工廠溫度幾乎不可能低於35,也不太可能高於60,那么通過條件篩選出正常的數據如下
import pandas as pd data = pd.DataFrame([\ [1,55],\ [2,45],\ [3,43], [2,44], [2,63],\ [3,33]\ ],columns=["working_num","temperature"]) data_screen = data[(data.temperature>35)&(data.temperature<60)] print(data_screen)
data_screen = data[(data.temperature>35)&(data.temperature<60)&(data.working_num>1)] print(data_screen)
也可加多個條件篩選,如上,得出的結果是:
2.2根據正態分布除去小概率事件數據
假設樣本中的特征數據符合正態分布,那么依據正態分布的條件可知在$(u\pm 3\sigma)$區間取值的概率為99.73%,而在區間之外的取值概率不到0.3%,可以認為是極小概率事件;$(u\pm 2\sigma)$區間之外的概率為85.45%。
使用正常的數據更容易得出合適的模型,所以篩出小概率事件的數據有時可以幫助我們提升算法的性能。
我們可以子集實現一個篩出小概率事件數據的函數:
import numpy as np import pandas as pd def shiftOutExtreme(data,column,ratio=3): ''' 輸入: data:DataFrame 樣本數據集 colum:str 選中的列名 ratio:float/int 系數,此值越大,取值空間越大 輸出: DataFrame 新的數據集 ''' my_array = data[column].values c_mean = np.mean(my_array) c_std = np.std(my_array) print(c_mean,c_std) new_data = data[(data[column] < ratio*c_std + c_mean) & (data[column] > c_mean - ratio*c_std)] print(new_data) return new_data data = pd.DataFrame([\ [1,13.1],\ [1,12.8],\ [1,13.2], [1,12.9], [1,12.7],\ [1,8.8]\ ],columns=["working_num","temperature"]) shiftOutExtreme(data,"temperature",1)
結果將最后一個8.8的數據篩選出去了:
3.數據類別處理
類別型數據是針對數值型數據而言的,因為算法沒法處理類別型數據,例如顏色數據等等。在機器學習中可以將數據分為3大類型:
1)數值型數據:可以直接進行數學運算的數據;
2)有序數據:類別的值可以進行排序,例如衣服的XL,L,M號碼等;
3)標稱數據:不具備排序的特性,例如顏色數據等。
這里主要對有序型數據和標稱數據進行處理。
3.1有序型數據的處理
類似於SQL中的外鍵映射,有序型數據也可通過映射轉換為數值型的數據。
例如下面的例子:
import pandas as pd df = pd.DataFrame([\ ["green","M",10.1,"class1"],\ ["red","L",13.5,"class2"],\ ["blue","XL",15.3,"class1"]\ ],columns=["color","size","price","label"]) print(df) size_mapping = {"M":1,"L":2,"XL":3} df["size"] = df["size"].map(size_mapping) print(df)
結果如下:
3.2 類標編碼
類標一般是string類型的,但有些機器學習庫要求類標以數值形式進行編碼;可以同有序數據編碼方式一樣,通過映射將類標映射到數值列表上,不過由於類標是無序的,映射后的數值大小沒有意義,僅僅是一種映射而已。
label_mapping = {label_name:idx for idx,label_name in enumerate(np.unique(df["label"]))} df["label"] = df["label"].map(label_mapping) print(df)
結果是:
此外scikit-learn中的LabelEncoder類可以很方便的完成類標的編碼工作:
from sklearn.preprocessing import LabelEncoder class_le = LabelEncoder() #將類標編碼 y = class_le.fit_transform(df["label"].values) #將類標還原 class_le.inverse_transform(y)
3.3標稱特征上的獨熱編碼(one-hot encoding)
獨熱編碼技術:創建一個新的虛擬特征,虛擬特征的每一列代表標稱數據的一個值,例如顏色可以虛擬出基礎的紅綠藍三色,所有顏色都可以通過這3個顏色的配比調和出來。
pandas模塊中的get_dummies方法可以很方便的實現獨熱編碼,此方法只對字符串列進行轉換,其他列保持不變。
new_df = pd.get_dummies(df) print(new_df)
結果如下:
4.數據集划分
4.1 現有模塊的方法
使用scikit-learn.model_selection 模塊中的train_test_split方法對數據集進行划分時,需要先子集把特征列和類標列分開,並子集給定划分的比例。
from sklearn.model_selection import train_test_split df = pd.DataFrame([\ ["green","M",10.1,"class1"],\ ["red","L",13.5,"class2"],\ ["blue","XL",15.3,"class1"]\ ],columns=["color","size","price","label"]) X,y = df.iloc[:,:-1].values,df.iloc[:,-1].values X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3,random_state=0)
其中test_size是划分數據集時測試數據所占全部數據的比例。
4.2 使用python自己實現
def train_test_split(X,y,test_size,random_state=False): #判斷數據長度是否相等 if len(X) != len(y): raise KeyError("X,y have difference num") length = len(X) if random_state: new_sequence = np.random.permutation(range(length)) X,y = X[new_sequence],y[new_sequence] test_num = int(length * test_size) return X[test_num:],y[test_num:],X[:test_num],y[:test_num]
5.特征縮放
使用梯度下降法的算法中,大多要使用特征縮放;
將不同的特征縮放到相同的區間,目前常用的技術有:
1)歸一化:將特征縮放到區間[0,1],技術上是:$x_{norm}^{(i)}=\frac{x^{(i)}-x_{min}}{x_{max}-x_{min}}$;其中max,min分別代表特征數據中的最大最小值;
2)標准化:將特征數據縮放為均值為0,方差為1的的正態分布數據,技術上:$x_{std}^{(i)}=\frac{x^{(i)}-u_{x}}{\sigma _{x}}$,其中u代表特征數據平均值,$\sigma$表示特征數均的標准差。
5.1歸一化(normalization)實現
使用scikit-learn.preprocessing庫中的MinMaxScaler類
from sklearn.preprocessing import MinMaxScaler mms = MinMaxScaler() X_train = mms.fit_transform(X_train) X_test = mms.tansform(X_test)
必須僅使用訓練集進行歸一化訓練,測試集不可參與歸一化類的訓練(即fit),之后在訓練好的歸一化類對象上進行轉換;程序也可以這么寫:
from sklearn.preprocessing import MinMaxScaler mms = MinMaxScaler() mms.fit(X_train) X_train = mms.transform(X_train) X_test = mms.tansform(X_test)
5.2標准化(standardization)實現
使用scikit-learn.preprocessing庫中的StandardScaler類
使用方式同歸一化類相同。
6.數據降維
數據降維可以減少模型的復雜度,降低過擬合風險,同時通過降維可以提高模型訓練以及運算速度,提高算法性能。
數據降維這邊主要划分為被動降維與主動降維兩大部分;其中被動降維是指通過L1正則化生成模型參數的稀疏矩陣,被動的使多個特征的模型參數為0;主動降維是指通過降維技術,降低原始數據集的特征數量。
6.1被動降維(L1正則化)
其實不屬於數據預處理的部分,不過能實現數據降維,這里也簡單提一下。
L1正則化:在算法的代價函數中加上L1正則化函數,函數表達式為$L1:\left \| w \right \|_{1}=\sum_{j=1}^{m}\left | w_{j} \right |$。
L1正則化意在懲罰過多過大的參數,所以在代價函數里,L1正則化函數前的系數越大,則其懲罰力度越大,一些無關緊要的特征對應的模型參數就越可能為0.
6.2主動降維
主動降維技術分為2個大類:
1)特征選擇:在原數據集特征上選出其子集,不對特征本身進行其他的構造,僅丟棄不重要的特征;
2)特征提取:對原數據集特征進行推演,構造一個新的特征集。
6.2.1 特征選擇:
一個經典的序列特征選擇算法是“序列后向選擇算法”(SBS),假定要將一個擁有d個特征的數據集降低到k維,SBS的步驟如下:
1)定義衡量標准函數J作為判定函數,算法的目標是最大化J;
2)檢查目前特征個數是否為k,如果是,則終止算法,返回新的數據集;
3)否則計算每個特征的衡量函數J,選擇最大的J對應的那個特征進行刪除,並跳轉第2)步。
一般J都選擇性能損失函數,即刪除一個特征后模型性能損失的值,假設A為整個特征集,$pfm(A)$為以特征集A訓練后的模型的性能;$A_{i}$表示A特征集中的第i個特征,$pfm(A-A_{i})$為刪除第i個特征后的模型的性能,那么:
$J(A_{i})=pfm(A) - pfm(A-A_{i})$
不要此類方法只適合於特征之間相關性不大的情況。
還有之前在隨機森林里提到的信息熵也可以作為特征選擇的一個參考方向。
6.2.2特征提取
是一種將原始數據集變換到一個維度更低的新的特征子空間的一門技術。
內容太多,重開一篇隨筆記錄。