10個資料科學家常犯的程式設計錯誤(附解決方案)

数据派THU發表於2019-05-24

本文為資深資料科學家常見的10個錯誤提供解決方案。

資料科學家是“比軟體工程師更擅長統計學,比統計學家更擅長軟體工程的人”。許多資料科學家都具有統計學背景,但是在軟體工程方面的經驗甚少。我是一名資深資料科學家,在Stackoverflow的python程式設計方面排名前1%,並與許多(初級)資料科學家共事。以下是我經常看到的10大常見錯誤,本文將為你相關解決方案:

  1. 不共享程式碼中引用的資料
  2. 對無法訪問的路徑進行硬編碼
  3. 將程式碼與資料混合
  4. 在Git中和原始碼一起提交資料
  5. 編寫函式而不是DAG
  6. 寫for迴圈
  7. 不編寫單元測試
  8. 不寫程式碼說明文件
  9. 將資料儲存為csv或pickle檔案
  10. 使用jupyter notebook

1. 不共享程式碼中引用的資料

資料科學需要程式碼和資料。因此,為了讓別人可以復現你的結果,他們需要能夠訪問到資料。道理很簡單,但是很多人忘記分享他們程式碼中的資料。

import pandas as pd

df1 = pd.read_csv('file-i-dont-have.csv') # fails

do_stuff(df)

