數據科學案例分析

數據科學案例分析

1. 引言

在你閱讀這個帖子的第一句話時,全世界已經生成了 TB 級的數據 (1 TB = 1024 GB)。為了應付這種大規模的數據流入,數據科學 (data science) 領域在過去十年中佔據了前列。 數據科學由不同領域 - 統計學,物理學,計算機科學,以及更多領域 - 融合在一起,數據科學家職業也被哈佛商業評論 (Harvard Business Review) 評為 21 世紀最性感的工作。

本貼用一個 Python 數據分析流程,展示典型的數據科學工作流程。整個流程是在 Jupyter 上完成 (Jupyter 誕生於 2014年,它支持所有編程語言的互動式數據科學和科學計算)。

【加微信公眾號 MeanMachine1031,在對話框回復 DS1 可下載代碼 (ipython notebook格式) 和數據 (csv格式)】

2. Python 安裝

如果你的計算機上沒有 Python,可以使用 Anaconda Python 發行版來安裝你需要的大部分 Python 包。主頁網址 (點我),網頁如下圖所示:

此外,本文需要使用了幾個 Python 的包是:

  • numpy: 提供快速數字數組結構和輔助函數。
  • pandas: 提供一個數據表結構並能高效地處理數據。

  • scikit-learn: 用於機器學習。

  • matplotlib: 用於基本繪圖。

  • seaborn: 用於高級統計繪圖。

要確保你具有所需的所有軟體包,在終端窗口 (terminal window) 使用 conda 或者 pip 安裝它們:

3. 案例分析

假設我們要創建一個智能手機應用程序,從智能手機拍攝的照片中自動識別花的種類。 我們正在與一個數據科學家團隊合作,該數據科學主管負責創建一個演示機器學習模型,測量花的萼片長度 (sepal length),萼片寬度 (sepal width),花瓣長度 (petal length) 和花瓣寬度 (petal width) 四個變數,並根據這些測量識別物種。

等等,萼片是什麼鬼?萼片是花的最外一環。下圖清晰指出花的萼片和花瓣。

我們已經從現場研究人員獲得了一個數據集,裡面包括三種類型的鳶尾花的測量,如下圖:

根據當地研究人員測量的每種鳶尾花的四個數據 (萼片長/寬和花瓣長/寬),我們最終目的是想正確的分類這三種花。但重中之重的第一步是數據處理。有了乾淨的數據用來機器學習很容易

  • Python 愛好者可以用 scikit-learn
  • R 愛好者可以用 Comprehensive R Archive Network (CRAN)

  • Matlab 愛好者可以用 Machine Learning Toolbox (MTL)

但怎麼處理數據有時候更像一門藝術而不像一門科學。在下一節我會從「回答問題」,「檢查數據」,「清理數據」和「測試數據」幾個方面來探索。

4. 數據處理

4.1 回答問題

任何數據分析項目的第一步就是提出想要解決的問題,並為成功解決該問題而定義一個度量 (還記得機器學習第一帖里的性能度量這個概念嗎)。一些常見的問題如下:

  • 在查看數據之前是否了解數據分析問題的類型,是回歸,分類還是聚類問題?(明晰問題本質)
    • 這是個根據萼片長度,萼片寬度,花瓣長度和花瓣寬度四個測量指標的分類問題
  • 是否在一開始就定義了成功的度量?(設定量化指標)

    • 因為是分類問題,所以可以使用查准率,即正確分類花的百分比,來量化模型的表現。我們的數據主管告訴我們應該實現 90% 的準確性。
  • 現有數據是否解決分類問題?(了解數據局限性)

    • 我們目前的數據集只有三種類型的鳶尾花。從這個數據集建立的模型將只適用於那些鳶尾花,未來創建一個通用的花分類器需要更多的數據。

請注意,我們提出這些問題時沒有編寫一行代碼甚至查看一行數據。思考這些問題執行有效數據分析的重要一步,它往往被忽視,希望你們不要。

4.2 檢查數據

即便是政府或銀行,他們公布的數據也有錯誤。在花費太多時間分析數據之前,提早檢查並修正這些錯誤能節省大量時間。一般來說,我們希望回答以下問題:

  • 數據格式有什麼問題嗎?
  • 數據數值有什麼問題嗎?

  • 數據需要修復或刪除嗎?

接下來用 python 在 Jupyter Notebook 里展示如何檢查和修正數據。

首先引進 python 裡面的幾個包,numpy 是為了做數學運算,pandas 是為了處理數據,matplotlib 是為了畫圖,seaborn 是為了畫高級圖。代碼如下圖:

