Python 在資料科學領域越來越流行了。它的流行不無道理。 Python 容易學,有超強資料科學庫,並且和 Hadoop 以及 Spark 等資料庫和工具整合得非常好。Python 可以從頭至尾完成一個資料科學專案,無論是讀取資料、分析資料、資料視覺化還是用機器學習來做預測都可以。
本文介紹如何用 Python 上手資料科學。如果想要了解更多內容請訪問 Dataquest, 那裡有使用 Python 完成資料科學任務的深入講解。
本文使用的是關於 2016 美國總統大選政治資助的資料集(連結 在此)。檔案是 csv 格式,每行代表對一個候選人的一次捐贈。這個資料集有幾列比較值得一提,比如:
cand_nm
– 接受捐贈的候選人姓名contbr_nm
– 捐贈人姓名contbr_state
– 捐贈人所在州contbr_employer
– 捐贈人所在公司contbr_occupation
– 捐贈人職業contb_receipt_amount
– 捐贈數額(美元)contb_receipt_dt
– 收到捐款的日期
安裝 Python
要分析這些資料,首先要安裝 Python。利用 Anaconda 這個工具來安裝 Python 是非常簡單的。Anaconda 在安裝 Python 的同時還會安裝一些流行的資料分析庫。點選 這裡 下載 Anaconda。建議安裝最新的 Python 3.5 版本。這個連結 裡介紹了一些 Python 2 與 Python 3 的對比。
Anaconda 會自動安裝一些這篇文章會用到的庫,包括 Jupyter、Pandas、scikit-learn 和 matplotlib。
Jupyter 入門
都安裝好之後可以啟動 Jupyter notebook (原名 IPython notebook)。Jupyter notebook 是個強有力的資料分析工具。它能夠幫助快速檢視資料、將結果視覺化以及把結果分享給他人。谷歌、IBM、微軟的資料科學家都用它來分析資料以及組內協作。
在命令列裡輸入 ipython notebook
來執行 Jupyter。如果遇到問題可以去它的 官方文件 裡查詢答案。
啟動後會出現一個可以檢視檔案的瀏覽器介面,在這個頁面上可以建立新的 notebook。請建立一個叫 Python 3 的 notebook,一會兒的資料分析中會用到它。如果剛才的安裝還沒成功,這篇文章 也許有幫助。
Notebook 工作區塊
每個 Jupyter notebook 都包含多個區塊(cell),區塊可以執行程式碼也可以只包含文件。每個 notebook 開始時都自帶一個區塊,如有需要可以自行增加多個區塊,比如:
1 2 3 4 |
# 程式碼區塊示例。產生的輸出會在下方顯示。 print(10) b = 10 |
1 2 3 4 |
# 可以建立多個區塊,每個程式碼區塊可以根據分析的需求跑任意次 # Jupyter notebook 中一個很讚的功能是每個區塊跑出來的結果會被快取起來, 這樣一個區塊可以利用另一個區塊跑出來的結果。 print(b * 10) |
如果想要了解更多關於 Jupyter 的知識請閱讀作者提供的更深入的 教程。
Pandas 入門
Pandas 是 Python 上的一個資料分析庫。它能讀取包括 csv 在內的不同格式的資料,分析資料也很有效。可以通過下面的程式碼來讀取資料:
1 2 3 |
import pandas as pd donations = pd.read_csv("political_donations.csv") |
1 |
donations.shape |
1 |
(384885, 18) |
1 |
donations.head(2) |
上面的區塊用 import pandas as pd
這個語句匯入了 Pandas 庫,然後用 read_csv() 這個函式把 political_donations.csv
這個檔案讀入了變數 donations
中。變數 donations
現在就是一個 Pandas DataFrame。Pandas DataFrame 可以被看做是加強版的矩陣,它自帶資料分析函式,並且允許不同的列包含不同的資料型別。
可以通過變數 donations
的 shape
屬性來列印它多少行多少列。每個區塊的最後一行語句或變數都會自動顯示,這個功能超讚!下一個區塊用了 DataFrames 的 head() 函式列印出了變數 donations
的頭兩行,這樣就能看裡面的資料了。
如想更深入地瞭解 Pandas 請參閱作者提供的 課程。
每個候選人收到的捐款總額
使用Pandas 中的 groupby() 函式能計算出每個候選人的整體統計資料。根據變數 cand_nm
(候選人姓名)來把變數 donations
分成不同的子集就可以針對每個候選人分別統計資料。首先要算的是捐款總額。把候選人的 contb_receipt_amount
這一列加起來就可以得到收到的捐款總額了。
1 |
donations.groupby("cand_nm").sum().sort("contb_receipt_amt") |
上面的程式碼首先用donations.groupby("cand_nm")
根據cand_nm
把donations
分成了不同的組。這個語句返回的是 GroupBy 物件,GroupBy 型別自帶一些專門用來整合資料的函式。其中就包含sum()
函式,在這個問題中可以用來計算每組中每一列中資料的和。
Pandas 在讀取資料的時候就會自動識別每一列的資料型別,在進行求和時只會針對數字型別的列來操作。這樣就得到了一個包含每個候選人contb_receipt_amt
列中所有數字之和及file_num
列中所有數字之和的 DataFrame。最後使用 DataFrames 中的 sort() 函式將contb_receipt_amt
的和從小到大排序。這樣就得到了每個候選人收到的捐款總額。
將捐款總額視覺化
Python 中最主要的視覺化包就是 matplotlib,可以用它來畫圖。Jupyter notebook 能夠在瀏覽器中直接渲染 matplotlib 的圖表。這個功能需要通過啟用 matplotlib 的 inline 模式來開啟。可以利用 Jupyter magics 中的命令來啟用它就能直接在 notebook 中看圖表了。
Magics 就是以 %
或者 %%
開頭的、能改變 Jupyter notebook 本身的命令。它們是為了讓能夠通過命令列改變 Jupyter 的設定,同時儘量不與 Python 程式碼混淆而存在的。要想在瀏覽器裡直接看 matplotlib 的圖表,需要在程式碼區塊裡執行 %matplotlib inline
。更多關於用 Jupyter 畫圖的內容可以 在此 閱讀。
用下面的程式碼來匯入 matplotlib
庫並且開啟 inline 模式:
1 2 3 |
import matplotlib.pyplot as plt %matplotlib inline |
Pandas 中的 DataFrames 自帶對視覺化的支援,呼叫 plot() 函式就可以生成 matplotlib
圖表。這麼用一般會比呼叫 matplotlib
更方便快捷。先給之前的 DataFrame 賦值給一個變數 total_donations,
再利用 indexing 來選擇 DataFrame 中的一列: contb_receipt_amt
。這樣就生成了一個 Pandas 中的 Series 型別的變數。
Pandas 中的 Series 和 DataFrames 包含的函式都差不多,但是 Series 只能存一維資料,比如單一行或者單一列。呼叫 Series 的 plot() 函式就生成了一個顯示每個候選人收到的捐款總額的柱狀圖。
1 |
total_donations = donations.groupby("cand_nm").sum().sort("contb_receipt_amt") |
1 |
total_donations["contb_receipt_amt"].plot(kind="bar") |
1 |
<matplotlib.axes._subplots.AxesSubplot at 0x108892208> |
計算捐款平均值
已經學會算捐款總額啦,再想算捐款平均值超級容易。直接用求平均值的 mean() 函式來替換求和用的 sum()
函式就得了。
1 2 |
avg_donations = donations.groupby("cand_nm").mean().sort("contb_receipt_amt") avg_donations["contb_receipt_amt"].plot(kind="bar") |
1 |
<matplotlib.axes._subplots.AxesSubplot at 0x108d82c50> |
預測捐款數目
下面來寫個簡單的根據一個人所在的州(contbr_st
)、職業(contbr_occupation
)及支援的候選人(cand_nm
)來預測捐款數額的簡單演算法吧。首先用這幾列及要預測的 contb_receipt_amt
列來另外建立一個 Dataframe。
1 |
pdonations = donations[["contbr_st", "contbr_occupation", "cand_nm", "contb_receipt_amt"]] |
下面來看看變數 pdonations
裡每一列的資料型別。Pandas 讀取 csv 檔案時會自動給每列賦予資料型別。只有數值型(numeric)的列才能用來做預測。
1 |
pdonations.dtypes |
1 2 3 4 5 |
contbr_st object contbr_occupation object cand_nm object contb_receipt_amt float64 dtype: object |
倒黴的是想要用的列都是 object 型的(都是字串)。這是因為它們都是分類資料(categorical data)。每列中有幾個可能的值,但這些選項是用文字來表示的而不是用數值型程式碼來表示的。可以先把每列都轉換成分型別(categorical),然後再轉換成數值型。這裡 有關於分型別資料的更多介紹。本質上就是分型別資料在後臺給一列中每個不同的值賦予了一個不同的數值型代號。可以將一列種的值都換成這些代號,這樣一列就完全被轉換成數值型的了。
1 2 |
pdonations["contbr_st"] = pdonations["contbr_st"].astype('category') pdonations["contbr_st"] = pdonations["contbr_st"].cat.codes |
1 |
pdonations["contbr_st"] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
0 1 1 1 2 1 3 2 4 2 5 2 6 2 7 2 8 2 9 2 10 2 11 2 12 2 13 2 14 2 ... 384870 75 384871 75 384872 75 384873 75 384874 75 384875 75 384876 75 384877 75 384878 75 384879 75 384880 75 384881 75 384882 75 384883 77 384884 77 Name: contbr_st, Length: 384885, dtype: int8 |
可以看到 contbr_st
列已經被轉換成數值型的了。下面對 contbr_occupation
及 cand_nm
兩列也進行同樣的操作。
1 2 3 |
for column in ["contbr_st", "contbr_occupation", "cand_nm"]: pdonations[column] = pdonations[column].astype('category') pdonations[column] = pdonations[column].cat.codes |
訓練集和測試集的拆分
接下來的預測步驟中可以利用到 Python 中最主要的機器學習包 scikit-learn。首先要把資料拆分成兩個部分。一部分用於訓練演算法,稱為訓練集;另一部分用於評估模型的效果,稱為測試集。這樣做是為了避免過擬合(overfitting)產生的有誤導性的結果。
用 train_test_split() 這個函式可以將 pdonations
拆分成一個訓練集和一個測試集。
1 2 3 |
from sklearn.cross_validation import train_test_split train, test, y_train, y_test = train_test_split(pdonations[["contbr_st", "contbr_occupation", "cand_nm"]], pdonations["contb_receipt_amt"], test_size=0.33, random_state=1) |
上面的程式碼將訓練演算法需要用的列及結果列(contb_receipt_amt)中的值分成了訓練集和測試集。測試集中包含33%的資料。每行資料被隨機分配到訓練集中或者測試集中。
擬合模型
下面會使用隨機森林(random forest)演算法來做預測。隨機森林是一個效果比較好並且適用於很多問題的演算法,在 scikit-learn 包中是通過 RandomForestRegressor 類來實現的。使用這個類訓練模型及用模型做預測都很簡單。
首先用 train
和 y_train
來訓練模型:
1 2 3 4 5 |
from sklearn.ensemble import RandomForestRegressor model = RandomForestRegressor(n_estimators=100, min_samples_leaf=10) model.fit(train, y_train) |
1 2 3 4 5 |
RandomForestRegressor(bootstrap=True, compute_importances=None, criterion='mse', max_depth=None, max_features='auto', max_leaf_nodes=None, min_density=None, min_samples_leaf=10, min_samples_split=2, n_estimators=100, n_jobs=1, oob_score=False, random_state=None, verbose=0) |
scikit-learn 包一個優點是裡面所有演算法都有一致的 API。訓練一個線性規劃(linear regression)模型和訓練一個隨機森林模型用的方法是一模一樣的。有了合適的模型就可以用它來做預測了。
預測及誤差計算
用 scikit-learn 包做預測也非常簡單。直接把測試集傳給訓練好的模型就行了。
1 |
predictions = model.predict(test) |
有了預測結果之後來算算誤差值。誤差能體現模型的效果,在調整模型時也能作為一個衡量標準。下面會用一個常見的誤差標準,均方誤差(mean squared error)。
1 2 3 4 |
from sklearn.metrics import mean_squared_error import math mean_squared_error(predictions, y_test) |
1 |
756188.21680533944 |
如果想了解更多關於 scikit-learn 的知識可以閱讀作者撰寫的 教程。
接下來做點什麼
對誤差求平方根得到的值和捐款額之間的關係更直觀。如果不求平方根而只用平均方差 (average squared error),那它就和上面用的資料沒什麼直接關係。無論怎麼算目前的誤差值都很大,有很多減小誤差的方法,比如:
- 利用上其他列中的資料
- 看看是否對每個候選人訓練一個模型效果會更好
- 嘗試用其他演算法
還有一些有意思的對資料的探索可以做,比如:
- 找出每個州哪個候選人得到的捐款最多
- 畫出對每個候選人來說,來自哪種職業的人捐的錢最多的圖
- 根據候選人是民主黨還是共和黨劃分,看看是否會有有意思的模式出現
- 通過名字給資料新增性別,看看如果根據性別劃分資料是否會顯現出有意思的模式
- 根據美國不同地區的捐款總額畫一個熱圖(heatmap)
想要深入瞭解本文講解到的概念,請參閱作者提供的 Python 資料科學 課程。
打賞支援我翻譯更多好文章,謝謝!
打賞譯者
打賞支援我翻譯更多好文章,謝謝!