數據科學家常遇到的10個錯誤
- 2019 年 10 月 7 日
- 筆記

數據科學家是「在統計方面比任何軟體工程師都要出色,在軟體工程方面比任何統計學家都出色的人」。許多數據科學家都有統計學背景,但很少有軟體工程經驗。我是一位高級數據科學家,在Python編碼的Stackoverflow上排名第一,並與許多(初級)數據科學家合作。下面是我經常看到的10個常見錯誤。
1. 不共享程式碼中引用的數據
數據科學需要程式碼和數據。因此,要使其他人能夠重現您的結果,他們需要有權訪問數據。雖然看起來很基礎,但是很多人忘記了共享程式碼的數據。
import pandas as pd df1 = pd.read_csv('file-i-dont-have.csv') # 錯誤 do_stuff(df)
解決方案:使用d6tpipe共享數據文件,或上傳到S3 / web / google等或保存到資料庫,以他人可以檢索文件(但不要將它們添加到git,詳見下文)。
2. 硬編碼無法訪問的路徑
與錯誤1相似,如果您對其他人無法訪問的路徑進行硬編碼,則他們將無法運行您的程式碼,因此要查看很多地方手動更改路徑。
import pandas as pd df = pd.read_csv('/path/i-dont/have/data.csv') # 錯誤 do_stuff(df) # or impor os os.chdir('c:\Users\yourname\desktop\python') # 錯誤
解決方案:使用相對路徑,配置全局路徑變數或d6tpipe使數據易於訪問。
3. 將數據與程式碼混合
由於數據科學程式碼需要數據,為什麼不將其存儲到同一目錄?當您使用它時,也可以在其中保存影像,日誌和其他垃圾文件。
├── data.csv ├── ingest.py ├── other-data.csv ├── output.png ├── report.html └── run.py
解決方案:將目錄按照類別進行組織,例如數據,日誌,程式碼等。
4. Git提交帶有源程式碼的數據
現在大多數人都可以控制他們的程式碼版本(如果不使用,那是另一個錯誤!參見git)。為了共享數據,可能想將數據文件添加到版本控制中。如果是很小的文件還可以,但是git並沒有對數據文件進行優化,尤其是大文件。
git add data.csv
解決方案:使用問題1中提到的工具來存儲和共享數據。如果確實要對控制數據進行版本控制,請參閱d6tpipe,DVC和Git大文件存儲。
5. 編寫函數而不是DAG
有足夠的數據,接下來談談實際的程式碼!由於在學習程式碼時首先要學習的內容之一就是函數,因此數據科學程式碼通常被組織為一系列線性運行的函數。這可能會導致幾個問題。
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。
6. 循環
和函數一樣,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具有向量化功能,可用於大多數的循環。
7. 不編寫單元測試
隨著數據,參數或用戶輸入的更改,您的程式碼可能會中斷,有時您可能不會注意到。這可能會導致錯誤的輸出,如果有人根據您的輸出做出決策,那麼錯誤的數據將導致錯誤的決策! 解決方案:使用assert語句檢查數據。pandas有相等測試,d6tstack有數據攝取和檢查,d6tjoin數據連接。程式碼示例:
assert df['id'].unique().shape[0] == len(ids) # 數據是否有所有的id assert df.isna().sum()<0.9 # 檢查缺失的數據 assert df.groupby(['g','date']).size().max() ==1 # 是否有重複的數據 assert d6tjoin.utils.PreJoin([df1,df2],['id','date']).is_all_matched() # 所有的id是否匹配
8. 不記錄程式碼
我明白你著急進行一些分析。您可以一起努力取得成果給客戶或老闆。然後一個星期後,他們說「請您更新此內容」。您看著您的程式碼,不記得為什麼要這麼做。現在想像其他人需要運行它。
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不包含架構,因此每個人都必須再次解析數字和日期。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,不需要你進行處理。
10. 使用jupyter筆記型電腦
讓我們以一個有爭議的結論來結束:jupyter notebooks 與CSV一樣普遍。很多人使用它們,那並不是好事。Jupyter notebooks 促進了上述許多不良的軟體工程習慣,尤其是:
- 很容易將所有文件存儲到一個目錄中
- 編寫的程式碼從上至下而不是DAG運行
- 沒有模組化程式碼
- 調試困難
- 程式碼和輸出混合在一個文件中
- 版本控制不好
入門很容易,但是擴展性很差。
解決方案:使用pycharm或spyder。