"import A as B" 這個格式是說引進 A 包用假名 B 來代替,為什麼用假名呢?你看看上圖裡假名 B 是不是比實名 A 要簡單。當你要想用 A 包裡面的函數 f,你可以寫成 A.f 或 B.f,但顯然寫成 B.f 使得代碼便於讀寫!Python 程序員喜歡用 np, pd 和 sb 來代替 numpy, pandas 和 seaborn,當然你有自由定你的 B。假名 B 除了可以代替一個包,還可以代替一個包里的函數,比如 plt 通常代替 matplotlib 包里的 pyplot 函數。

檢查點 1. 數據格式 (format)

首先用 pandas 讀取 csv 文件並將數據存成數據表 (data frame) 格式。

  1. read_csv( filename, na_values = [NaN] ) 是 pandas 裡面用來從 csv 文件讀取數據的函數。裡面用到的兩個參數,第一個 filename 是讀取 csv 文件名,第二個參數用來把 csv 裡面空白處用 NaN 代替。此行代碼將 csv 里的數據轉成 pandas 里的數據表,命名為 iris_data。

  2. 讀取完可用 head(10) 和 tail() 函數來看前 10 個數據和後 5 個數據,如果括弧裡面沒有指定參數,默認為 5。

從上圖可知,數據似乎是存成可用的格式 (還記得大神 Hadley Wickhan 怎麼定義乾淨數據嗎?每一列代表一個變數;每一行代表一個記錄 )。

  • 數據的第一行定義了列標題,標題的描述足以讓我們了解每個列代表的內容 (萼片長度,萼片寬度,花瓣長度和花瓣寬度),標題甚至給我們記錄測量的單位 (cm, 厘米)
  • 第一行之後的每一行代表一個花的觀測數據:四個測量指標和一個類 (class),它告訴我們花的種類。比如前 10 個都是山鳶尾花 (注意第 8 到 10 個的花瓣寬度沒有數據,用 NaN 來表示),而後 5 個都是維吉尼亞鳶尾花。

檢查點 2. 數據統計 (statistics)

接下來,檢查數據的分布可以識別異常值。我們從數據集的匯總統計數據開始。

  1. describe() 函數的產出每列數據的個數 (count),均值 (mean),標準差 (std),最小值 (min),25, 50 和 75 百分位數 (25%, 50%, 75%) 和最大值 (max)。50 百分位數也就是中位數 (median)。

  2. 從該表中看到幾個有用的值。 例如,我們看到缺少 5 條花瓣寬度的數據(表裡 count 那一行的萼片長度,萼片寬度和花瓣長度的個數都是 150 個,唯獨花瓣寬度是 145 個)。

此外,這樣的表給不了太多有用信息,除非我們知道數據應該在一個特定的範圍 (如萼片長度的最小值是 0.055, 和它其他指標如均值和幾個百分位數都不是一個數量級的,很有可能是測量錯誤)。

比起一串枯燥的數值,我更喜歡絢爛的繪圖。因此我喜歡可視化數據,它能使異常值立即脫穎而出。

接下來我們用 pairplot 函數創建一個散點矩陣圖,函數里

  • 第一個參數 iris_data.dropna() 就是除去 NaN 的數據表,這麼做原因很簡單,圖裡不可能顯示的出 NaN 值的。
  • 第二個參數 hue = class 就是根據類 (class) 下不同的值賦予不同的顏色 (hue 就是色彩的意思) 。

讓我們再回顧一下 iris_data 的樣子:

它有 5 列,前四列 (萼片長度,萼片寬度,花瓣長度和花瓣寬度) 可看成自變數,第五列 (類) 可看成變數。散點矩陣圖繪製前四列變數的相關係數圖,而且用不同顏色區分不同的類下面的這四個變數。 從上圖可知,橫軸縱軸都有四個變數,那麼總共可以畫出 16 (4*4) 張小圖。

  • 對角線上的 4 張都是某個變數和自己本身的關係,由於自己和自己的相關係數永遠是 1,畫出相關係數圖意義不大,因此這 4 張都是堆棧條形圖 (stacked bar chart)。這種圖是用於分解和比較整體和部分的圖,說白了就是不同類都對應著一個條形圖 (bar chart),堆棧條形圖就是將所有條形圖加總。

  • 非對角線的 12 張就是某個變數和另一個變數的關係。比如第一行第二列的圖描述的就是萼片長度 (看縱軸第一個 sepal_length_cm 字樣) 和萼片寬度 (看橫軸第二個 sepal_width_cm 字樣)。

