The tutorial provided here is not to teach you how to implement the following data analysis related algorithms (if you are interested in this part, you may go here to see how I implement them in Python.) I would like to explain and provide some notes that are worth to mention when you apply these algorithms to your data. Note that I only provide some general concepts of data analysis that may not cover all (especially the cutting-edge) data analysis techniques.
I teach data analysis courses at NCCU, Taiwan for several semesters and I found some students sometimes misuse these algorithms, so I wish this note may help you. If any question, please contact hsiaom at nccu dot edu dot tw.
Overview by using Table-based Data Analysis
What is model? Supervised and Unsupervised
Regression and Classification
Distance and Clustering
What else?
Evaluation methods
Series and Dataframe
Exploratory Data Analysis, EDA
Encoding
K-means (+knn + Hierarchical Clustering)
Linear Discriminant Analysis, LDA
Association Rules
首先要解釋模型 (model) 這個概念。對於最簡單的資料分析形式來說,下圖的表格型式是最常見的了。你有 n 筆資料,標記好 index 放在左側,每一筆資料有 m 個屬性(attributes 或叫做 m 個特徵 features,attribute 和 feature 這兩個詞可以互換)。舉例來說,一班有 45 (n) 個同學,每一個同學有身高 (x1)、體重 (x2)、...、籍貫 (xm) 等多個特徵,總共有 m 個特徵,你會把資料妥善放在對應的表格中。這裡的 y 值比較特別,通常我們也稱之為 target,y 表示一個我們特別關心的值。舉例來說,y 值是同學畢業之後第一份薪水的數字。而一般來說,大型資料的 n 和 m 的數量都很大,n 大約在數千到數百萬以上都有可能,m 可能在數十或是數萬以上,因此這個表格會太大而難以處理,因此我們有了模型的需要。(而且若我們可以用比較簡單的方式表示原始資料,那必定表示資料一定有可以歸納整理的規則。)跟玩具模型一樣的概念,世界上最好的模型就是原始的那個物體,舉例來說,最棒的飛機模型就是原始的那一架飛機,而模型只是一個比較粗糙的表示,可能有一些我們認為不重要的細節在製作模型的過程中被我們捨去。因為我們沒有這麼大的空間把原始的飛機留下來,因此模型的功能就是能最大化的保留原始的樣貌就可以了。同樣的例子,由於 n 和 m 可能很大(而且我們人腦無法 process 這麼大量的資料讓我們歸納出 X 與 y 的關係),所以我們希望能有演算法來告訴我們一個簡單的數學方法來描述X 和 y 的關係。這樣的描述關係 X → y 就是所謂的模型,而能夠自動根據資料產生模型的演算法有非常多種,稍後會談到。而這個模型是我們根據一組資料建立出來的,這 n 筆資料我們稱為 training data。
一般來說,我們最早認識的模型是下圖左的「線性迴歸」(Linear Regression),最簡單的線性回歸只有一個 x,然後有一個 y。比方說全台灣國小一年級新生的體重 (x) 以及身高 (y) 的關係式,我們可以假定身高和體重有一個線性的關係 y = ax + b。然後我們可以搜羅許多小一同學的資料 (n 個),去計算出 a 和 b 的最佳數值,來當作模型的參數,並且我們就用這個模型來取代整份資料表。這裡有兩點值得注意。第一,模型的選擇是分析者要做的功課,所以身高和體重是否適合使用 y = ax+b 是建立模型的人需要負責的(說不定它們的關係比較接近 y = ax^2 + bx + c 或其他數學形式),模型選擇的觀察方法我們到 EDA 的章節再談。第二,模型是一個資料的表示方式,就如同下圖,我們建立的線性模型是橘色的線,而實際的值是藍色的點。我們利用藍色的點找到「最好的這一條線」的參數 a(線的斜率)和 b(線的截距)。但是在實務上,「最好的這一條線」的定義也是可以由模型的建立者選擇的,數學家會有各種不同的數學公式來評估一個模型的參數如何被定義為「最好」,這點我們也是稍後再談。不過我們可以先舉線性回歸的例子,通常最好的回歸線他的定義是「資料點距離回歸線的距離平方和要是最小的」,以右圖例來說,就是藍色半透明的矩形面積和要是最小的,那麼這個橘線就是最好的回歸線,這個選擇回歸線的方法叫做 MSE (Mean Squared Error)。當然,你也可以不要選擇則這個定義,那麼你會找出不同參數的回歸線。
模型最常被使用在預測 (prediction) 上,以第一張表格的圖來說,我們通常還有一些資料集是只有 X 但是沒有 y 的,因此我們可以用 training data 建立的模型 ( X → y ),來輸入 testing data 的 X 來預測 testing data 的 y 值(假定這裡有 t 筆資料)。當然這樣的使用方式一些假設。第一,training data 和 test data 應該是具有統計上的相似性,這樣我們才能拿 training data 的 model 過來給 testing data 做預測。比方說,model 是用小學生的資料做的,但是 testing data 卻是成年人的資料,那麼就不太適當了。(除非有證據顯示小學生和成年人的身高體重的線性參數是相似的。)第二,training data 應當和 testing data 是獨立的資料集,你不可以把 testing data 拿去做偷偷做 model 。另外還有 validation data,這個稍後再談。
如果你的 training data 資料有 X 有 y,那麼我們可以一般性地稱為這是一個 supervised learning problem,產生模型後可以做 prediction 的應用。這個 y 值可以是數值型的,例如:身高、薪水、房價、成績;或這個 y 值也可以是非數值型的,例如:信用評等高中低(高中低有順序關係)、類別ABC(類別可以沒有順序關係,例如貓或狗或兎)、投資與否(也就是二元的類別)。通常若 y 是實數,我們會特稱這是 regression 的問題;若 y 是類別,我們則稱這個是 classification 問題。他們各自有不同的演算法和評估方式來產生 model。另外要特別注意,X → y 這樣的映射可以不要是狹義的數學公式(例如:y = ax + b),model 可以是一個廣義的規則 (rule) ,例如:「如果 x1 < 5,我就判定 y 是 13.5」,所以 supervised problem 的 model 是更多元的。
但若是你的 training data 資料只有 X 沒有 y,我們一般性地稱為 unsupervised learning problem。由於沒有 y 就無法製作 X → y 這樣的模型了,所以這時我們只會針對 X 來做分析。通常你可以視這樣的 X 資料表格為一個 m 維度空間中的 n 個資料點。這些資料點之間或許有一些隱含的相對關係可以在這個 m 維度中展現,例如:這 n 個點可能有群聚 (clustering) 的現象。下圖就是一個群聚的例子,X 只有兩維表示花朵的花萼寬和花萼高,你可以簡單地看出這裡應該有兩種不同的花(圖中被標記成不同顏色的點)。處理群聚還有很多技巧,尤其是對於距離公式的選擇和使用技巧,以及我們怎麼認定這 n 筆資料要分成若干群的方法,這些細節我們稍後再談。
資料分析的問題由以上表格形式我們可以討論一下。一、n 的數量不夠多,也就是訓練資料集不足。若 X 與 y 有一定的關係,但資料量少的時候,模型容易有偏差(容易有偏差不表示一定有偏差)。比方說:只拿台北市的國小一年級學生來學習,台北小孩可能營養比較好(垃圾食物吃比較多)體重比較重,他們可能不足以代表整個台灣的一年級學生。n 的大小要看該資料集是否有代表性,也並非一定資料多就是好。若資料不足,有時候可以採用內插法多生一些偽資料出來,但這已經涉及到偽資料是否具有代表性的問題。二、n 的數量太大,若運算能力不足,多半使用隨機 sampling 的方式取用部分的資料。但是隨機取用的資料同樣牽涉到資料代表性的問題,取樣也要小心。三、m 的數量太少,若世界上最正確的模型為 X* → y*,但你只能蒐集到部分的 X 作為 features,那麼你的模型演算法可能做不出一個好的 y。尤其是我們根本不知道 X* 到底應該包含多少以及哪些 feature。四、m 的數量太多,為了避免前一個問題發生,我們有時候會收集過多的 features,但是並不是每一個 feature 都有用,所以通常會進行 feature 的整理。比較常見的手段稱為「降維」(dimension reduction),也就是將原本 m 維度的資料,在不失去其代表性的情況之下(或是失去一點點),僅使用 k 維度 (k < m) 來表示 n 筆資料。比方來說,若 m1 為公分身高,m2 是英吋身高。我們可以知道這兩個值根本就是具有線性轉換的關係(或者學術一點相關係數為 1),或許是因為當初想要儘可能地蒐集資料,所以把這兩個欄位都設計上去。對於很多產生模型的演算法來說,m1 與 m2 只要保留一個就可以了。另外一種例子是,若薪資、年資和獎金也是具有某一種關係,那麼用三個欄位表示這些值會太浪費空間。我們可以用(或許)兩個欄位就可以好好地描述 n 筆資料了。降維的手段我們稍後在 PCA 的章節再談。五、模型的選擇是要倚靠分析者的,若比方說身高和體重的關係不是 y = ax +b,而是 y = ax^2+bx+c,那麼你選用前一個模型就不是好選擇。但是模型非常多種,而每一個模型演算法又有不同的參數可以選擇,到底哪一個是最好的選擇,有的時候需要靠人的智慧和經驗。六、分類問題的不平衡資料,若 y 值是二元分類問題(是或否),但是我們所收集到的資料卻大部分是「是」(假設 80%),只收集到少量的「否」(假設 20%),那麼模型通常會有點失準,因為在大部分的預測情況之下,模型只要回答「是」就好了,那麼這個模型就可以得到很高的正確率(80%+)。這時候的解法可能是把資料都降低到兩類一樣多,但很明顯這樣 n 值也會少很多。另外一個解法是看一下「否」這個類別的準確性是不是夠高,模型是否有學到「否」的特徵而不是一味的回答「是」,可以參考本文後面的評估方法。另外,可以把少的類別資料集做擴充,就如同 n 太少的時候。
那麼除了以上的 supervised/unsupervised learning problem 可以簡單的用表格解釋之外,還有什麼樣的問題是在這個表格之外的?Reinforcement learning 目前是公認的另外一個類別的學習問題(但他比較難用表格解釋)。先前我們是學習資料中 X → y 的關係,讓 predict y 很準確。但強化學習的對象是一個系統(或稱為環境),這個系統的內隱規則未知,而我們同樣要找出這個系統的內隱規則(比方說一個機率分布),不過我們一開始並沒有資料集,我們只可以對這個系統互動來逐漸地收集資料,而互動的動作是事先定義好的。所以你可以想像我們一開始沒有 n 個點,只有幾個動作 (actions) 可以做,每做一個動作就可以產生一組觀察值 (observation),我們的目的是讓某個 observation 值可以接近(或等於)一個理想值或最大最小值(這個要看不同應用而定)。由於我們不太能無窮盡地做所有的動作的組合(因為動作可以是一連串的,我們也不知道要連續做多少的 action),所以我們必須要有策略地進行 action,並且根據所觀察到的 observation 來找到這個系統運作的原理,最終達成我們設定的 目標。舉例來說,我們面對的系統是一個迷宮,我們可以做的動作有四個是前、後、左、右移動。系統在我們動作之後,可以輸出一個二元的 observation 值告訴我們「是否到迷宮出口」了。(我們同時可以記錄下來過去我們之前走過的路,或是目前所在的位置,或是這個系統目前的狀態。)那麼當 action 執行多次後,我們會蒐集到一些跟這個迷宮有關的 action 與 observation 的對應,這些資料裡面應該會隱含了這個迷宮通往出口的路線。這正是我們要學習的東西。(但強化學習的策略跟先前提到的 supervised/unsupervised learning problem 差異比較大,我們就只談到這裡。)
回到模型的部分,如果我們有若干個模型要進行評估哪一個比較好,通常我們會找一組 testing data 是預先知道其 y 值的,然後來比較各個 model 預測出來的 target 值(通常會稱為 y^ 或 y hat 值),跟已知的 testing data y 值有沒有差別。若 y 值是數值(也就是 regression 問題),那麼採取的評估策略會是 t 個測試資料中,y^ 與 y 差距的總和是多少,這個差距公式有很多形式,比方說:(1) 1/t Sigma_t |y^-y|,也就是直接把 t 個 y^ 和 y 的絕對值差距加總平均、(2) 1/t Sigma_t (y^-y)^2,也就是把 t 個 y^ 和 y 的差平方後加總平均。以上這兩個公式來說,越好的 model 會有越低的值。這個所選擇的公式叫做 loss function,當然 loss function 有很多形式。不同 model 在選擇參數時,也通常會儘量 minimize 這個 loss 值,而 minimize 的過程通常是用微積分的手段。上述的 (2) 公式是可微的,而且相較於 (1) 比較有鑑別度,所以 (2) 或是更複雜的 loss function 比較常用。
若 y 值是類別(也就是 classification 問題),那麼我們通常評估的策略是 t 個測試資料中,有多少比例的 y^ 是正確的分類到 y 類別中。我們常用 confusion matrix 來展示分類的結果。以下是某一個 model 針對某個二分類問題最後繪製的 confusion matrix,這裡 t 是 100 個資料,共分為兩類 Iris-versicolor 以及 Iris-virginica,測試資料剛好兩類資料各一半。若我們以 Iris-versicolor 為分類的目標(通常會排在另外一個分類的前面或上面),左側是 actual 也就是真實的 y 分類,上側是這個 model predicted 的結果。左上的格子 46 表示這個 model 有 46 筆資料分類至正確的 Iris-versicolor 類別,但有 4 筆 Iris-versicolor 的資料(右上)預測錯誤分至 Iris-virginica 類別;左下則是 3 筆 Iris-virginica 分類錯誤至 Iris-versicolor,右下則是有 47 筆 Iris-virginica 能正確預測分類至 Iris-virginica 類別。
以下右圖是每一個欄位的名字以及相關的公式,P = True Positive + False Negative、N = False Positive + True Negative。每一個分類的 model 都可以算出這樣的表格結果以及各公式的值,在評量 model 時當然 TP 和 TN 越高越好,但是值得注意的是每一個應用其實對於不同評估值的要求並不同。舉例來說,若這是一個癌症的判別(也就是 target class 是「有癌症」),我們當然會盡量希望 TP、TN 越高越好,但若不能兩全,我們可以允許儘量提高 TP 而讓 TN 差一點沒關係(TN 差一點也就是 FP 會變多)。實際的語意是指我們要盡量抓出所有有癌症的病人 (TP),而有一些誤判 (FP) 沒有關係,但是盡量不能出現漏判 (FN) 的病人,因為誤判的病人可以再做第二次的檢驗,但是漏判的病人會喪失提早治療的機會。另外一個相對的例子是「要推薦某產品」,在眾客戶中成功推薦產品 (TP) 當然很重要,但是沒推薦 (FN) 可能客戶頂多不滿意,但是推薦錯誤 (FP) 多了可能會造成客戶的反感。
一般來說 accuracy 是最普遍的指標,他表示在實驗中有多少的準確分類程度,強調 TP 和 TN 要儘量越多越好,但是看不出是哪一種分類錯誤(也就是不知道是 FP 多還是 FN 多),所以模型多半會搭配 recall 和 precision 來做衡量,甚至 F1 score 也是幾乎必備的評量指標了。
若你只有 training data 而沒有 testing data 的話,一般可以將 training data 事先(隨機或按時間順序)切成兩塊,大塊的作 training、小塊的作 testing。
由於第一章圖的表格很適合拿來做資料分析的運算,所以在分析的程式語言中有一個名為 pandas 的套件是專門用來處理這樣表格型的多維資料,若你的資料只有一個 x,那麼可以用 pandas.Series 的處理,若你有多個 x 形成的 X,可以用 pandas.Dataframe 來處理。我有一份 Python 的 code 在demonstrate pandas 的運算,可以看一下。這一小章節不會教你怎麼寫 pandas,但是會做出一些提醒。
pandas 運算有一個特別的法則就是 element-wise operation 或是 propagation。這是一般人在從 C 或 Java 語言轉換至 R 或 python 時,常常會遇到的撰寫問題。在直觀上,表格對於 C 或 Java 來說就是一個 cell(i, j) 的概念,要遍歷整個表格需要雙重迴圈的幫忙,但是由於 C 或 Java 我們比較少在寫 multi-thread,所以基本上遍歷整個表格需要 O(m*n) 的時間複雜度。可是 pandas 假設了一件事情,當我們在遍歷每一個 cell (或是每一個 column 或是每一個 row)的時候,都應該是獨立的運算,所以若我們有多個 CPU 的時候,這些 cells(或 columns 或 rows)應該要可以平行的處理,讓 (m*n) 很大的時候,能善用多核 CPU 的運算資源,快速的處理表格。所以你應該要善用 pandas 裡面 apply() 以及 applymap() 這樣的函式來一次處理整個 cells、rows 或 columns。
這裡不得不提到 python 原生的 map() 和 reduce() 函式,以及 iterable 的概念。map() 函式的定義是 map(function, iterable),第一個參數 function 很好理解,是一個 function pointer,指名要使用那一個 function 作為 mapping function。第二個參數是一個 iterable,表示這個變數是一個物件,而這個物件可以降解出一個一個能迭代出來的 item。舉例來說,一個 list 物件 li = [1, 2, 3, 4, 5] 就可以迭代產生五個 integer 的 items。或是字串 s = "book" 可以迭代產生四個 character 的 items。同樣的,一個 pandas.Series 是可以迭代出一個一個的 cell,而 pandas.Dataframe 可以一個一個迭代出 rows 或是 columns。假設 function pointer 指向一個 y = f(x) = x+1 的函式。那麼 map(f, [1,2,3,4,5]) 的結果就會是一個一個地將 iterable 解開成 item 之後,一個一個地丟進去 f(x) 裡面,並把一個一個的 y 收集起來。因此前述的 map() 會產出 [2, 3, 4, 5, 6] 這樣的結果。functools.reduce(function, iterable) 則是類似,但是他的 function 要吃兩個參數,假定為 y = add(u, v) = u+v,那麼 functools.reduce(add, [1, 2, 3, 4, 5]) 的結果運算順序如下,他是指先從後方的 iterable 取兩個 items 出來當作第一輪的 u 和 v,並進行 y 的運算。而在下一個 iteration 時,後方的 iterable 一次只提供一個 item 並給予 function 的第二個參數(也就是 v),這時的第一個參數(也就是 u)將由上一個 iteration 的 y 來代替。這樣的運算將直至 iterable 使用完最後一個 item 為止。所以 reduce() 最後只會輸出一個值而已,並不像 map() 是輸出一個 iterable。
對比到 series,若你要針對 cell 的每一個值 +1,其實你不需要迴圈來做這件事情,pandas.Series.apply(func) 也提供類似的概念,由於 Series也被認為是一個 iterable,所以他取出 item 時,會是一個一個的值 (cell),所以指定的 func() 會運作在每一個 cell 上面。這裡有官方的範例。這種遍歷的 operation 就叫做 element-wise operation。
同樣的 dataframe 也有類似的函式為 apply() 和 applymap(),apply() 會將 func 使用在每一個 series 上,applymap() 則會將 func 使用在每一個 cell 上。
若針對 iterable 做數學運算或邏輯運算(例如:加減乘除、**、==)也是完全符合以上的規則,所以若一個 s = pandas.Series([1, 2, 3, 4]),我們執行以下動作「s == 3」,這一行 python 運算其實是指將 s 這一個 iterable 的每一個 item 拿出來進行「== 3」 的運算比較,並將每一個 item 的比較結果再組合回一個 iterable,所以「== 3」的運算比較會進行四次,其最後的結果會是一個 series,其值為 False, False, True, False。要記住 iterable 的運算都是 element-wise operation,s ==3 並不是在詢問 s 這個變數是否等於3,他是在問這個 iterable 的每一個 item 拿出來是否等於三。
在多半的時候我們空有表格卻無法下手分析資料,因此有一些步驟可以幫助我們逐漸地了解資料,慢慢探索資料的關係,這個過程稱為探索式的資料分析。以下有一些可議是著先試試看的 EDA 方法,不妨嘗試看看。不過首先你要知道你的資料是屬於哪一種類型的,我們先介紹「尺度 (scale)」的概念,再處理表格的資料探索。
Nominal Scale(名目尺度)、Categorical Scale(類別尺度):名目就是類別的意思。指的是某一欄資料裡面的值是分門別類的,例如:男女、是否、真偽、好壞,以上是二元類別的資料;也有多類別的例如:顏色、國家、語言。要注意的是類別沒有任何優劣關係,每一個類別不能互相比大小,他們只是單純的描述資料集在這一個欄位屬於哪一個類別。因此若要將名目尺度的資料繪製在座標平面上,安排先後順序是沒有實質意義的。因此使用圓餅圖、長條圖 (bar chart) 是比較適合顯示不同類別之間的比例關係。由於名目尺度不能比較大小,所以無法做數學運算,頂多計算眾數,千萬不要拿去算平均或是線性代數的運算。
Ordinal Scale(順序尺度):順序尺度和名目一樣是類別型的資料,但是各類別有是先定義好的順序,例如:大中小、教育程度、優良中劣。不過我們並不知道順序之間的差距是多少,只知道各類別的順序。原本名目尺度的圖形仍舊可以使用,但是由於可以排序了,因此中位數、各四分位差都可以計算。
Interval Scale(間距尺度):仍舊承繼名目以及順序尺度,但是現在每一個類別之間的差是可以量化的。有一部分的數值是落在這一個間距尺度,例如:溫度、年分、緯度。通常順序尺度可以依照資料使用者自訂的公式,把順序尺度對應到間距尺度上;間距尺度也可以自訂規則任意地做線性縮放。但有一點值得說明的是間距尺度的「零」是沒有意義的。回顧剛剛舉的例子,攝氏溫度零並不表示沒有溫度,西元零年並不表示沒有年分、緯度零度並不表示沒有緯度。這些間距尺度僅是表示一個等分的間距關係,數值任意平移或是縮放都沒有影響數值間的順序以及距離關係,所以加減法可以使用在資料上。在統計上除了上述的眾數、中位數、四分位差之外,平均數在這個尺度下才有意義,也可以計算全距了。我們通常在這種尺度下不會說攝氏一百度是攝氏五十度的兩倍,因為「零」沒有被定義,所以也不會有倍數關係,所以乘法和除法是不適用這種資料。
Ratio Scale(比率尺度):呈上,但這個尺度的「零」就有了絕對的意義。例如薪水、年齡、身高。絕大部分的物理量都落在這一個尺度裡面,分辨比率以及間距尺度的差別就是這個值的「零」是不是代表沒有。基本上所有的統計數值都可以使用在這個尺度上,例如:標準差、變異數。運算上加減乘除都可以使用。
Absolute Scale(絕對尺度):這是比較特化的尺度,數字具有絕對的意義,無法做其他有意義的轉換,基本上會遇到的就是機率或是分數,通常是沒有單位的數值。有時候時間也算在內,但我們常把時間當作比率尺度,把絕對尺度留給機率。
觀察資料首要先處理資料品質的問題,包含了資料遺失、異常資料、不一致資料、重複的資料、無法判別的資料。
由於很多建立模型的演算法只能處理純數字的運算,所以在真實世界收集到的資料往往需要進一步的 transform 成數字型態。在大部分的時候,其實數字也並不是直接拿來使用,例如身高和體重的範圍不太一樣,理論上身高這個數字要大得多,所以為了保持資料不會因為數值大小而影響演算法,通常即使已經是數值的資料也會進行前處理。處理通常是平移或縮放兩個步驟,比較常用的是把原先 x1 的數值換成 (x1-avg(x1))/(std(x1)),這個統計應該都學過,叫做標準化或是 z-score,把原值減去平均數在除以標準差。另外一種是 (x1-Xmin)/(Xmax-Xmin) 這個稱為 Feature Scaling 或 min-max normalization。當然也有其他更複雜的形式,不過這些目的都是將資料平移或縮放至一個可以相互比較的區間,以避免數值大小所帶來的偏誤。以 z-score 來說,其轉換後的數值平均值會落在 0,而數值與平均值的差異是以幾個標準差來表示的。
若是非數字型態的資料欄位,比較常使用的是稱為 one-hot encoding 的技巧。例如某一欄位是頭髮的顏色共有四色(黑、黃、棕、白),我們可以特別設置新的四個欄位在 X 裡面,若某筆資料髮色原為黑,那麼這四個欄位則標記為 [1, 0, 0, 0],如此我們可以把類別資料,轉換為數值資料。
另外,若 y 值也是類別型的資料,我們也常用 one-hot encoding 的方式來表示。
注意!有時同一份原始資料,但因為 encoding 的方法不同,最後做出來的模型好壞也常常會不同,所以可以多嘗試不同的 encoding 方式。
This page was last updated on 13 August 2018, at 23:00 (UTC+8).