解決方案:使用d6tpipe(https://github.com/d6t/ d6tpipe)來共享你的程式碼中的資料檔案、將其上傳到S3/web/google驅動等,或者儲存到資料庫,以便於別人可以檢索到檔案(但是不要將其新增到git,原因見下文)。

2. 對無法訪問的路徑進行硬編碼

與錯誤1相似,如果你對別人無法訪問的路徑進行硬編碼,他們將無法執行你的程式碼,並且必須仔細檢視程式碼來手動更改路徑。令人崩潰!

import pandas as pd

df = pd.read_csv('/path/i-dont/have/data.csv') # fails

do_stuff(df)

# or

import os

os.chdir('c:\\Users\\yourname\\desktop\\python') # fails

解決方案:使用相對路徑、全域性路徑配置變數或d6tpipe,使你的資料易於訪問。

d6tpipe:

https://github.com/d6t/d6tpip

3. 將程式碼與資料混合

既然資料科學的程式碼中包含資料,為什麼不把它們放到同一目錄中?那樣你還可以在其中儲存影像、報告和其他垃圾。哎呀,真是一團糟!

├── data.csv

├── ingest.py

├── other-data.csv

├── output.png

├── report.html

└── run.py

解決方案:將你的目錄進行分類,比如資料、報告、程式碼等。請參閱Cookiecutter Data Science或d6tflow專案模板[見#5],並使用#1中提到的工具來儲存和共享資料。

Cookiecutter Data Science:

https://drivendata.github.io/cookiecutter-data-science/

d6tflow專案模板:

https://github.com/d6t/d6tflow-templat

4. 在Git中和原始碼一起提交資料

現在,大多數人對他們的程式碼使用版本控制(如果你不使用,那就是另外一個錯誤,請參閱git:https://git-scm.com/)。在嘗試共享資料時,很容易將資料檔案新增到版本控制中。當檔案很小時是可以的,但是git並沒有針對資料進行最佳化,尤其是大檔案。

git add data.csv

解決方案:使用第1點中提到的工具來儲存和共享資料。如果你真的希望對資料進行版本控制,請參閱 d6tpipe,DVC和Git大檔案儲存。

d6tpipe:

https://github.com/d6t/d6tpipe

DVC:

https://dvc.org/

Git大檔案儲存:

https://git-lfs.github.com

5. 編寫函式而不是DAG

關於資料部分已經夠多了,現在來談一談實際的程式碼!在學習程式設計時最先學習的內容之一就是函式,資料科學程式碼通常由一系列線性執行的函式組成。

這會導致一些問題,請參閱“為什麼你的機器學習程式碼可能不好的4個原因”:

https://github.com/d6t/d6t-python/blob/master/blogs/reasons-why-bad-ml-code.rst

def process_data(data, parameter):

data = do_stuff(data)

data.to_pickle('data.pkl')


data = pd.read_csv('data.csv')

process_data(data)

df_train = pd.read_pickle(df_train)

model = sklearn.svm.SVC()

model.fit(df_train.iloc[:, :-1], df_train['y'])

解決方案:資料科學程式碼不是一系列線性連線的函式,而是一組具有依賴關係的任務集合。請使用d6tflow或airflow。

d6tflow:

https://github.com/d6t/d6tflow-template

airflow:

https://airflow.apache.org

6. 寫for迴圈

與函式類似,for迴圈也是你學習程式設計時最初學習的內容。它們易於理解,但是執行緩慢且過於冗長,通常意味著你不瞭解向量化的替代方案。

x = range(10)

avg = sum(x)/len(x); std = math.sqrt(sum((i-avg)**2 for i in x)/len(x));

zscore = [(i-avg)/std for x]

# should be: scipy.stats.zscore(x)

# or

groupavg = []

for i in df['g'].unique():

dfg = df[df[g']==i]

groupavg.append(dfg['g'].mean())

# should be: df.groupby('g').mean()

解決方案:Numpy,scipy和pandas為你需要for迴圈的情況提供了向量化函式。

Numpy:

http://www.numpy.org/

scipy:

https://www.scipy.org/

pandas:

https://pandas.pydata.org

7. 不編寫單元測試

隨著資料、引數或使用者輸入的改變,你的程式碼可能會出現問題,有時你並沒有注意到。這可能會導致糟糕的輸出結果,而如果有人基於你的輸出做出決策,那麼糟糕的資料將會導致糟糕的決策。

解決方案:使用assert語句來檢查資料質量。pandas有相等測試,d6tstack有資料提取檢查以及用於資料連線的d6tjoin。

pandas相等測試:

https://pandas.pydata.org/pandas-docs/stable/reference/general_utility_functions.html

d6tstack:

https://github.com/d6t/d6tstack

d6tjoin:

https://github.com/d6t/d6tjoin/blob/master/examples-prejoin.ipyn

以下是資料檢查的示例程式碼:

assert df['id'].unique().shape[0] == len(ids) # have data for all ids?

assert df.isna().sum()<0.9 # catch missing values

assert df.groupby(['g','date']).size().max() ==1 # no duplicate values/date?

assert d6tjoin.utils.PreJoin([df1,df2],['id','date']).is_all_matched() # all ids matched?

8. 不寫程式碼說明文件

我明白,你急著做出一些分析結果。你把事情彙總到一起分析,將結果交給你的客戶或老闆。一個星期之後,他們回來說,“可以把XXX改一下嗎”或者“可以更新一下這裡嗎”。你看著你的程式碼,但是並不記得你當初為什麼這麼寫。現在就像是在執行別人的程式碼。

def some_complicated_function(data):

data = data[data['column']!='wrong']

data = data.groupby('date').apply(lambda x: complicated_stuff(x))

data = data[data['value']<0.9]

return data

解決方案:即使在你已經提交分析報告後,也要花費額外的時間,來對你做的事情編寫說明文件。以後你會感謝自己,別人更會感謝你。那樣顯得你很專業!

9. 將資料儲存為csv或pickle檔案

回到資料,畢竟是在講資料科學。就像函式和for迴圈一樣,CSV和pickle檔案很常用,但是並不好用。CSV檔案不包含綱要(schema),因此每個人都必須再次解析數字和日期。Pickle檔案解決了這個問題,但是它只能在python中使用,並且不能壓縮。兩者都不是儲存大型資料集的最優格式。

def process_data(data, parameter):

data = do_stuff(data)

data.to_pickle('data.pkl')

data = pd.read_csv('data.csv')

process_data(data)

df_train = pd.read_pickle(df_train)

解決方案:使用parquet或其他帶有資料綱要的二進位制資料格式,在理想情況下可以壓縮資料。d6tflow將任務的資料輸出儲存為parquet,無需額外處理。

parquet:

https://github.com/dask/fastparquet

d6tflow:

https://github.com/d6t/d6tflow-template

10. 使用jupyter notebook

最後一個是頗有爭議的錯誤:jupyter notebook和csv檔案一樣普遍。許多人使用它們,但是這並不意味著它們很好。jupyter notebook助長了上述提到的許多不良程式設計習慣,尤其是:

把所有檔案儲存在一個目錄中

編寫從上至下執行的程式碼,而不是DAG

沒有對程式碼進行模組化

很難除錯

程式碼和輸出混在一個檔案中

沒有很好的版本控制

它容易上手,但是擴充套件性很差。

解決方案:使用pycharm和/或spyder。

pycharm:

https://www.jetbrains.com/pycharm/

spyder:

https://www.spyder-ide.org

作者簡介:Norman Niemer是一家大規模資產管理公司的首席資料科學家,他在其中釋出資料驅動的投資見解。他有哥倫比亞大學的金融工程專業理學碩士學位,和卡斯商學院(倫敦)的銀行與金融專業理學學士學位。

原文標題:

Top 10 Coding Mistakes Made by Data Scientists

原文連結:

https://github.com/d6t/d6t-python/blob/master/blogs/top10-mistakes-coding.md

THU資料派
THU資料派

THU資料派"基於清華,放眼世界",以紮實的理工功底闖蕩“資料江湖”。釋出全球大資料資訊,定期組織線下活動,分享前沿產業動態。瞭解清華大資料,敬請關注姐妹號“資料派THU”。

相關文章