一文湊齊四種變量轉換方法!

在一份數據集中通常會遇見兩類數據——數值型與類別型,數值型變量通常就是int、float類型,類別型變量就是object類型,也就是我們總說的字符型變量。如果更官方地講,數值型變量被稱作定量變量、類別型變量被稱作定性變量。

數值型變量主要體現在連續值和離散值:

  • 連續值:體溫、房屋面積等
  • 離散值:人數、個數等

我們都知道在大多數機器學習算法中都要與"距離"多多少少都會有些關係,所以只允許傳入數值型變量,在不需要做其它處理的前提下,原始數據集中的數值型變量都是可以直接使用的,典型的算法代表有支持向量機、邏輯迴歸、KNN算法等。

當然也有算法是可以處理字符型變量的,比如樸素貝葉斯、決策樹、隨機森林等,這類模型不關心變量的取值,而是注重變量間的分佈狀況和變量之間的條件概率,但如果要通過sklearn調用這些算法的API時,規定也必須要傳入數值型變量才合法。所以這個時候通常會面對一個問題,如何將字符型變量轉化成數值型變量呢?

LabelEncoder

我自己隨意構建了一個數據集,共有五個特徵,一個類標籤,可以看到只有Age和Heat是數值型變量,其餘四個皆爲字符型(類別型)變量。如果不調用任何API要將一列字符型數據轉化爲數值型數據,可以利用自定義函數結合apply或map函數實現,先以標籤變量Label舉例。

def label(e):
    if e == '猛男':
        return 0
    if e == '美女':
        return 1
    else:
        return 2
Data["Label"] = Data["Label"].apply(label)

這種方式針對類別較少的特徵還算可行,但是類別一多,if語句會寫吐的,並且可觀性極差極差!在sklearn中有專門處理類別標籤的API,只需要索引出對應的標籤變量並傳入即可。

from sklearn.preprocessing import LabelEncoder
LabE = LabelEncoder()#實例化
label = LabE.fit_transform(Data1.iloc[:,-1])
Data1.iloc[:,-1] = label
#一行表示
#Data1.iloc[:,-1] = LabelEncoder().fit_transform(Data1.iloc[:,-1])

一行表示是上面的化簡形式,最後的效果都是相同的,實例化之後還可以通過相應的方法獲取標籤變量中的類別。

LabE.classes_
'''
array(['猛男', '美女', '靚仔'], dtype=object)
'''

最後運行的結果如下:

OrdinalEncoder

處理過標籤變量之後,自然也要處理特徵中的,方法呢可以選擇利用上文提及的自定義函數的方法,但很明顯的弊端就是每一列都需要一個自定義函數,導致效率太低了。sklearn中也有處理特徵中爲類別型變量的API,使用方法與上文大體相同:

from sklearn.preprocessing import OrdinalEncoder
Data2 = Data1.copy()
OrE = OrdinalEncoder()#實例化
Data2.iloc[:,2:-1] = OrE.fit_transform(Data2.iloc[:,2:-1])
#Data2.iloc[:,2:-1] = OrdinalEncoder().fit_transform(Data2.iloc[:,2:-1])

與LabelEncoder最大不同之處就是OrdinalEncoder要求傳入數據不能爲一維,意思就是如果你只想轉換一個特徵(上面代碼爲三個),那麼你需要通過reshape(-1,1)將一維轉換爲二維再傳入,而LabelEncoder是隻針對標籤變量的方法,所以自然接受一維數據。

OrdinalEncoder是沒有class_方法的,取而替之的是categories_,作用也是獲取傳入數據每個特徵的類別。

OrE.categories_
'''
[array(['女', '男'], dtype=object),
 array(['優', '差', '良'], dtype=object),
 array(['>40kg', '>50kg', '>60kg'], dtype=object)]
'''

最後運行的結果如下:

OneHotEncoder

現在我們要考慮一個問題,就是我們這麼轉換變量真的合理嗎?上面介紹了數值型變量,這裏再來簡要說一下類別型變量。

類別型變量按照變量間是否存在次序關係,可以分爲無序型變量和有序型變量,而有序型變量在可計算的約束下又可以說成有距變量:

  • 無序型變量:數據集中的性別
  • 有序型變量:數據集中的體重
  • 有距變量:數據集中的成績

先解釋一下有距變量,比如上面數據集中的體重有三個類別60kg、50kg、40kg,很明顯這是有序的,但是這三者間是存在可計算關係的,比如60kg-10kg=50kg,類別之間可以通過一定計算相互轉換。

再看有序型變量,比如上面數據集中的成績也有三個類別優、良、差,這三個類別並不是毫無關係,因爲在某種意義上"優"強於"良"、“良"強於"差”,很明顯這也是有序變量,但這可不是有距變量,因爲優-良\neq差是一定的。而無序變量理解起來就非常簡單了,性別就是一個特別典型的例子,類別間的關係就是男\neq女,這是一種"有你就沒我的關係"。

前面在通過OrdinalEncoder對這三個類別型變量進行轉換時,都會轉換成相應的數字,從算法的角度來講,如果你給我傳入的數據爲數字,那麼我就認爲它是可以相互計算的,但是數字是有大小之分的,2和0參與計算起到的作用自然不同。

但我們已經清楚只有有距變量纔有這樣的相互計算的性質,而無序變量和有序變量經過編碼轉換後被誤解成了有距變量,忽略掉了數字本身的性質,自然會影響後期的建模,而對於這樣的變量可以利用獨熱編碼將原本變量轉化爲啞變量。

from sklearn.preprocessing import OneHotEncoder
OneHot = OneHotEncoder()#實例化
result = OneHot.fit_transform(Data1.iloc[:,2:4]).toarray()
OneHotnames = OneHot.get_feature_names().tolist()#獲取新特徵名稱
OneHotDf = pd.DataFrame(result,columns=OneHotnames)
Data3.drop(columns=['Sex','Grade'],inplace=True)
Data3 = pd.concat([OneHotDf,Data3],axis=1)

最後運行的結果如下:

這個新建的DataFrame中的左半部分是由許多新的特徵構成,每個特徵只包含0和1兩個元素,其中1代表有、0代表沒有。比如Sex特徵中包含兩個元素"男"和"女",而這兩個元素就可以通過啞變量轉化形成兩個新的特徵。Sex特徵中爲"男"的樣本在新特徵"x0_男"中就爲1,在"x0_女"中就爲0,反之可推出Sex中的"女"在新特徵中的分佈。

Dummy Coding

上面利用OneHotEncoder進行啞變量轉換略顯複雜,在Pandas庫中也有相應的方法可以起到同樣的效果。

Data4 = pd.get_dummies(Data4)

一行代碼顯得更加整潔,最後運行結果如下,在get_dummies也有很多參數可以設置,比如新特徵的名字等。

這裏再介紹一下虛擬變量,虛擬變量和獨熱編碼十分相似,不同之處在於虛擬變量比獨熱編碼少生成一個變量,下圖就是虛擬變量的一種形式。

在轉換爲虛擬變量後,以Grade這一特徵爲例,可以看到刪去了"Grade_良"這一新特徵,因爲對於一個人的成績而言,“優"和"差"這兩個特徵足以表達出所有的信息,如果一個人成績不是"優"和"差”,那一定就是"良",所以原數據經獨熱編碼後是有冗餘的,而虛擬變量則很好地刪去冗餘,相應的代價就是虛擬變量在表達上沒有獨熱編碼直觀。

參考鏈接:
[1].https://blog.csdn.net/weixin_30732825/article/details/101073774
[2].https://www.bilibili.com/video/BV1qt411C7PX

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章