獨家 | 10個數據科學家常犯的編程錯誤(附解決方案)

數據科學家是「比軟件工程師更擅長統計學,比統計學家更擅長軟件工程的人」。許多數據科學家都具備統計學背景,可是在軟件工程方面的經驗甚少。我是一名資深數據科學家,在Stackoverflow的python編程方面排名前1%,並與許多(初級)數據科學家共事。如下是我常常看到的10大常見錯誤,本文將爲你相關解決方案:html

  • 不共享代碼中引用的數據
  • 對沒法訪問的路徑進行硬編碼
  • 將代碼與數據混合
  • 在Git中和源碼一塊兒提交數據
  • 編寫函數而不是DAG
  • 寫for循環
  • 不編寫單元測試
  • 不寫代碼說明文檔
  • 將數據保存爲csv或pickle文件
  • 使用jupyter notebook

1. 不共享代碼中引用的數據

數據科學須要代碼和數據。所以,爲了讓別人能夠復現你的結果,他們須要可以訪問到數據。道理很簡單,可是不少人忘記分享他們代碼中的數據。python

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,緣由見下文)。git

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

與錯誤1類似,若是你對別人沒法訪問的路徑進行硬編碼,他們將沒法運行你的代碼,而且必須仔細查看代碼來手動更改路徑。使人崩潰!github

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,使你的數據易於訪問。web

d6tpipe:數據庫

https://github.com/d6t/d6tpipapache

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並無針對數據進行優化,尤爲是大文件。cookie

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


原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索