【Python機器學習】104:如何處理加入類別變數的回歸模型

How to deal with regression models with categorical variables

張育晟 Eason Chang
展開數據人生
9 min readJul 9, 2020

--

Photo by Ilya Orehov on Unsplash

上一篇文章提到如何在模型中加入高次項或是新的變數,那假如我想在模型再加入類別變數要如何處理?目前向量 X 的值(場均得分與場均籃板數)都是數值,所以運算起來並沒有太大的困難,但是在原始資料裡除了數值以外還有一些名目資料,如球員的位置或球員姓名等。

從上圖中可以看到在我們nba的資料裡,名目資料包含了球員姓名、出生年月日與位置。如果我在之前訓練出來的模型照著球員的位置去分類,會不會在預測年薪上得到不同的結果呢?

畢竟雖然有些球員他的場均得分不高,但他可以透過其他數字(ex:籃板球數、助攻數等)來提高他的薪資:像是籃球的內線球員,他的得分可能不會像後衛那麼耀眼,但是他的籃板數或阻攻數可能很高,對球隊獲勝也是至關重要。如此看來,在訓練薪資模型時把球員的位置考慮進去是必要的。

籃球員的位置(Position)

大家可能平時不太關注籃球,對籃球的內容不太熟,因此我們從原始資料看一下籃球員的位置(Position)有哪些。一般在探索原始資料的數值時,會畫直方圖或是散步圖看分布的情況如何,但是類別變數要怎麼探索呢?

我們可以使用unique()函數觀察類別函數個別的值有哪些,也可以用value_counts()得知原始資料中的類別變數分佈如何。

從上圖中觀察到原始資料的位置(pos)有分成 G、F、C、G-F、F-G、F-C、C-F,也就是常聽到的後衛、前鋒跟中鋒。但是除了這三個位置代號,還有一堆G-F、F-G有的沒的,類似這種代號的就是所謂的搖擺人(Swingmen、Tweener)。在現實生活中,大家應該聽過小皇帝詹姆斯(Lebron James)吧,他就是號稱可以從控球後衛打到大前鋒的球員,又能控球,得分能力&搶籃板能力也很突出。

由於含有多個位置的球員分析起來有點繁複,因此我們將位置簡化,大致分成三類(G、F、C)即可,Swingmen或Tweener改以第一位置登錄。

先準備了一個Dictionary,接著讓原來的資料從 Key 使用mapping 函數對應到 Value。如此以來,unique()的結果就只會有3個值(即是我們想要的G、F、C)。同時,我們也在原本的DataFrame新增了一個變數叫 pos-recategorized,正是我們重新分類的新的球員位置。

從上圖中發現,G 跟 F 的數量差不多,C相較之下少一點。折這情況很正常,畢竟一支球隊5個人,2個後衛,2個前鋒,只有1個中鋒,加上中鋒需要的身高要求高,在場上主要又做苦工,因此許多原本打中鋒的球員現在都跑去打前鋒了(NBA時代的變化Q_Q)。

重新分類完了之後,我們將後衛(G)、前鋒(F)、中鋒(C)分別用不同顏色在散步圖標記:

這裏介紹一個不錯的套件:seaborn。我們一般在用matplotlib.pyplot 作圖時,資料都需要做成陣列。假如我現在要畫3個類型的點,那我就要準備3種類型的陣列,然後分3次畫。使用seaborn套件可以解決這個問題,加速視覺化,在畫圖時方便很多。

從上圖中,看起來後衛(G)與前鋒(F)的薪資分佈位置相似,但中鋒(C)比較自成一格,得分即使再高薪資似乎有天花板存在,這也是為什麼現在大家都不願意打中鋒的原因之一。

One-Hot-Encoding

如果我們針對不同的位置分別去訓練模型,找到不同的系統h,聽起來似乎是個不錯的辦法。我們能否依據 pos_recategorized 這個變數將h調整為:

如此一來,之後如果有新秀或自由球員來簽約,我們除了要知道他的場均得分,還要知道他優先登錄的位置,才能一起做評估預測薪資。

如果我們試著將三個 if 條件所對應的h(x)利用一個 δ 係數結合,寫成一個方程式:

加入 δ 係數後,原本 3 個方程式我就可以寫成1條式子,但我要讓 δ 係數在不同鋒衛位置時數值為 1 或 0。

這個做法有個專有名詞叫One-Hot-Encoding或叫 Dummy Variable:

如果該球員是後衛(G),則 δ(G) 是1,另外兩個 δ 都是0,以此類推。
如果該球員是後衛(G),則 δ(G) 是1,另外兩個 δ 都是0。在這情況下後衛的公式就會只剩w0(G)+w1(G)X1

接下來我們試著自己手動創建 δ 係數:首先要先知道球員的位置有G、F、C,所以需要手刻的 δ 係數有3個。先用 Dictionary 把球員的位置分別用0、1、2代表,0代表後衛,、1代表前鋒,2則代表中鋒。接下來的任務就是把 0 轉成[1,0,0]、1轉成[0,1,0]、2轉成[0,0,1]:

接著我們在原本的 DataFrame 中新增 delta_G、delta_F、delta_C三個變數,我們把幾個重要的欄位挑選出來檢查:

以上就是手動創建 δ 係數的作法,但是有些模型的類別變數可不止3個,為阿里要一個一個手動刻顯然不切實際。因此 Python 幫助模型建立 δ 係數的方法有兩種:

一種是使用 Scikit-learn 套件的 Transformer:sklearn.preprocessing.OneHotEncoder創建 δ 係數

由於OneHotEncoder是一個Transformer,因此可以直接fit_transform。

另一種方法則是使用 Pandas 內建的pd.get_dummies 創建 δ 係數:

如果不放prefix參數,則輸出的欄位名稱只會跟原始資料的欄位名稱一樣,亦即C、F、G。

加入 δ 係數的回歸模型:

在透過 Python 套件求得 δ 係數後,接著拿各球員的平均得分(ppg)乘以各位置的 δ 係數,產生3個對應的新欄位:delta_G_ppg、delta_F_ppg、delta_C_ppg,這就是我們的 X1*δ(G)、X1*δ(F)、X1*δ(C)。

如此一來,公式中X的6個變數(3個X0 & 3個X1)都準備好了,接著就可以把X和 y 拿來分割成訓練和驗證資料了:

使用Normal equations 解 w:

算出來的6個w分別對應到傳進來的X的順序

使用sklearn.linear_model.LinearRegression求w:

如果大家試著用Scikit-Learn的內建函數跑一遍,會發現X不是只有6個變數嗎?那怎麼會同時跑出1個截距(Intercept)和6個係數(Coefficient)呢?

這是因為Scikit-Learn不知道你訓練的是有加類別變數的回歸模型,因此函數還是以為你傳了跟原本一樣的6個變數,那當然會回傳7個係數。遇到這狀況該怎麼辦才好呢?

其實LinearRegression中有個參數叫fit_intercept,意思是要不要幫你算截距項。由於在先前的例子都要做,所以就沒提到這個參數,但是現在由於我們不再需要截距項了,因此只要把這個參數設為 False即可。

訓練完模型後,我們將系統h在訓練資料上描繪出來:

綠線(中鋒)的斜率看起來似乎比其他兩條要小一點。現在,不同位置的球員進來,就會有不一樣的對照h(薪資斜率),但還是在同一個式子裡面。

如上圖,現在我們訓練出來的模型h不再只是一條線了,而是3條不同球員位置(G、F、C)的線。未來如果還有遇到需要在模型裡加入類別變數了話,可以藉由OneHotEncoding來處理這類型的問題。

感謝你閱讀完這篇文章,如果你覺得這些文章對你有幫助請在底下幫我拍個手(長按最多可以拍50下手)。

上一章:【Python機器學習】103:使用正規方程式解決線性 & 非線性回歸模型

下一章:【Python機器學習】105:機器學習中誤差的來源與正規化

--

--

張育晟 Eason Chang
展開數據人生

在不斷變動中努力思索自己的定位,想做的事情很多,但最希望的是不要變成無趣的人。 Linkedin: https://www.linkedin.com/in/stareason1997