序言
在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腳本類似。要運作的指令被分解成以下部分:
其中
是可執行的指令 (r代碼中是 rscript, python代碼中是python)
是執行腳本所在的完整或相對檔案路徑。需要注意的是,如果在路徑名中有空格,整個檔案路徑必須用雙引号括起來。
這是空格分隔的參數清單用來解析腳本本身。請注意,這些不能作為字元串傳遞。
例如,打開一個終端環境并運作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中的形式以一個字元串清單表示,其相應的元素如下所示:
['', '', '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時,退出狀态存儲在一個名為“狀态”的屬性中。
通過子程序調用,可以将python和r整合到一個應用程式中。這允許一個父程序調用另一個程序作為子程序,并擷取任何輸出到标準輸出的結果。
原文釋出時間為:2016-05-07
本文來自雲栖社群合作夥伴“大資料文摘”,了解相關資訊可以關注“bigdatadigest”微信公衆号