0
想要讓你的代碼更專業(yè),最好的方法就是使其可重用。
「可重用」是什么意思?在你的數(shù)據(jù)科學職業(yè)生涯中的某個時刻,你編寫的代碼將被使用不止一次或兩次。也許你會對一些不同的圖像文件集運行相同的預處理管道,或者你有一套用于比較模型的評估技術(shù)。我們都復制并粘貼了相同的代碼,但是一旦你發(fā)現(xiàn)自己復制了相同的代碼不止一次或兩次,那就應(yīng)該花點時間使你的代碼可重用。重用好的代碼并不是欺騙或懈?。核菍r間的有效利用,并且被認為是軟件工程中的最佳實踐。
我認為有六個核心原則:1)讓你或你的同事很容易重用你的代碼;2)讓你的代碼看起來非常精良和專業(yè);最重要的是,3)節(jié)省你的時間。
模塊化:代碼被分解成獨立的小部分(如函數(shù)),每個部分都做一件事。
正確:你的代碼做的是你想要的事情。
可讀性:很容易閱讀代碼并理解它的作用。變量名是信息性的,代碼有最新的注釋。
風格:代碼遵循單一、一致的風格(例如,r 的 tidyverse 風格指南,python 代碼的 pep 8)
通用性:解決一個不止一次發(fā)生的問題,并預測數(shù)據(jù)的變化。
創(chuàng)造性:解決一個尚未解決的問題,或者是對現(xiàn)有解決方案的明顯改進。
讓我們更詳細地介紹一下這些步驟中的每一步,并給出一些示例代碼,看看它們在實踐中是如何工作的。
模塊化
模塊化代碼意味著你的代碼被分解成獨立的小部分(比如函數(shù)),每個部分都做一件事。
無論是在 python 還是 r 中,每個函數(shù)都有以下幾個部分:
函數(shù)的名稱。
函數(shù)的參數(shù)。這是你將傳遞到函數(shù)中的信息。
函數(shù)體。這是定義函數(shù)功能的地方。通常,我會為我的函數(shù)編寫代碼,并首先使用現(xiàn)有的數(shù)據(jù)結(jié)構(gòu)進行測試,然后將代碼放入函數(shù)中。
返回值。這是你的函數(shù)在完成編寫后將返回的內(nèi)容。在 python 中,需要通過在函數(shù)底部添加 return(thing_to_return)來指定要返回的內(nèi)容。在 r 中,默認情況下將返回函數(shù)體最后一行的輸出。
讓我們看一些例子。這里有兩個示例函數(shù),一個在 python 中,一個在 r 中,它們做了相同的事情(或多或少)。
它們都有相同的函數(shù)名,find_most_common
他們都有一個參數(shù),values
它們都有一個執(zhí)行大致相同操作的主體:計算值中每個 values 顯示的次數(shù)
它們都返回相同的內(nèi)容:輸入?yún)?shù)值中最常見的值
python 示例
# define a function
def find_most_common(values):
list_counts = collections.Counter(values)
most_common_values = list_counts.most_common(1)
return(most_common_values[0][0])
# use the function
find_most_common([1, 2, 2, 3])
r 示例
# define the function
find_most_common <- function(values){
freq_table <- table(values)
most_common_values <- freq_table[which.max(freq_table)]
names(most_common_values)
}
# use the function
find_most _common(c(1, 2, 2, 3))
很直截了當,對吧?(即使兩種語言之間的語法有點不同)。你可以使用這個編寫小函數(shù)的一般原則,每個函數(shù)只做一件事,將代碼分解成更小的片段。
為什么是函數(shù)?
如果你有更多的編程經(jīng)驗,你可能會好奇為什么我選擇談?wù)摵瘮?shù),而不是類或其他相關(guān)概念從[面向?qū)ο缶幊蘛。我認為函數(shù)式編程適合于很多數(shù)據(jù)科學工作,所以這是我將用來向你展示模塊化代碼示例的一般框架。
函數(shù)式編程。一種編寫代碼的方式,在這種方式下,你將一個或多個數(shù)據(jù)片段傳遞到一個函數(shù)中,然后返回的結(jié)果將是這些數(shù)據(jù)片段的某種轉(zhuǎn)換。這意味著你不需要修改函數(shù)體中的現(xiàn)有變量。如果你有興趣了解更多,我建議你看下這篇討論。
我喜歡將函數(shù)方法用于數(shù)據(jù)科學的主要原因是,它使將多個函數(shù)鏈接到一個數(shù)據(jù)處理管道變得容易:一個函數(shù)的輸出成為下一個函數(shù)的輸入。就像這樣:
數(shù)據(jù)->函數(shù) 1->函數(shù) 2->函數(shù) 3->轉(zhuǎn)換數(shù)據(jù)
有一些非常有用的工具可以幫助你做到這一點,包括 r 中的 pipes 和 python 中 pyjanitor 的方法接。
python 示例:將函數(shù)鏈接在一起
本例基于 pyjanitor 文檔中的一個示例,向你展示了如何使用現(xiàn)有 pandas 函數(shù)設(shè)置一個小的數(shù)據(jù)管道。
它讀取一個文件(pd.read_excel('dirty_data.xlsx')行),然后使用一些函數(shù)對其進行轉(zhuǎn)換,這些函數(shù)可以清除列名、刪除丟失的數(shù)據(jù)、重命名其中一列并將其中一列轉(zhuǎn)換為 datetime 格式。輸出也是一個數(shù)據(jù)幀。
cleaned_df = (
pd.read_excel('dirty_data.xlsx')
.clean_names()
.remove_empty()
.rename_column("full_time_", "full_time")
.convert_excel_date("hire_date")
)
cleaned_df
r 示例:將函數(shù)鏈接在一起
這里有一個 r 示例,它執(zhí)行與 python 示例相同的操作。
cleaned_df <- read_excel('dirty_data.xlsx') %>%
clean_names() %>%
remove_empty() %>%
renames(“full_time”, “full_time_”) %>%
excel_numeric_to_date(“hire_date”)
將代碼分解為函數(shù)(特別是如果每個函數(shù)只轉(zhuǎn)換傳遞給它的數(shù)據(jù))可以讓你重用代碼并將不同的函數(shù)組合成緊湊的數(shù)據(jù)管道,從而節(jié)省時間。
正確
我所說的「正確」是指你的代碼按照你說的/認為的那樣去做。這很難檢查。確保代碼正確的一種方法是代碼審查。
代碼審查是一個過程,在這個過程中,你的同事仔細檢查你的代碼,以確保它的工作方式和你認為的是一樣的。
不幸的是,這對數(shù)據(jù)科學家來說并不總是可行的。尤其是如果當你是公司里唯一的數(shù)據(jù)科學家時,很難讓一個沒有統(tǒng)計學或機器學習背景的人給你提供關(guān)于你的代碼的可靠反饋。隨著這個領(lǐng)域的發(fā)展,數(shù)據(jù)科學代碼進行代碼審查可能變得更加常見……但同時,你可以通過一些測試來幫助審查代碼是否正確。
測試是使用一小段代碼檢查你的代碼是否正常工作。
測試用例不必寫得很復雜!在這里,我將研究如何用一行代碼向函數(shù)添加測試。
在上面編寫的 python 函數(shù)中,我返回了最常見的值……但是如果有多個返回值的情況怎么辦?
assert 是一個內(nèi)置在 python 中的方法,它幫助我們檢查某些內(nèi)容是否正確。如果是正確的,那么什么都不會發(fā)生。否則,我們的函數(shù)將停止運行并給出報錯信息。
import collectionsdef find_most_common(values):
""""Return the value of the most common item in a list"""
list_counts = collections.Counter(values)
top_two_values = list_counts.most_common(2)
# make sure we don't have a tie for most common
assert top_two_values[0][1] != top_two_values[1][1]\ ,"There's a tie for most common value"
return(top_two_values[0][0])
這里的 assert 語句檢查最常用值的計數(shù)是否與第二個最常用值的計數(shù)不同。如果是,函數(shù)將停止并返回錯誤消息。
首先,讓我們檢查一下,如果沒有 tie,我們的功能是否會按預期工作:
到目前為止還不錯:5 比任何其他值都多。但如果有 tie 呢?
我們得到一個 assertion 錯誤和一個很有用的錯誤信息!
雖然這是一個非常簡單的例子,但是包含一些測試可以幫助你確保代碼正在做你認為它正在做的事情。如果要導入其他庫并定期更新它們,這一點尤其重要:不要僅僅因為你沒有更改代碼,就認為要導入的代碼沒有更改!測試可以幫助你在錯誤引起問題之前找到它們。
使用測試檢查代碼是否正確可以幫助快速捕獲錯誤,從而節(jié)省時間。
可讀性
「可讀」代碼是易于閱讀和理解的代碼,即使這是你第一次看到這段代碼。一般來說,變量名和函數(shù)名之類的單詞表示的實際的意思,則代碼越容易閱讀。此外,描述代碼在做什么或為什么做出特定選擇的注釋可以幫助提高代碼的可讀性。
變量名
變量名是信息性的,代碼有最新的注釋和 docstring。
一些不太可讀的變量名示例如下:
單個字符,如 x 或 q。有一些例外,如使用 i 作為索引或 x 作為 x 軸。
所有小寫名稱之間沒有空格,例如 likethisexample 或者 somedatafromsomewhere。
非格式化或不明確的名稱,例如 data2 不會告訴你數(shù)據(jù)中的內(nèi)容或者它與 data1 的區(qū)別。df 告訴你某個東西是一個數(shù)據(jù)幀……但是如果你有多個數(shù)據(jù)幀,你怎么知道它是哪一個?
你可以通過以下幾個規(guī)則改進命名:
使用某種方式指示變量名中單詞之間的空格。由于你不能使用實際的空格,常用的方法是 snake_case 和 camelcase。
使用這些名稱來描述變量中的內(nèi)容或函數(shù)的作用。例如,sales_jan 的信息量比單純的 data 要大,z_score_calculator 的信息量比單純的 calc 或 norm 的要大。
當你還在想怎么寫代碼的時候,我建議你返回去,把變量名取得更好。
注釋
注釋是代碼中解釋的文本。在 python 和 r 中,可以通過以 # 開頭來表示該行是注釋。更好地寫注釋的一些技巧:
雖然有些風格指南建議不要包含關(guān)于代碼在做什么的信息,但我實際上認為這在數(shù)據(jù)科學中是有道理的。
如果你更改了代碼,請記住更新注釋!
如果你用做某件事的方式非同尋常,那么也需要加上一條注釋來解釋為什么這樣做,如果接下來有人嘗試更新代碼,會不會遇到問題。
有些風格指南只建議你用英語寫注釋,但是如果你和使用另一種語言的人一起工作,我個人建議你用同事最容易理解的語言來寫注釋。
docstring:在 python 中,docstring 是函數(shù)或類中第一位文本的注釋。如果要導入函數(shù),則應(yīng)包含 docstring。這使你和其他使用該函數(shù)的人能夠快速了解該函數(shù)的功能。
def function(argument):
“”” This is the docstring. It should describe what the function will do when run”””
若要檢查函數(shù)的 docstring,可以使用語法函數(shù)「function_name.__doc__」。
如果您是 r 用戶,并且要向代碼中添加 docstring,則可以使用 docstring 包。
具有可讀性的代碼讀起來更快。當你需要回到一個項目,或者當你第一次遇到新的代碼并且需要了解正在發(fā)生的事情時,這會節(jié)省你的時間。
風格
當我在這里說「風格」時,我的字面意思是「遵循特定的風格」。風格在稱為「風格指南」的文檔中進行描述和定義。如果你以前沒用過風格指南,那就很方便了!遵循特定的風格指南可以使代碼更易于閱讀,并幫助你避免常見錯誤。
風格指南將提供一些指導,比如在哪里需要空格、如何組織文件中的代碼結(jié)構(gòu)以及如何命名函數(shù)和文件等。不遵循風格指南的代碼可能仍然運行得很好,但是看起來有點奇怪,而且通常很難閱讀。
pro tip:實際上,你可以使用一個名為「linter」的程序來自動檢查代碼是否遵循特定的樣式指南。python 的 pylint 和 r 的 lintr 是兩個流行的 linter。你可以在這里看到如何使用 linter 檢查 r 實用程序腳本的示例:https://www.kaggle.com/rtatman/linting-scripts-in-r 。
一旦你選擇了要遵循的風格指南,就應(yīng)該盡最大努力在代碼中始終如一地遵循它。當然,風格指南之間存在差異,但是 python 和 r 風格指南之間的是有共同點的。舉幾個例子:
你應(yīng)該將所有導入(庫(包)或?qū)肽K名)放在代碼文件的頂部,并且每行只有一個導入。
使用制表符縮進或空格縮進取決于你的風格指南,但不應(yīng)混合使用制表符和空格(例如,有些行用兩個空格縮進,有些行用制表符縮進)。
避免在行的末端有空格。
函數(shù)和變量的名稱都應(yīng)該用下劃線隔開。
盡量使代碼行不超過一定的長度,最好少于 80 個字符。
一開始,風格指南可能有點讓人難以接受,最好不要太過強調(diào)。隨著你讀寫更多的代碼,遵循特定的風格指南將變得越來越容易。同時,即使是一些小的改進也會使代碼更易于遵循和使用。
例子
對于這個例子,我們將使用一些 r 代碼并修改它以適應(yīng) tidyverse 樣式指南。
CZS <- function(x) {
sd <- sd(x); m = mean(x)
(x -m)/sd}
我們可以在這里做很多事情,以便遵循 tidyverse 風格指南。
函數(shù)名不提供任何信息,也不遵循 tidyverse 約定(小寫字母加下劃線)。
使用多個賦值運算符(<-和=)。
我們使用的是 tab 和空格。
連接多個行(這是可能的,但在 python 和 r 中都強烈反對)。
我們的中綴運算符周圍沒有空格(例如+、-、\,等數(shù)學符號)。
在新行上沒有右大括號 }。
一旦我們處理好這些問題,我們的代碼現(xiàn)在看起來是這樣的:
calculate_z_score <- function(x) {
sd <- sd(x)
m <- mean(x)
(x - m) / sd
}
我個人認為這比第一個例子更容易閱讀,盡管他們做了完全相同的事情。
遵循風格的代碼通常更容易閱讀和發(fā)現(xiàn)錯誤。此外,大多數(shù)風格指南都會推薦最佳實踐,以幫助你避免常見的錯誤。所有這些都節(jié)省了你和同事調(diào)試的時間。
通用性
「通用性」的意思是在各種情況下都能夠使用。具有通用性的代碼解決了會不止一次發(fā)生的問題,并預期數(shù)據(jù)變化。
如果我打算重用代碼,我應(yīng)該重寫代碼嗎?
不,當然不是。寫新代碼來解決一個獨特的問題沒有錯??赡苣阈枰焖僦孛慌募蛘哂腥艘竽銥橐淮涡匝菔局谱饕粋€新的、獨特的可視化效果。
然而,你可能不想費盡周折,使自己編寫的每一行代碼都完全可重用。雖然有些人會不同意我的觀點,但我個人認為,如果你(或其他人)真的要重用代碼,那么只需要花費大量時間來完善代碼。
數(shù)據(jù)科學家必須做很多不同的事情,知道很多不同的事情:比起仔細地潤色每一行你曾經(jīng)編寫的代碼,你的時間可能能夠用在更好的地方。當你知道代碼將被重用時,花時間去完善你的代碼是有意義的?;ㄒ稽c時間讓每件事都更容易理解和使用,可以節(jié)省很多時間。
預測數(shù)據(jù)的變化
我所說的「數(shù)據(jù)的變化」是指數(shù)據(jù)中的差異,這些差異會把事情分解開來。例如,你可能編寫了一個函數(shù),假設(shè)你的數(shù)據(jù)幀有一個名為 latitude 的列。如果有人下周在數(shù)據(jù)庫中將列的名稱更改為 lat,則你的代碼運行可能會中斷。
為了確保你可以獲取預期要獲取的數(shù)據(jù),可以使用數(shù)據(jù)驗證。我這里有一個 notebook,如果你好奇的話,可以查看關(guān)于數(shù)據(jù)驗證更詳細地介紹。下面是一些我最喜歡的工具:
python:
我喜歡 csvvalidator 包,以前寫過一個介紹:https://www.kaggle.com/rtatman/dashboarding-with-notebooks-day-5 。
對于 python 中的 json 數(shù)據(jù),cerberus 模塊可能是最流行的工具。
對于可視化丟失的數(shù)據(jù),missingno 包非常方便。
要檢查文件的類型,python magic 模塊可能會有所幫助。
r:
對于 r,用于數(shù)據(jù)驗證的 validate 包(我以前為其編寫過教程:https://www.kaggle.com/rtatman/dashboarding-with-notebooks-day-5-r )可能是你的最佳選擇。它可以處理表格、層次結(jié)構(gòu)和原始文本數(shù)據(jù),這很好。)
要確定文件類型,可以使用 mime 包。
通用代碼可以在各種情況下使用。這節(jié)省了你的時間,因為你可以在多個不同的地方應(yīng)用相同的代碼。
創(chuàng)造性
我所說的「創(chuàng)造性」是指解決一個尚未解決的問題或是對現(xiàn)有解決方案明顯改進的代碼。我之所以把這個也包括進來,是為了鼓勵你查找現(xiàn)有的庫或模塊(或 kaggle 腳本)來解決你的問題。如果有人已經(jīng)編寫了你所需的代碼,并且該代碼允許你使用,那么你可能應(yīng)該這樣做。
如果有明顯的改進的話,我建議你寫一個庫來復制另一個庫的功能。例如,python 庫 flastext。它允許你做使用正則表達式能做的相同操作(如查找、提取和替換文本),但速度要快得多。
只有在沒有現(xiàn)有解決方案的情況下花時間編寫代碼才能節(jié)省時間,因為你可以在現(xiàn)有工作的基礎(chǔ)上構(gòu)建,而不是從頭開始。
via:https://www.kaggle.com/rtatman/six-steps-to-more-professional-data-science-code
雷鋒網(wǎng)雷鋒網(wǎng)雷鋒網(wǎng)
雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。