如何將Python和R整合進一個數據分析流程
在Python中調用R或在R中調用Python,為什麼是「和」而不是「或」?
在互聯網中,關於「R Python」的文章,排名前十的搜索結果中只有2篇討論了一起使用R和Python的優點,而不是把這兩種語言對立起來看。這是可以理解的:這兩種語言從一開始都具有非常顯著的優缺點。從歷史上看,儘管把兩者分割開來是因為教育背景:統計學家們傾向用R,而程序員則選擇了Python語言。然而,隨著數據科學家的增加,這種區別開始變得模糊起來:
數據科學家就是這樣一種人:軟體工程師中最懂統計學,統計學家中最會編程的人。 – josh_wills在推特上這樣說到。
由於這兩種語言各自提供大量獨特的庫資源,對能夠利用這兩種語言的相對優勢的數據科學家的需求正在不斷增長。
Python與R的對比
在以下領域中,Python 比R 更有優勢:
網路爬蟲和數據抓取:雖然R中的rvest已經簡化了網頁抓取, Python的beautifulsoup和Scrapy更加成熟,並提供更多的功能。
資料庫連接:雖然R有大量的用於連接到資料庫的選項, Python的sqlachemy只用了一個程序包就提供了所有的資料庫連接功能,並可廣泛用於生產環境。
而在以下領域中,R比Python更有優勢:
統計分析選項:儘管Python的SciPy和 Pandas以及 statsmodels的組合提供了很大的一套統計分析工具,而R是專門圍繞著統計分析應用等創建的,因此提供了更多的相關工具。
互動式圖像或控制板:bokeh, plotly和intuitics最近都把Python的圖形使用擴展到了Web瀏覽器,但是舉個使用shiny的例子,R中的shiny 控制面板運行速度更快,而且往往需要更少的代碼。
此外,由於數據科學團隊現在擁有一個比較廣泛的技能庫,任何應用程序所選擇的編程語言都可能用到以前的知識和經驗。對於一些應用,特別是原型設計和開發應用,人們使用他們已知的工具則速度會更快。
純文本 的「Air Gap(網閘)」策略
指在完全斷開網路物理連接的基礎上,實現合法信息的共享。本文中指用純文本文件實現兩種語言間代碼的共享——譯者注。
使用純文本作為兩種語言之間的物理隔離,你需要按如下步驟進行。
從命令行中重構你的R和Python腳本,並接受命令行參數。
輸出共享數據到公共文件格式。
在一種語言中執行另一種語言,按要求傳遞參數。
優勢:
最簡單的方法,通常最快
可以輕鬆查看中間輸出結果
已有常見文件格式,如: CSV , JSON , YAML的解析器
劣勢:
需要事先商定一個共同的模式或文件格式
如果流程變長的話,難以管理中間輸出結果和路徑
如果數據量變大,本地磁碟讀寫將成為瓶頸
命令行腳本
通過Windows 或Linux終端環境命令行運行R和Python腳本類似。要運行的命令被分解成以下部分:
<command_to_run> <path_to_script> <any_additional_arguments>
其中
<command> 是可執行的命令 (R代碼中是 Rscript, Python代碼中是Python)
<path_to_script>是執行腳本所在的完整或相對文件路徑。需要注意的是,如果在路徑名中有空格,整個文件路徑必須用雙引號括起來。
<any_additional_arguments>這是空格分隔的參數列表用來解析腳本本身。請注意,這些不能作為字元串傳遞。
例如,打開一個終端環境並運行R腳本,命令如下:
Rscript path/to/myscript.R arg1 arg2 arg3
請注意以下問題:
對於Rscript 和Python 命令必須在你所在的路徑中執行,否則你需要提供文件的完整路徑。
含有空格符的路徑名會產生問題,尤其是在Window系統中,因此必須用雙引號括起來,這樣才被認為是一個單獨的文件路徑。
R語言中訪問命令行參數
上面的例子中,arg1,arg2 和 arg3是用來解析可執行R腳本的參數,可以使用commandArgs函數訪問
##myscript.py
#獲取命令行參數
myArgs <- commandArgs(trailingOnly = TRUE)
#myArgs是所有參數的特徵向量
print(myArgs) print(class(myArgs))
通過設置trailingOnly 為TRUE,myArgs向量中只包含添加到命令行的參數。如果默認設置為FALSE ,myArgs向量中還包含其它參數,比如剛被執行的腳本路徑。
Python語言中訪問命令行參數
通過下面的命令行執行Python腳本:
python path/to/myscript.py arg1 arg2 arg3
通過在Python腳本中導入sys模塊訪問arg1, arg2 和arg3參數。 sys模塊包含了系統具體的參數和函數,在這裡,我們只對 argv的屬性感興趣。這個argv屬性是所有被傳遞到當前正在執行腳本的參數列表。表中的第一個元素是正在被執行的腳本的完整路徑。
# myscript.py
import sys
# 獲取命令行參數
my_args = sys.argv
# my_args 是一個列表,其中的第一個元素是執行的腳本
print(type(my_args))
print(my_args)
如果你只希望保留傳遞到腳本的參數,你可以使用列表切片來選擇除了第一個元素以外的所有參數。
# 使用切片,選擇除第一個以外的所有元素
my_args = sys.argv[1:]
回顧一下上面的R語言例子,所有的參數需要以字元串的形式傳遞,因此有必要轉換為所期望的數據類型。
將輸出結果寫入文件
通過中間文件共享R和Python之間的數據有幾種選擇。通常,對於普通文本文件,CSVs是很好的表格數據格式,而處理可變長欄位或許多嵌套數據結構的非結構化數據(或元數據)形式時,JSON 或YAML是最好的數據格式。
這些都是很常見的數據序列化格式,在R和Python中已存在相應的語法解析器。
在R語言中推薦下面的程序包:
對於CSV文件,使用readr
對於JSON文件,使用jsonlite
對於YAML文件,使用yaml
Python中推薦:
對於CSV文件,使用csv
對於JSON文件,使用json
對於YAML文件,使用PyYAML
csv 和json模塊是Python標準的庫文件,是Python內置模塊,而PyYAML需要額外安裝程序包。所有的R程序包均需要安裝。
總結
R 和Python之間的數據傳遞可以通過單一傳遞途徑進行:
使用命令行傳遞參數
使用常見的結構化文本文件傳遞數據
然而,在某些實例中,需要將文本文件作為中間文件存儲在本地,這不僅很麻煩而且還影響性能。接下來,我們將討論如何在R和Python中直接調用並在內存中輸出。
命令行執行和執行子進程
為了更好地理解在執行子進程的時候發生了什麼,值得重新考慮當命令行運行一個Python 或 R進程中更多的細節。在運行下面的命令時,啟動了一個新的 Python 進程執行該腳本。
在執行過程中,任何被輸出到標準輸出和標準錯誤流的數據會返回到控制台顯示。最常見的實現方式是通過Python中的一個內置函數print()或是 R中的函數 cat()和 print(),它們將給定字元串的寫入標準輸出流。一旦腳本執行完畢,Python進程隨即關閉。
在這種方式下運行命令行腳本是有用的,但如果希望用這個方法執行多個連續卻相互獨立腳本時,就變得繁瑣,並且容易出錯。然而,這可能讓一個Python或R進程直接去執行另一個類似的命令。這樣有好處,即從一個Python父進程啟動一個R中的子進程去運行特定的腳本,進而完成分析。一旦R腳本運行完畢,R中子進程的輸出不是被傳到控制台,而是返回到父進程中。使用這種方法除去了手動單獨執行命令行的步驟。
實例
為了說明一個進程的執行是由另一個進程引起的,我們將會用兩個簡單的例子:一個是Python調用R,另一個是R調用Python。我們人為降低了每個案例中分析結果的重要性,以便把重點放在機器是如何的實現的過程上。
R腳本範例
我們簡單的R腳本例子要從命令行獲取一系列數字並返回最大值。
# max.R
# 獲取命令行參數
myArgs <- commandArgs(trailingOnly = TRUE)
# 轉換成數字類型
nums = as.numeric(myArgs)
# cat將把結果寫入標準輸出流
cat(max(nums))
在Python中執行R腳本
我們需要利用子進程的模塊,也就是標準庫的一部分,來實現從Python中進行調用。我們將使用函數check_output 來調用 R 腳本,執行命令並存儲標準輸出的結果。
想要在Python中調用R來執行 max.R腳本,首先要建立要運行的命令。在Python中的形式以一個字元串列表表示,其相應的元素如下所示:
[『<command_to_run>』, 『<path_to_script>』, 『arg1』 , 『arg2』, 『arg3』, 『arg4』]
下面代碼是運行在Python中調用R的一個例子:
# run_max.py
import subprocess
# 定義命令和參數
command = 『Rscript』
path2script = 『path/to your script/max.R』
# args變數的值是一個列表
args = [』11』, 『3』, 『9』, 』42』]
#建立子進程命令
cmd = [command, path2script] + args
# check_output會執行命令並存儲結果
x = subprocess.check_output(cmd, universal_newlines=True)
print(『The maximum of the numbers is:』, x)
參數 universal_newlines=True 告訴 Python 把返回的輸出結果解釋為文本字元串,並處理 Windows 和 Linux 的換行字元。如果省略了這個,則輸出結果會被作為一個位元組的字元串返回,同時在進行任何字元串進一步操作之前必須調用x.decode()來解碼成文本。
Python 腳本範例
在我們簡單的 Python 腳本中,我們將給定的字元串(第一個參數)拆分為基於所提供的字元串模式的多個子字元串 (第二個參數)。然後,結果以每行一個子字元串的形式輸出到控制台。
# splitstr.py
import sys
# 獲取傳入的參數
string = sys.argv[1]
pattern = sys.argv[2]
#執行分割
ans = string.split(pattern)
#把所產生的元素列表合成一個新命令行
# 分割字元串並列印
print(『n』.join(ans))
在R中調用Python
當用R執行子進程時,建議使用 R 的system2函數來執行並獲取輸出。這是因為內置的系統函數跨平台不兼容,非常難使用。
建立要執行的命令是類似於上面的 Python 例子,然而system2 期望命令根據它的參數被分解開來。此外,這些參數首先必須總是正在執行的腳本的路徑。
最後一個困難可能是R腳本路徑名稱中的空格處理引起的。解決這一問題最簡單的方法是為全路徑名稱加上雙引號,然後用單引號封裝此字元串,這樣,R保留參數本身的雙引號。
下面的代碼中,給出在R 中執行 Python 腳本的實例。
# run_splitstr.R
command = 「python」
#注意在字元串中的單引號和雙引號(如果路徑名中有空格,這是必須的)
path2script=」path/to your script/splitstr.py」『
# 設置args成向量
string = 「3523462—12413415—4577678—7967956—5456439」
pattern = 「—」
args = c(string, pattern)
# 把腳本路徑加入,成為第一個arg參數
allArgs = c(path2script, args)
output = system2(command, args=allArgs, stdout=TRUE)
print(paste(「The Substrings are:n」, output))
為了獲取標準輸出中的特徵向量(每個元素一行),stdout=TRUE 必須在system2中具體說明,不然返回的只是退出狀態。當stdout=TRUE時,退出狀態存儲在一個名為「狀態」的屬性中。
推薦閱讀:
※張溪夢:14年數據分析經歷,我總結為這三點「道、術、器」
※【數據分析·實戰】評分與銷量有相關性嗎?
※融匯|高效投融及投資管理的未來 VC SaaS X 華興資本逐鹿
※用R語言製作商務圖表,讓你的圖表美出新高度~