從散點矩陣圖中,我們可以迅速看出數據集的一些問題:

  1. 圖的右側標註這五個類 (Iris-setosa, Iris-setossa, Iris-versicolor, versicolor, Iris-virginica),但原本要分類的花只有三類 (Iris-setosa, Iris-versicolor, Iris-virginica)。這意味著在記錄數據時可能會犯下一些錯誤。
  2. 在測量中有一些明顯的異常值可能是錯誤的。
  • 例如第一行後三張小圖,對於 Iris-setosa (山鳶尾花,藍點),一個萼片寬度值落在其正常範圍之外
  • 例如第二行第一,三,四張小圖,對於 Iris-versicolor (變色鳶尾花,紅點) ,幾個萼片長度值都接近零。

下一步我們的任務是要處理錯誤的數據。

4.3 清理數據

修正點 1. 數據類 (class)

問題:按理應該只有三個類,圖中卻顯示五個。

在與現場研究人員交談後,聽起來像是一位研究員忘記在 Iris-versicolor 之前添加 Iris-。另一個類 Iris-setossa 他們只是多打了一個 s。讓我們使用代碼來修復這些錯誤。

首先用 unique() 函數 (unique 有唯一不重複的意思) 來看 iris_data 裡面不重複的類名稱,發現裡面有五種,分別是 Iris-setosa, Iris-setossa, Iris-versicolor, versicolor 和 Iris-virginica (和散點矩陣圖顯示的一樣)。

接著用數據表裡的 loc[] 函數 (loc 是 location 的縮寫,有定位之意) 來找到類值為 versicolor 的所有行,被將其類的值更新為 Iris-versicolr;同理找到類值為 Iris-setossa 的所有行,被將其類的值更新為 Iris-setosa。再用 unique() 函數檢驗修改過的數據是不是只有三類,更新後的散點矩陣圖如下:

完美!只有三個類而分別是 Iris-setosa, Iris-versicolor 和 Iris-virginica。

修正點 2. 異常數值 (outliers)

修復異常值是一件棘手的事情。因為我們很難判斷異常值是否由測量誤差引起,或者是不正確的單位記錄數據,或者是真正的異常。如果我們決定排除任何數據,需要記錄排除的數據並提供排除該數據的充分理由。由上節所知,我們有兩種類型的異常值。

問題 1:山鳶尾花的一個萼片寬度值落在其正常範圍之外 (黑色圓框)。

我們的研究人員知道,山鳶尾花 (Iris-setosa) 的萼片寬度 (sepal_width_cm) 不可能低於 2.5 厘米。顯然,這個記錄是錯誤的,這種情況下最有效的方法是刪除它而不是花時間查找原因。但是,我們仍需要知道有多少個類似這樣的錯誤數據,如果很少刪除它沒有問題,如果很多我們需要查明原因。

第一行代碼是用數據表裡的 loc[] 函數來找到類值為 Iris-setoa (因為是藍點) 並且 sepal width 小於 2.5 的所有行。最後發現只有一個這樣的數據,而上圖的條形圖也確認了這樣的異常值只有一個。因此可以直接刪除此數據。

第一行代碼將類值不是 Iris-setosa 並且 sepal width 大於 2.5 的所有數據都新存到 iris_data 中。從上麵條形圖也看到了再沒有這個異常值。

完美! 現在所有的山鳶尾花的萼片寬度都大於 2.5 厘米。

問題 2:變色鳶尾花的幾個萼片長度值接近與零 (黑色橢圓框)。

所有這些接近零的 sepal_length_cm 似乎錯位了兩個數量級,好像它們的記錄單位米而不是厘米。在與實地研究人員進行了一些簡短的對話後,我們發現其中一個人忘記將這些測量值轉換為厘米。讓我們使用代碼來修復這些錯誤。

第一行代碼是用數據表裡的 loc[] 函數來找到類值為 Iris-versicolor (因為是紅點) 並且 sepal length 接近零的所有行,發現有五個數據,而條形圖最左邊顯示的數據個數也確認了是五個。

第一行代碼將類值為 Iris-versicolor 並且 sepal length 都小於 1 的所有數據乘以 100 (將米轉成厘米)。從上麵條形圖也看到了再沒有這五個異常值。

完美! 我們成功修正了這些異常值,要不然我們的分析結果可能毫無意義。

修正點 3. 缺失數值 (missing value)

對了,我們還有些 NaN 數據。通常我們有兩種方式來處理這類數據。

  1. 刪除 (deletion)
  2. 插補 (imputation)

在本例中刪除不是理想的做法,特別是考慮到它們都在 Iris-setosa 下,如圖:

所有缺失的值都屬於 Iris-setosa類,直接刪除可能會對日後數據分析帶來偏差。此外,可以用插補方法,其最常見的方法平均插補 (mean imputation)。其做法就是「假設知道測量的值落在一定範圍內,就可以用該測量的平均值填充空值」。

