R 語言的模塊化
寫 markdown 部落格時我一直都用 VSCode 這款軟體,它可以配合 markdown preview enhance 這個優秀的 markdown 插件達到非常好的寫作體驗。但是今天打開這個軟體時準備寫部落格時,卻發現側邊預覽的快捷按鍵似乎變更了,我隨便亂按了一通,不僅沒有呼出側邊預覽,還把輸入法給弄成了繁體。
這個時候我突然想到說,不然這篇部落格就用繁體和臺灣腔來寫好了。
這個月一直處於一種非常繁忙的狀態,二月份的文章也是是幾天前才補上,我給自己訂下的目標是一個月至少要寫一篇文章,而今天已經是 3 月 29 日了。雖然我之前有構思過接下來要寫的一些文章,但那都是一些需要投入較多精力才能夠寫出來的幹貨,所以我就心裡就想說,要不今天就寫一些比較輕鬆簡單的內容好了。
那麼在數據科學領域,哪方面的內容又輕鬆簡單又對別人有用捏?當然是 R 語言啦 ~(≧▽≦)/~
那麼今天就來寫寫把一個 R 語言工程給模塊化的正確姿勢好了。
R 語言的特性
我所在的小組是做數據模型的 prototype 來的,周圍的同事清一色只使用 R,沒有對比,所以對 R 的一些弊端感受不深,我自己在 python、R、Julia 都寫,所以在比對中深切地感受到一件事情:其他稍微正經一點的語言可能只是 IDE 簡陋(比如 Julia),而 R 語言的簡陋直接體現在語言層面。
這些簡陋特性有利有弊,但是用 R 的人基本都是一些不關注底層的統計人才,用 R 主要也是做 prototype,所以這些特性很少被人詬病。
舉例來說,在 python 中使用 import xxx
調用一個程式模塊之後,需要使用這個程式模塊的名稱來訪問這個模塊裏的對象,而 R 語言把所有的 .R
文檔的每一行都當做 REPL 的素材,不管一個 .R
文檔是被直接運行還是在另一個文檔中被調用,它的每一行代碼都會被很簡單地直接送入 Rterm 解釋器中運行,也就是說即使你的整個專案是按照模塊化的方式來寫的,不同的程式模塊在運行後產生的對象也還是會保留在同一個 work space 中(R 裏叫做 work image),同名對象衝突的問題無法避免。(可能需要解釋一下,灣灣說專案就是 project 的意思啦)
但這個特性其實是可以被我們正面利用的!利用 R 的這一特性和 source()
函數,我們可以比較方便地寫出一個模塊化的簡單專案。其實這也是我今天不用 python 做示範的原因,因爲寫 python 必須要會 import
,但 R users 中有不少人只知 library()
而不會去用 source()
這個很方便的函數。
模塊化的概念
很多數據分析師盡管每天都會用到 Python 與 R 這兩門語言,但是他們並沒有一個比較良好的編程習慣。我完全明了編程技能在數據領域裡其實不算是一個人核心競爭力,我也絕不是一個只知道沉迷於技術的 naive guy,但如果一個良好高效的習慣能爲你每天節省下哪怕半個小時的時間,這個習慣就非常值得去培養。
並且,學習良好的編程方式也是在學習一種經過前人檢驗的優秀思維方式。
就拿模塊化來說,完成一個任務時,可以將其拆分成多個子任務,每個子任務也可以往下細分,我們每次可以只去完成一個最小的單元。這種思路其實是非常符合人的思維習慣的,但是我身邊的一些朋友,明明頭腦很聰明,卻總是開著 Rstudio,把所有的代碼都塞進一個 .R
文檔裏(爲何此時突然想到了我的 emacs 裡面從不關閉的幾百個 buffer…),並且針對所有腳本文檔和數據文檔也不採用工程化的方法管理起來。這種做法其實就非常不利於管理整個任務或者說專案的結構以及發展進度,並且這樣寫出來的東西沒有可讀性,不利於與他人協作。
模塊化一個 R 專案
簡單案例
現在我們來建立一個專案,他的目錄結構是這樣:
在這個專案當中,main.R
是我們去運行的模塊,而 pythagorean.R
是被我們調用的模塊。
來看看這兩個模塊的代碼:
# pythagorean.R# 在這個模塊中,定義一個函數pythagorean <- function(x, y) {z = sqrt(x^2 + y^2)return(z)}# main.R# 在這個模塊中調用其它的模塊# 調用一個已經封裝的包library(ggplot2)# 調用剛才寫的模塊source("../modulize/func/pythagorean.R", encoding="utf-8")x <- 3y <- 4z <- pythagorean(x, y)cat("z=", z, "
")cat("主模塊已被運行
")
在 main.R
中,我們使用了 library(ggplot2)
來調用 ggplot2
這個包,然而在調用我們自己寫的模塊時,我門卻是使用的 source()
這個函數。不能用 library()
來調用我們寫的模塊,因為它調用的是經過打包的 R 程式包。要把自己寫的模塊進行打包,需要用到專門的工具經過繁瑣的步驟才能達到,並且一旦代碼有更改就要重新打包,製作 R 包的過程可以看看 這篇文章。
所以對個人來講,要把自己的專案模塊化並調用,用 source()
函數反而更方便。當 main.R()
中的
source("../modulize/func/pythagorean.R", encoding="utf-8")
這一行被運行的時候,實際上會導致整個 pythagorean.R
被運行,然後我們定義的函數就存入到當前 R 進程的開闢的某段內存中,此時在這個 R 進程的任何地方都可以使用這個函數。實際上,我運行 main.R
這個文件使用的也是
source("main.R", encoding="utf-8")
這條命令。在運行 main.R
之後得到如下結果:
z= 5 主模塊已被運行
得到這樣的結果說明我們在 main.R
中成功使用了 pythagorean
中定義的函數。在這個簡單的例子中可能不太看得出模塊化的優點,可是一旦任務內容較多,就會導致所有的代碼都密密麻麻地擠在一個文件里,不利於修改維護或是與他人協作。
靈活地模塊化 R project
上面舉了一個簡單的例子來演示 R 的模塊化,講真,那個例子還是比較空洞,而且好像人人都在用它舉例的樣子。所以接下來就再舉一個比較貼合實際場景的簡單案例好了。
在平時的工作中,從事數據工作的人基本都有鏈接數據庫的需求,為了更方便地對數據進行處理也會選擇使用 R 語言來鏈接數據庫,我自己主要打交道的數據庫是 Oracle,R 語言鏈接到這個數據庫會用到 RODBC
這個程式包,我見過的許多人都會把鏈接數據庫的程式寫成下面這樣:
# main.R# 調包library(RODBC)# 鏈接信息myconn <- odbcConnect("mydsn",uid="elder",pwd="+1s")data <- sqlQuery(myconn, "select name, age, gender, income, weight, height from table_info where height > 155")other code
這樣子寫完整個文件之後,這個文件的代碼行數會很多,並且其中有相當大的篇幅都是 sql 語句,這樣的代碼哪怕是自己讀起來也很不方便。其實,這段程式可以用模塊化的思想寫成下面這樣:
# query.Rsql_script = "selectname,age,gender,income,weight,heightfromtable_infowhereheight > 155"# main.R# 調包library(RODBC)# 鏈接信息myconn <- odbcConnect("mydsn",uid="elder",pwd="+1s")source("query.R", encoding="utf-8")data <- sqlQuery(myconn, sql_script)other code
把查詢語句放入另外一個獨立的模塊中然後調用,main.R
這個主程式的結構瞬間變得清爽了。
透過 source()
函數,一個 R 工程還可以以其它很多方式被靈活地模塊化,從而提高程式的可讀性與可維護性。這一切之所以能辦到,都要感謝 R 語言的設計者沒有給它加上複雜嚴謹的語言特性 23333
推薦閱讀:
※R語言實戰之數據結構
※基於Python的信用評分卡模型分析
※探索電影大數據
※Kaggle競賽--泰坦尼克號生存預測
※江湖險惡,不行就撤