數據預處理中的數據編碼問題|python數據挖掘思考筆記(2)
某些機器學習演算法和模型只能接受定量特徵的輸入,需要將定性特徵轉換為定量特徵。 sklearn中的數據預處理preproccessing庫提供了相關的處理方法:
OneHotEncoder(),
LabelEncoder(),
LabelBinarizer(),
MultiLabelBinarizer()
Binarizer
OneHotEncoder(獨熱編碼)
為什麼能使用one hot?
1.使用one-hot編碼,將離散特徵的取值擴展到了歐式空間,離散特徵的某個取值就對應歐式空間的某個點。
2.將離散特徵通過one-hot編碼映射到歐式空間,是因為,在回歸,分類,聚類等機器學習演算法中,特徵之間距離的計算或相似度的計算是非常重要的,而我們常用的距離或相似度的計算都是在歐式空間的相似度計算,計算餘弦相似性,基於的就是歐式空間。
3.將離散型特徵使用one-hot編碼,可以會讓特徵之間的距離計算更加合理。
比如,有一個離散型特徵,代表工作類型,該離散型特徵,共有三個取值,不使用one-hot編碼,其表示分別是x_1 = (1), x_2 = (2), x_3 = (3)。兩個工作之間的距離是,d(x_1, x_2) = 1, d(x_2, x_3) = 1, d(x_1, x_3) = 2。那麼x_1和x_3工作之間就越不相似嗎?顯然這樣的表示,計算出來的特徵的距離是不合理。
那如果使用one-hot編碼,則得到x_1 = (1, 0, 0), x_2 = (0, 1, 0), x_3 = (0, 0, 1),那麼兩個工作之間的距離就都是sqrt(2).即每兩個工作之間的距離是一樣的,顯得更合理。
one hot 編碼的優點:
1.能夠處理非連續型數值特徵。
2.在一定程度上也擴充了特徵。比如性別本身是一個特徵,經過one hot編碼以後,就變成了男或女兩個特徵。
#使用onehot編碼import pandas as pd df2 = pd.DataFrame({id: [3566841, 6541227, 3512441], sex: [1, 2, 2], level: [3, 1, 2]})#輸出df2 id level sex0 3566841 3 11 6541227 1 22 3512441 2 2from sklearn.preprocessing import OneHotEncoder #導入OneHotEncoderid_data = df2.values[:, :1] # 獲得ID列transform_data = df2.values[:, 1:] # 指定要轉換的列enc = OneHotEncoder() # 建立模型對象df2_new = enc.fit_transform(transform_data).toarray() # 轉換# 查看df2_newarray([[ 0., 0., 1., 1., 0.], [ 1., 0., 0., 0., 1.], [ 0., 1., 0., 0., 1.]])df2_all = pd.concat((pd.DataFrame(id_data),pd.DataFrame(df2_new)),axis=1)#組合數據幀# 輸出df2_all,可以看到特徵由原來的3個變成了現在的6個 0 0 1 2 3 40 3566841 0.0 0.0 1.0 1.0 0.01 6541227 1.0 0.0 0.0 0.0 1.02 3512441 0.0 1.0 0.0 0.0 1.0# 再來看下不使用onehot編碼,使用自定義轉import pandas as pddf = pd.DataFrame({id: [3566841, 6541227, 3512441], sex: [male, Female, Female], level: [high, low, middle]})# 輸出df id level sex0 3566841 high male1 6541227 low Female2 3512441 middle Femaledf_new = df.copy() #複製新的一份數據框用來存儲轉換結構for col_num, col_name in enumerate(df): # 循環讀出每個列的索引值和列名 col_data = df[col_name] # 獲得每列數據 col_dtype = col_data.dtype # 獲得每列dtype類型 if col_dtype == object: # 如果dtype類型是object(非數值型),執行條件 df_new = df_new.drop(col_name, axis=1) # 刪除df數據框中要進行標誌轉換的列 value_sets = col_data.unique() # 獲取分類和順序變數的唯一值域 for value_unique in value_sets: # 讀取分類和順序變數中的每個值 col_name_new = col_name + _ + value_unique # 創建新的列名,使用原標題+值的方式命名 col_tmp = df.iloc[:, col_num] # 獲取原始數據列 new_col = (col_tmp == value_unique) # 將原始數據列與每個值進行比較,相同為True,否則為False df_new[col_name_new] = new_col # 為最終結果集增加新列值# 輸出df_new,可以看到level,sex分別都由1個特徵變成了3個特徵 id level_high level_low level_middle sex_male sex_Female0 3566841 True False False True False1 6541227 False True False False True2 3512441 False False True False True
使用one hot 編碼注意點:
a,OneHotEncoder 的輸入必須是 2-D array
from sklearn.preprocessing import OneHotEncoder#data.cloumn1 返回的 Series 本質上是 1-D array#所以要改成data[[cloumn1]] 2-D array再傳入OneHotEncoder(sparse = False).fit_transform( data[[cloumn1]])
b,OneHotEncoder無法直接對字元串型的類別變數編碼。
OneHotEncoder無法直接對字元串型的類別變數編碼,所以如果想利用OneHotEncoder對字元串型類別變數的支持,所以一般都採用曲線救國的方式:
from sklearn.preprocessing import OneHotEncoderfrom sklearn.preprocessing import LabelEncoder#先用 LabelEncoder() 轉換成連續的數值型變數a = LabelEncoder().fit_transform(data[cloumn])#再用 OneHotEncoder() 二值化OneHotEncoder( sparse=False ).fit_transform(a.reshape(-1,1)) # 注意: 這裡把 a 用 reshape 轉換成 2-D array
c,離散特徵如果是數值型變數,則數值變數的取值之間沒有大小的意義,如果是分類變數,則是無序變數。
d,對於每一個特徵,如果它有m個可能值,那麼經過獨熱編碼後,就變成了m個二元特徵。並且,這些特徵互斥,每次只有一個激活。因此,數據會變成稀疏的。
#示例>>> data=np.array([[1,0,3.25], [0,0,5.2], [2,1,3.6]])>>> enc=OneHotEncoder(categorical_features=np.array([0,1]),n_values=[3,2])>>> enc.fit(data)>>> enc.transform(data).toarray()array([[ 0. , 1. , 0. , 1. , 0. , 3.25], [ 1. , 0. , 0. , 1. , 0. , 5.2 ], [ 0. , 0. , 1. , 0. , 1. , 3.6 ]])
categorical_features是需要獨熱編碼的列索引,
n_values是對應categorical_features中各列下類別的數目,
注意這裡兩個值可以不指定,直接使用fit_transform函數也可以,程序將統計各列中類別的多少。但是只對整數有效,對浮點數會轉換為整數之後再統計,也就是對於3.5和3.6默認都是3,也就是同一類。
如果指定了這兩個參數,就要對未轉換的數據提出要求,各列必須是以{0,1,2,3,4......}來編碼,而不能以{1,10,100,200.........}這種隨意的方式來編碼。 否則會出現數組越界的錯誤。
LabelEncode(標籤編碼)
LabelEncoder是一個可以用來將標籤規範化的工具類,它可以將標籤的編碼值範圍限定在[0,n_classes-1]. 當然,它也可以用於非數值型標籤的編碼轉換成數值標籤(只要它們是可哈希並且可比較的):
#示例>>> le = preprocessing.LabelEncoder()>>> le.fit(["paris", "paris", "tokyo", "amsterdam"])LabelEncoder()>>> list(le.classes_)[amsterdam, paris, tokyo]>>> le.transform(["tokyo", "tokyo", "paris"])array([2, 2, 1])>>> list(le.inverse_transform([2, 2, 1]))[tokyo, tokyo, paris]
註:示例來源於官方文檔
LabelEncoder
設計為只支持 1-D array,也使得它無法像上面 OneHotEncoder 那樣批量接受多列輸入。
LabelBinarizer(標籤二值化)
LabelBinarizer是一個用來從多類別列表創建標籤矩陣的工具類:
>>> from sklearn import preprocessing>>> lb = preprocessing.LabelBinarizer()>>> lb.fit([1, 2, 6, 4, 2])LabelBinarizer(neg_label=0, pos_label=1, sparse_output=False)>>> lb.classes_array([1, 2, 4, 6])>>> lb.transform([1, 6])array([[1, 0, 0, 0], [0, 0, 0, 1]])
註:示例來源於官方文檔
無論 LabelEncoder() 還是 LabelBinarizer(),他們在 sklearn 中的設計初衷,都是為了解決標籤 y 的離散化,而非輸入 X。
所以他們的輸入被限定為 1-D array,這恰恰跟 OneHotEncoder() 要求輸入 2-D array 相左。因此我們使用的時候要格外小心。
Binarization(特徵二值化)
特徵二值化 是 將數值特徵用閾值過濾得到布爾值 的過程。特徵二值化的核心在於設定一個閾值,大於閾值的賦值為1,小於等於閾值的賦值為0。
公式表達如下:
>>> X = [[ 1., -1., 2.],... [ 2., 0., 0.],... [ 0., 1., -1.]]>>> binarizer = preprocessing.Binarizer().fit(X) # fit does nothing>>> binarizerBinarizer(copy=True, threshold=0.0)>>> binarizer = preprocessing.Binarizer(threshold=1.1)>>> binarizer.transform(X)array([[ 0., 0., 1.], [ 1., 0., 0.], [ 0., 0., 0.]])
註:示例來源於官方文檔
參考文章:
[scikit-learn] 特徵二值化編碼函數的一些坑4.3. Preprocessing data推薦閱讀:
※如何用Python和機器學習炒股賺錢?
※現在哪些編程技術比較火爆。需求量很大且待遇看漲?
※使用Flask實現用戶登陸認證的詳細過程
※Python版本3.3有zip這個用法嗎?
※[E0] Python入門