上面代碼裡面 iris_data[A].isnull() 語句是找出 A列中值為 "NA", "NaN" 的行數據,而 "|" 是「或」的意思,因此上面整句話是找到萼片長度,萼片寬度,花瓣長度和花瓣寬度這四列下的所有含 NaN 的行數據,最後發現只有 5 個,而且 NaN 都來自花瓣寬度。

接下來用 hist() 函數畫出 Iris-setosa 花瓣寬度的條形圖,可以清楚看到大多數寬度在 0.25 左右。

然後用 mean() 準確求出其寬度的平均值,將其 NaN 值全部用平均值代替,最後打出那 5 行插補後的數據表。

為了確保所有 NaN 值已被替換,再次用 iris_data[A].isnull() 語句來查看,出來的結果是一個只有列標題的空數據表。這表示表內已經沒有 NaN 值了。

完美!完美!完美!

4.4 測試數據

存儲數據 (save data)

經過所有這些艱苦的工作,我們不想在每次使用數據集時都重複這個過程。讓我們將整潔的數據文件保存為一個新文件,日後做數據分析可以直接從該文件開始。

第一行代碼 to_csv() 函數是把整理後得數據寫道一個名叫 iris-data-clean 的 csv 文件里,index = False 意思是不要記錄在csv 里記錄行數。第二行代碼重新讀取剛寫好的 csv 並存成名為 iris_data_clean 的數據表。

讓我們再看看基於乾淨數據畫的散點矩陣圖吧

從上圖可看到:

  • 五個類變成三個類。
  • 異常值全部刪除或修正了。

整個圖看起來整潔多了。

聲明數據 (assert data)

為了防止一些數據問題沒有解決,我們可以用 assert 語句來做聲明。該語句好處是,在運行時如果聲明語句為真,沒有任何事發生,反之會報錯而警告我們有哪些錯誤數據需要注意且修正。

上述三個聲明分別是:

  1. 花應該只有三個類型。
  2. 變色鳶尾花的萼片長度應該大於 2.5 厘米。

  3. 數據不應該有缺失。

如果任何聲明被違反,我們應該立即停止分析,而回到整理階段。

5. 小結

小貼士:

  1. 確保數據在預期範圍內,並儘可能使常識來定義預期範圍處理丟失的數據
  2. 根據情況刪除它或者更換它

  3. 永遠不要手動清理數據,數據量一大你會發現你根本無法複製此過程

  4. 使用代碼來記錄如何整理數據的過程,Jupyter Notebook 真的是個很好的工具

  5. 將數據可視化,和字元相比,圖永遠更直觀,也是更容易發現數據問題

6. 彩蛋

電影都有彩蛋,帖子為什麼不能有?本彩蛋只想證實有了乾淨數據,用 scikit-learn 來機器學習不要太容易。不信?下圖文饗你!

要把大象放冰箱總共分幾步?三步!

  1. 把冰箱門打開
  2. 把大象放進去

  3. 把冰箱門關上

完了要掉粉了,言歸正傳。碰巧的是有了乾淨數據後,機器學習也分三步:

1. 選出特徵 (輸入變數) 和標記 (輸出變數)

all_input 是特徵,all_classes 是標記。有標記那麼此分類問題是監督學習。

2. 劃分訓練集和測試集

用 75% 和 25% 的比例來劃分訓練集和測試集,設一個 random_state 是為了在機器學習中每次出的結果一樣,便於找問題。

3. 用模型來學習

再一次感嘆 scikit-learn 的強大和好用,三行代碼解決分類問題了。第一行創建一個決策樹分類器變數。第二行用 fit() 函數在訓練集上學習。第三行用 score() 在測試集上評估。

噢耶!好像沒費什麼力氣,我們的查准率就達到了 97%!遠高於主管給我們的 90% 要求。

這時候有些機器學習專家會說了:

  • 這個訓練集和測試集劃分是隨機的,一次不足以說明你查准率高。
  • 這數據太少沒代表性,換套數據模型可能過擬合。

  • 特徵選擇有很大學問,憑什麼就選萼片長度,萼片寬度,花瓣長度和花瓣寬度這四個變數?

是的,你說的這些都是 legit 的,但是本文核心是想說數據清理後機器學習會更容易,我只是舉個例子。再者,我此貼把上面的問題全解決了後面的帖子怎麼出?跟著我繼續在數據科學和機器學習中繼續探索咯!Stay Tuned!
推薦閱讀:

LDA模型的前世今生
身為數據科學家怎麼能不掌握這四大技能!
R語言兵器譜:數據科學家的十八般武藝
粗略學習Metro Map to Data Scientist(數據科學家之路)
業務分析師和數據科學家有什麼不同(轉載)

TAG:數據可視化 | 數據科學家 |