(點選上方公號,快速關註我們)
英文:Srini Kadamati,轉自:資料派(ID:datapi),翻譯:梁傅淇
資料科學的完整流程一般包含以下幾個組成部分:
-
資料收集
-
資料清洗
-
資料探索和視覺化
-
統計或預測建模
雖然這些組成部分有助於我們理解資料科學的不同階段,但對於程式設計工作流並無助益。
通常而言,在同一個檔案中改寫完整的流程將會導致Jupyter Notebook、指令碼變成一團亂麻。此外,大多數的資料科學問題都要求我們在資料收集、資料清洗、資料探索、資料視覺化和統計/預測建模中切換。
但是還存在更好的方法來組織我們的程式碼!在這篇部落格中,我將介紹大多數人在做資料科學程式設計工作的時候切換的兩套思維樣式:原型思維樣式和生產流思維樣式。
原型思維樣式強調 |
生產流思維樣式強調 |
某部分程式碼的迭代速度 |
整體工作流程的迭代速度 |
更少的抽象 (直接修改程式碼和資料型別) |
更多的抽象 (修改引數) |
程式碼更鬆散 (模組化程度低) |
程式碼更結構化 (模組化程度高) |
幫助人們更瞭解程式碼和資料 |
幫助電腦更自動地執行程式碼 |
我個人使用JupyteLab來進行整個流程的操作(包括寫原型程式碼和生產流程式碼)。我建議至少使用JupyteLab來寫原型程式碼:
JupyteLab:
http://jupyterlab.readthedocs.io/en/stable/getting_started/installation.html
借貸俱樂部資料
為了更好地理解原型和生產流兩種思維樣式,我們來處理一些真實的資料。我們將使用個人對個人的借貸網站——借貸俱樂部上面的借貸資料。跟銀行不同,借貸俱樂部自身並不借錢,而是為貸款人提供一個市場以貸款給因不同的原因(比如維修、婚禮)需要借款的個人。
借貸俱樂部網站:
https://www.dataquest.io/blog/programming-best-practices-for-data-science/www.lendingclub.com
我們可以使用這個資料來建立一個模型,判斷一個給定的貸款申請是否會成功。這篇部落格中,我們不會深入到建立機器學習模型工作流。
借貸俱樂部提供關於成功的貸款(被借貸俱樂部和聯合貸款人透過的貸款)和失敗的貸款(被借貸俱樂部和聯合貸款人拒絕的貸款,款項並沒有轉手)的詳盡歷史資料。開啟他們的資料下載頁,選擇2007-2011年,並下載資料:
資料下載頁:
https://www.lendingclub.com/info/download-data.action
原型思維樣式
在原型思維樣式中,我們比較關心快速迭代,並嘗試瞭解資料中包含的特徵和事實。建立一個Jupyter Notebook,並增加一個Cell來解釋:
-
你為了更好地瞭解借貸俱樂部而做的所有調查
-
有關你下載的資料集的所有資訊
首先,讓我們將csv檔案讀入pandas:
import pandas as pd
loans_2007 = pd.read_csv(‘LoanStats3a.csv’)
loans_2007.head(2)
我們得到兩部分輸出,首先是一條警告資訊:
/home/srinify/anaconda3/envs/dq2/lib/python3.6/site-packages/IPython/core/interactiveshell.py:2785: DtypeWarning: Columns (0,1,2,3,4,7,13,18,24,25,27,28,29,30,31,32,34,36,37,38,39,40,41,42,43,44,46,47,49,50,51,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,123,124,125,126,127,128,129,130,131,132,133,134,135,136,142,143,144) have mixed types. Specify dtype option on import or set low_memory=False.
interactivity=interactivity, compiler=compiler, result=result)
然後是資料框的前5行,這裡我們就不展示了(太長了)。
警告資訊讓我們瞭解到如果我們在使用pandas.read_csv()的時候將low_memory引數設為False的話,資料框裡的每一列的型別將會被更好地記錄。
第二個輸出的問題就更大了,因為資料框記錄資料的方式存在著問題。JupyterLab有一個內建的終端,所以我們可以開啟終端並使用bash命令head來檢視原始檔案的頭兩行資料。
head -2 LoanStats3a.csv
原始的csv檔案第二行包含了我們所期望的列名,看起來像是第一行資料導致了資料框的格式問題:
Notes offered by Prospectus
https://www.lendingclub.com/info/prospectus.action
增加一個Cell來說明你的觀察,並增加一個code cell來處理觀察到的問題。
import pandas as pd
loans_2007 = pd.read_csv(‘LoanStats3a.csv’, skiprows=1, low_memory=False)
在借貸俱樂部下載頁檢視資料字典以瞭解哪些列沒有包含對特徵有用的資訊。Desc和url列很明顯就沒有太大的用處。
loans_2007 = loans_2007.drop([‘desc’, ‘url’],axis=1)
然後就是將超過一半以上都缺失值的列去掉,使用一個cell來探索哪一列符合這個標準,再使用另一個cell來刪除這些列。
loans_2007.isnull().sum()/len(loans_2007)
loans_2007 = loans_2007.dropna(thresh=half_count, axis=1)
因為我們使用Jupyter Notebook來記錄我們的想法和程式碼,所以實際上我們是依賴於環境(透過IPython核心)來記錄狀態的變化。這讓我們能夠自由地移動cell,重覆運行同一段程式碼,等等。
通常而言,原型思維樣式專註於:
-
可理解性
-
使用Markdown cell來記錄我們的觀察和假設
-
使用一小段程式碼來進行真實的邏輯操作
-
使用大量的視覺化和計數
-
抽象最小化
-
大部分的程式碼都不在函式中(更為面向物件)
我們會花時間來探索資料並且寫下我們採取哪些步驟來清洗資料,然後就切換到工作流樣式,並且將程式碼寫得更強壯一些。
生產流樣式
在生產流樣式,我們會專註於寫程式碼來統一處理更多的情況。比如,我們想要可以清洗來自借貸俱樂部的所有資料集的程式碼,那麼最好的辦法就是概括我們的程式碼,並且將它轉化為資料管道。資料管道是採用函式式程式設計
的原則來設計的,資料在函式中被修改,併在不同的函式之間傳遞:
函式式程式設計教程:
https://www.dataquest.io/blog/introduction-functional-programming-python/
這裡是資料管道的第一個版本,使用一個單獨的函式來封裝資料清洗程式碼。
import pandas as pd
def import_clean(file_list):
frames = []
for file in file_list:
loans = pd.read_csv(file, skiprows=1, low_memory=False)
loans = loans.drop([‘desc’, ‘url’], axis=1)
half_count = len(loans)/2
loans = loans.dropna(thresh=half_count, axis=1)
loans = loans.drop_duplicates()
# Drop first group of features
loans = loans.drop([“funded_amnt”, “funded_amnt_inv”, “grade”, “sub_grade”, “emp_title”, “issue_d”], axis=1)
# Drop second group of features
loans = loans.drop([“zip_code”, “out_prncp”, “out_prncp_inv”, “total_pymnt”, “total_pymnt_inv”, “total_rec_prncp”], axis=1)
# Drop third group of features
loans = loans.drop([“total_rec_int”, “total_rec_late_fee”, “recoveries”, “collection_recovery_fee”, “last_pymnt_d”, “last_pymnt_amnt”], axis=1)
frames.append(loans)
return frames
frames = import_clean([‘LoanStats3a.csv’])
在上面的程式碼中,我們將之前的程式碼抽象為一個函式。函式的輸入是一個檔案名的串列,輸出是一個資料框的串列。
普遍來說,生產流思維樣式專註於:
-
適合的抽象程度
-
程式碼應該被泛化以匹配的類似的資料源
-
程式碼不應該太過泛化以至於難以理解
-
管道穩定性
-
可依賴程度應該和程式碼執行的頻率相匹配(每天?每週?每月?)
在不同的思維樣式中切換
假設我們在執行函式處理所有來自借貸俱樂部的資料集的時候報錯了,部分潛在的原因如下:
-
不同的檔案當中列名存在差異
-
超過50%缺失值的列存在差異
-
資料框讀入檔案時,列的型別存在差異
在這種情況下,我們就要切換回原型樣式並且探索更多。如果我們確定我們的資料管道需要更為彈性化並且能夠處理資料特定的變體時,我們可以將我們的探索和管道的邏輯再結合到一起。
以下是我們調整函式以適應不同的刪除閾值的示例:
import pandas as pd
def import_clean(file_list, threshold=0.5):
frames = []
for file in file_list:
loans = pd.read_csv(file, skiprows=1, low_memory=False)
loans = loans.drop([‘desc’, ‘url’], axis=1)
threshold_count = len(loans)*threshold
loans = loans.dropna(thresh=half_count, axis=1)
loans = loans.drop_duplicates()
# Drop first group of features
loans = loans.drop([“funded_amnt”, “funded_amnt_inv”, “grade”, “sub_grade”, “emp_title”, “issue_d”], axis=1)
# Drop second group of features
loans = loans.drop([“zip_code”, “out_prncp”, “out_prncp_inv”, “total_pymnt”, “total_pymnt_inv”, “total_rec_prncp”], axis=1)
# Drop third group of features
loans = loans.drop([“total_rec_int”, “total_rec_late_fee”, “recoveries”, “collection_recovery_fee”, “last_pymnt_d”, “last_pymnt_amnt”], axis=1)
frames.append(loans)
return frames
frames = import_clean([‘LoanStats3a.csv’], threshold=0.7)
預設值是0.5,如果需要的話,我們也可以改為0.7。
這是一些將管道改得更為彈性的方式,按推薦程度降序排列:
-
使用可選引數、位置引數和必需引數
-
在函式中使用if / then陳述句以及使用布林輸入值作為函式的輸入
-
使用新的資料結構(字典,串列等)來表示特定資料集的自定義操作
這個管道可以擴充套件到資料科學工作流程的所有階段。這是一些偽程式碼,可以總攬它的結構。
import pandas as pd
def import_clean(file_list, threshold=0.5):
## Code
def visualize(df_list):
# Find the most important features and generate pairwise scatter plots
# Display visualizations and write to file.
plt.savefig(“scatter_plots.png”)
def combine(df_list):
# Combine dataframes and generate train and test sets
# Drop features all dataframes don’t share
# Return both train and test dataframes
return train,test
def train(train_df):
# Train model
return model
def validate(train_df, test-df):
# K-fold cross validation
# Return metrics dictionary
return metrics_dict
frames = import_clean([‘LoanStats3a.csv’, ‘LoanStats2012.csv’], threshold=0.7)
visualize(frames)
train_df, test_df = combine(frames)
model = train(train_df)
metrics = test(train_df, test_df)
print(metrics)
下一步
如果你對加深理解和練習感興趣的話,我推薦:
瞭解如何將你的管道轉化為作為一個模組或者從命令列中單獨執行的指令碼:
https://docs.python.org/3/library/main.html
瞭解如何使用Luigi來構建更複雜的、能夠在雲上面執行的管道
https://marcobonzanini.com/2015/10/24/building-data-pipelines-with-python-and-luigi/
瞭解更多有關資料工程的資訊:
https://www.dataquest.io/blog/tag/data-engineering/
【關於投稿】
如果大家有原創好文投稿,請直接給公號傳送留言。
① 留言格式:
【投稿】+《 文章標題》+ 文章連結
② 示例:
【投稿】《不要自稱是程式員,我十多年的 IT 職場總結》:http://blog.jobbole.com/94148/
③ 最後請附上您的個人簡介哈~
看完本文有收穫?請轉發分享給更多人
關註「資料分析與開發」,提升資料技能