數(shù)據(jù)科學(xué)家是“比軟件工程師更擅長(zhǎng)統(tǒng)計(jì)學(xué),比統(tǒng)計(jì)學(xué)家更擅長(zhǎng)軟件工程的人”。許多數(shù)據(jù)科學(xué)家都具有統(tǒng)計(jì)學(xué)背景,但是在軟件工程方面的經(jīng)驗(yàn)甚少。我是一名資深數(shù)據(jù)科學(xué)家,在Stackoverflow的python編程方面排名前1%,并與許多(初級(jí))數(shù)據(jù)科學(xué)家共事。以下是我經(jīng)常看到的10大常見(jiàn)錯(cuò)誤,本文將為你相關(guān)解決方案:
不共享代碼中引用的數(shù)據(jù)
對(duì)無(wú)法訪問(wèn)的路徑進(jìn)行硬編碼
將代碼與數(shù)據(jù)混合
在Git中和源碼一起提交數(shù)據(jù)
編寫(xiě)函數(shù)而不是DAG
寫(xiě)for循環(huán)
不編寫(xiě)單元測(cè)試
不寫(xiě)代碼說(shuō)明文檔
將數(shù)據(jù)保存為csv或pickle文件
使用jupyter notebook
1.不共享代碼中引用的數(shù)據(jù)
數(shù)據(jù)科學(xué)需要代碼和數(shù)據(jù)。因此,為了讓別人可以復(fù)現(xiàn)你的結(jié)果,他們需要能夠訪問(wèn)到數(shù)據(jù)。道理很簡(jiǎn)單,但是很多人忘記分享他們代碼中的數(shù)據(jù)。
importpandasaspddf1=pd.read_csv('file-i-dont-have.csv')#failsdo_stuff(df)
解決方案:使用d6tpipe來(lái)共享你的代碼中的數(shù)據(jù)文件、將其上傳到S3/web/google驅(qū)動(dòng)等,或者保存到數(shù)據(jù)庫(kù),以便于別人可以檢索到文件(但是不要將其添加到git,原因見(jiàn)下文)。
2.對(duì)無(wú)法訪問(wèn)的路徑進(jìn)行硬編碼
與錯(cuò)誤1相似,如果你對(duì)別人無(wú)法訪問(wèn)的路徑進(jìn)行硬編碼,他們將無(wú)法運(yùn)行你的代碼,并且必須仔細(xì)查看代碼來(lái)手動(dòng)更改路徑。令人崩潰!
importpandasaspddf=pd.read_csv('/path/i-dont/have/data.csv')#failsdo_stuff(df)#orimportosos.chdir('c:\Users\yourname\desktop\python') # fails
解決方案:使用相對(duì)路徑、全局路徑配置變量或d6tpipe,使你的數(shù)據(jù)易于訪問(wèn)。
3.將代碼與數(shù)據(jù)混合
既然數(shù)據(jù)科學(xué)的代碼中包含數(shù)據(jù),為什么不把它們放到同一目錄中?那樣你還可以在其中保存圖像、報(bào)告和其他垃圾。哎呀,真是一團(tuán)糟!
├──data.csv├──ingest.py├──other-data.csv├──output.png├──report.html└── run.py
解決方案:將你的目錄進(jìn)行分類(lèi),比如數(shù)據(jù)、報(bào)告、代碼等。請(qǐng)參閱Cookiecutter Data Science或d6tflow項(xiàng)目模板[見(jiàn)#5],并使用#1中提到的工具來(lái)存儲(chǔ)和共享數(shù)據(jù)。
4.在Git中和源碼一起提交數(shù)據(jù)
現(xiàn)在,大多數(shù)人對(duì)他們的代碼使用版本控制(如果你不使用,那就是另外一個(gè)錯(cuò)誤,請(qǐng)參閱git:https://git-scm.com/)。在嘗試共享數(shù)據(jù)時(shí),很容易將數(shù)據(jù)文件添加到版本控制中。當(dāng)文件很小時(shí)是可以的,但是git并沒(méi)有針對(duì)數(shù)據(jù)進(jìn)行優(yōu)化,尤其是大文件。
gitadddata.csv
解決方案:使用第1點(diǎn)中提到的工具來(lái)存儲(chǔ)和共享數(shù)據(jù)。如果你真的希望對(duì)數(shù)據(jù)進(jìn)行版本控制,請(qǐng)參閱d6tpipe,DVC和Git大文件存儲(chǔ)。
5.編寫(xiě)函數(shù)而不是DAG
關(guān)于數(shù)據(jù)部分已經(jīng)夠多了,現(xiàn)在來(lái)談一談實(shí)際的代碼!在學(xué)習(xí)編程時(shí)最先學(xué)習(xí)的內(nèi)容之一就是函數(shù),數(shù)據(jù)科學(xué)代碼通常由一系列線性運(yùn)行的函數(shù)組成。
這會(huì)導(dǎo)致一些問(wèn)題,請(qǐng)參閱“為什么你的機(jī)器學(xué)習(xí)代碼可能不好的4個(gè)原因”:
defprocess_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'])
解決方案:數(shù)據(jù)科學(xué)代碼不是一系列線性連接的函數(shù),而是一組具有依賴(lài)關(guān)系的任務(wù)集合。
6.寫(xiě)for循環(huán)
與函數(shù)類(lèi)似,for循環(huán)也是你學(xué)習(xí)編程時(shí)最初學(xué)習(xí)的內(nèi)容。它們易于理解,但是運(yùn)行緩慢且過(guò)于冗長(zhǎng),通常意味著你不了解矢量化的替代方案。
x = range(10)avg=sum(x)/len(x);std=math.sqrt(sum((i-avg)**2foriinx)/len(x));zscore=[(i-avg)/stdforx]#shouldbe:scipy.stats.zscore(x)# orgroupavg=[]foriindf['g'].unique():dfg=df[df[g']==i]groupavg.append(dfg['g'].mean())#shouldbe:df.groupby('g').mean()
解決方案:Numpy,scipy和pandas為你需要for循環(huán)的情況提供了矢量化函數(shù)。
7.不編寫(xiě)單元測(cè)試
隨著數(shù)據(jù)、參數(shù)或用戶(hù)輸入的改變,你的代碼可能會(huì)出現(xiàn)問(wèn)題,有時(shí)你并沒(méi)有注意到。這可能會(huì)導(dǎo)致糟糕的輸出結(jié)果,而如果有人基于你的輸出做出決策,那么糟糕的數(shù)據(jù)將會(huì)導(dǎo)致糟糕的決策。
解決方案:使用assert語(yǔ)句來(lái)檢查數(shù)據(jù)質(zhì)量。pandas有相等測(cè)試,d6tstack有數(shù)據(jù)提取檢查以及用于數(shù)據(jù)連接的d6tjoin。
以下是數(shù)據(jù)檢查的示例代碼:
assert df['id'].unique().shape[0] == len(ids) # have data for all ids?assertdf.isna().sum()<0.9?#?catch?missing?valuesassert?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.不寫(xiě)代碼說(shuō)明文檔
我明白,你急著做出一些分析結(jié)果。你把事情匯總到一起分析,將結(jié)果交給你的客戶(hù)或老板。一個(gè)星期之后,他們回來(lái)說(shuō),“可以把XXX改一下嗎”或者“可以更新一下這里嗎”。你看著你的代碼,但是并不記得你當(dāng)初為什么這么寫(xiě)。現(xiàn)在就像是在運(yùn)行別人的代碼。
defsome_complicated_function(data):data=data[data['column']!='wrong']data=data.groupby('date').apply(lambdax:complicated_stuff(x))data=data[data['value']<0.9] return data
解決方案:即使在你已經(jīng)提交分析報(bào)告后,也要花費(fèi)額外的時(shí)間,來(lái)對(duì)你做的事情編寫(xiě)說(shuō)明文檔。以后你會(huì)感謝自己,別人更會(huì)感謝你。那樣顯得你很專(zhuān)業(yè)!
9.將數(shù)據(jù)保存為csv或pickle文件
回到數(shù)據(jù),畢竟是在講數(shù)據(jù)科學(xué)。就像函數(shù)和for循環(huán)一樣,CSV和pickle文件很常用,但是并不好用。CSV文件不包含綱要(schema),因此每個(gè)人都必須再次解析數(shù)字和日期。Pickle文件解決了這個(gè)問(wèn)題,但是它只能在python中使用,并且不能壓縮。兩者都不是存儲(chǔ)大型數(shù)據(jù)集的最優(yōu)格式。
defprocess_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或其他帶有數(shù)據(jù)綱要的二進(jìn)制數(shù)據(jù)格式,在理想情況下可以壓縮數(shù)據(jù)。d6tflow將任務(wù)的數(shù)據(jù)輸出保存為parquet,無(wú)需額外處理。
10.使用jupyter notebook
最后一個(gè)是頗有爭(zhēng)議的錯(cuò)誤:jupyter notebook和csv文件一樣普遍。許多人使用它們,但是這并不意味著它們很好。jupyter notebook助長(zhǎng)了上述提到的許多不良編程習(xí)慣,尤其是:
把所有文件保存在一個(gè)目錄中
編寫(xiě)從上至下運(yùn)行的代碼,而不是DAG
沒(méi)有對(duì)代碼進(jìn)行模塊化
很難調(diào)試
代碼和輸出混在一個(gè)文件中
沒(méi)有很好的版本控制
它容易上手,但是擴(kuò)展性很差。
解決方案:使用pycharm和/或spyder。
-
編程
+關(guān)注
關(guān)注
88文章
3565瀏覽量
93535 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4277瀏覽量
62323
原文標(biāo)題:數(shù)據(jù)科學(xué)家常犯的 10 個(gè)編程錯(cuò)誤
文章出處:【微信號(hào):DBDevs,微信公眾號(hào):數(shù)據(jù)分析與開(kāi)發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論