面對大數據時代,Stata16推出了Python接口,允許在Stata界面調用Python。為配合《高級計量經濟學》課程的“編程專題”,本文將對Stata Function Interface(sfi)作簡要介紹,感興趣的同學可以適當嘗試。
說到軟件交互,其實就是4個事情:
如何在Stata中啟動Python環境
如何將Stata的數據類型轉化為Python的數據類型
如何將Python的數據類型轉化為Stata的數據類型
如何將Python函數封裝在Stata命令中
先說幾句題外話。我個人認為,Stata16的這項新功能意義并不大。一方面,Python早就可以調用Stata了,我們完全可以將Stata的命令封裝在Python命令中;另一方面,數據類型的轉化完全可以通過數據集的導入導出來實現。正如我們經常做的,用Python爬一組數據,然后保存為Excel工作表,最后用Stata讀取并建模。因此在我看來,Stata16提供Python接口,會將很多Stata鐵粉引向免費的Python,但對同時掌握Stata和Python的用戶卻毫無激勵。來都來了,我們簡單看看這個接口吧。
1 啟動Python環境
1.1 配置Stata與Python的關聯
首先,你需要在電腦上同時安裝好Stata和Python。如果你還沒有安裝Python,建議下載Anaconda,版本為3.X。地址如下:
https://www.anaconda.com/distribution/
假設我們將Anaconda安裝在D盤根目錄下,會發現Python軟件的地址為
?D:\Anaconda3\python.exe
打開Stata16,在命令欄輸入
set python_exec D:\Anaconda3\python.exe
Stata和Python就算關聯好了。
嘗試一下:在命令欄鍵入python,出現以下結果就算成功了。
. python
----------------------------------------------- python (type end to exit) ---------------
>>>
1.2 進入Python環境
在Stata界面下進入Python環境的方式有3種:
完全進入
鍵入python可完全進入Python環境,命令提示符從.變為>>>。此后,我們可以自由輸入Python代碼,直到輸入end命令退出該環境。舉例如下:
. python
----------------------------------------------- python (type end to exit) ---------------
>>> print('Hello NJUPT!')
Hello NJUPT!
>>> city = 'NJUPT'[:2]
>>> print('I LOVE', end=' '); print(city)
I LOVE NJ
>>> end
-----------------------------------------------------------------------------------------
半完全進入
鍵入python:可進入Python環境,但這種進入并不穩定,一旦出現錯誤就會回到Stata環境中。舉例如下:
. python:
----------------------------------------------- python (type end to exit) ---------------
>>> print('I am a teacher.')
I am a teacher.
>>> print(1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
-----------------------------------------------------------------------------------------
r(7102);
.
非完全進入
在python:后直接鍵入一個或多個命令,可以在不跳出Stata環境的前提下,臨時性地執行Python語法。舉例如下:
. python: print('I Love You.'); print('I Love You, too.')
I Love You.
I Love You, too.
非完全進入非常有用,能使我們在撰寫Stata小程序時直接調用Python的方法。
1.3 在Python環境下調用Stata
當(半)完全地進入Python環境時,又如何臨時調用Stata命令呢?很簡單,使用stata:前綴即可。舉例如下:
. python
----------------------------------------------- python (type end to exit) ---------------
>>> stata: sysuse auto.dta, clear
(1978 Automobile Data)
>>> stata: regress price weight, nohead nocons
------------------------------------------------------------------------------
price | Coef. Std. Err. t P>|t| [95% Conf. Interval]
------------- ----------------------------------------------------------------
weight | 2.041977 .0926943 22.03 0.000 1.857238 2.226717
------------------------------------------------------------------------------
>>> end
-----------------------------------------------------------------------------------------
1.4 數據類型不兼容問題
剛拿到Stata16,我還無法完全摸清Stata和Python在數據類型上的兼容性,比如:
. python
----------------------------------------------- python (type end to exit) ---------------
>>> a = 1 1
>>> stata: display a
a not found
r(111);
>>> stata: scalar x = a
>>> stata: display x
2
>>> end
-----------------------------------------------------------------------------------------
為了保證代碼執行的正確性,我們盡可能在兩種軟件的數據類型轉換上做到“全手動”。下一節可以幫助我們了解這種轉換的基本規律。
2 數據交互
本節介紹如何在Stata的數據類型和Python的數據類型之間進行轉換。其適用情境為:
數據已經在Stata內存(數據編輯器)中,但想要使用Python的方法;比如,我們已經加載auto.dta數據集,但希望使用Python的matplotlib庫繪制一個以length為橫軸、weight為縱軸、price為尺寸的散點圖,并保存為dpi為300的png格式。
使用Python獲取數據或運算出數據結果,但想要使用Stata的命令;比如,我們通過tushare獲取財經數據,但希望使用Stata進行Garch(1,1)估計,求出長期波動率。
當然更復雜的是兩者間相互調用,一方的執行結果決定了另一方的執行內容,反之亦然。
2.1 Stata Function Interface (sfi)
首先必須明確,sfi是一個Python庫,因此我們始終在Python環境下載入和使用它。sfi的文檔在以下地址中:
https://www.stata.com/python/api16/
從功能(而非模塊)上梳理SFI的命令,主要包括以下4類:
創建數據表的命令,主要包括設置樣本量和添加變量,形式上以add開頭
將Stata數據轉化為Python數據的命令,形式上以get開頭
將Python數據轉化為Stata數據的命令,形式上以store或set開頭
其他命令,比如刪除變量、處理異常、管理內存等
第4類命令其實很奇怪,我們完全不需要使用sfi庫中的相關命令,轉換之后使用Stata或Python自帶的功能處理就好。因此,我們主要介紹前3類。
2.2 add族
當你希望將Python數據集轉換為Stata數據集時,這一步非常重要。因為此時,Stata內存中什么也沒有,沒有數據、沒有變量、甚至沒有工作表;因此我們需要先告訴Stata,從Python轉過來的數據集有怎樣的“長”和“寬”,Stata才能準確接納該數據集。
假設我們使用tushare庫獲取了浦發銀行的日線行情數據,我們必須先確定要將多長、多寬的數據放入Stata;本例中,我們希望放入全部的樣本(索引)和2個變量(trade_date/close),確定工作表大小的代碼如下:
. python
----------------------------------------------- python (type end to exit) ---------------
>>> import tushare as ts
>>> tsToken = 'f***7'
>>> pro = ts.pro_api(tsToken)
>>> df = pro.daily(ts_code='600000.SH')
>>> df.shape
(4000, 11)
>>> end
我們通過數據框的shape屬性了解到df數據框中有4000行(樣本)和11列(變量),根據我們的要求,最終進入Stata的是一個4000×2的數據集。接下來,我們就可以通過addObs和addVar*命令新建行列。
. python
----------------------------------------------- python (type end to exit) ---------------
>>> from sfi import Data # 引入sfi.Data
>>> Data.addObs(len(df)) # 創建與df行數一致的樣本量
>>> Data.addVarStrL('Date') # 創建字符串變量Date
>>> Data.addVarFloat('Price') # 創建浮點型變量Price
>>> end
-----------------------------------------------------------------------------------------
打開數據編輯器,我們會看到工作表中已經有兩個變量和4000行了,其中Date是空格,Price是缺失值符號.。其實,這兩步完全可以在Stata環境下完成,使用Python環境的優勢在于我們有len函數可以確定df數據框的樣本量,這是一個動態過程。
2.3 store族
確定了工作表的長寬后,我們就可以使用store命令將Python數據存入Stata中。本例中,我們將df數據框中的trade_date列存入Stata的Date列,將df數據框中的close列存入Stata的Price列;操作如下:
. python
----------------------------------------------- python (type end to exit) ---------------
>>> Data.store('Date',None,df['trade_date'])
>>> Data.store('Price',None,df['close'])
>>> end
-----------------------------------------------------------------------------------------
我們來了解一下store命令的參數。store命令的形式為store(var, obs, val[, selectvar])。參數var表示目標列名稱,可以是數值/字符串/列表/None;參數obs表示樣本量,可以是數值/列表/None,None列示全部樣本;參數val表示待存入的數據,要求是數組。selectvar是一個可選參數,默認為None,表示對存入變量的選取。需要特別注意的是obs選項,因為沒有缺省值,我們不能省略None。
2.4 get族
get族命令用于Python讀取Stata數據(和一些非數據內容)。根據讀取的數據類型差異,我們分成Data、Scalar、Matrix和Macro這4種主要類型來講:
Data.get和Data.getAsDict
因為Python是動態類型語言,因此不需要先確定數據的存儲類型和名稱。以讀取auto數據集為例:
. python
----------------------------------------------- python (type end to exit) ---------------
>>> from sfi import Data
>>> lst = Data.get('price weight length') # 列表形式
>>> lst[0]
[4099, 2930, 186]
>>> dct = Data.getAsDict('price weight length') # 字典形式
>>> import pandas as pd
>>> df = pd.DataFrame(dct) # 轉化為數據框
>>> df.head(1)
price weight length
0 4099 2930 186
>>> end
-----------------------------------------------------------------------------------------
get命令提取出的數據是列表形式,丟失了列標簽;getAsDict命令提取出的數據是字典形式,保留了列標簽(鍵)。我個人建議使用后者,并及時保存為數據框格式。此外,當我們只需要數據集中的單個值時,可以使用Data.getAt命令,該命令通過變量參數和索引參數確定唯一的位置并提取對應元素。
Scalar.getValue和Scalar.getString
除了數據集外,我們還會在Stata中使用一些“單值”,比如常數、結果、參數值等等。我們使用sfi.Scalar模塊來讀取這些內容。舉例如下:
. scalar s = "I Love You."
. display s
I Love You.
. quietly regress price weight length
. display e(r2)
.34756307
. python
----------------------------------------------- python (type end to exit) ---------------
>>> from sfi import Scalar
>>> Scalar.getString('s') # 提取字符串
'I Love You.'
>>> Scalar.getValue('e(r2)') # 提取數值
0.3475630724239044
>>> end
-----------------------------------------------------------------------------------------
當我們需要重復使用Stata命令時,Python迭代可能比Stata自己的循環效率更高,提取并記錄單值就顯得格外重要。
Matrix.get
當數據結果為矩陣時,我們使用Matrix.get命令實現提取。舉例如下:(接上例回歸結果)
. matrix list e(b)
e(b)[1,3]
weight length _cons
y1 4.6990649 -97.960312 10386.541
. python
----------------------------------------------- python (type end to exit) ---------------
>>> from sfi import Matrix
>>> Matrix.get('e(b)')
[[4.699064878412987, -97.9603118181582, 10386.540540844977]]
>>> end
-----------------------------------------------------------------------------------------
單使用Matrix.get命令會丟失矩陣的行列名稱,因此Stata還提供了Matrix.getColNames和Matrix.getRowNames來提取行列名稱。與Data.getAt類似,我們也可以通過Matrix.getAt命令提取矩陣中的單個元素,注意該命令的參數有3個,分別是矩陣名、行和列。
Macro.getLocal和Macro.getGlobal
在Stata中,Macro被稱為“宏”,分為局部宏(local)和全局宏(global)兩種;宏名稱在細分類中不能重復,但局部宏和全局宏的名稱可以相同(實際上只是命名時相同,存儲時并不同)。同樣,我們可以通過Macro.getLocal和Macro.getGlobal獲取宏。舉例如下:
. local x 1 1
. display `x'
2
. python
----------------------------------------------- python (type end to exit) ---------------
>>> from sfi import Macro
>>> Macro.getLocal('x') # 切記:宏是即用即解的
'1 1'
>>> Macro.getGlobal('e(model)') # ereturn的宏是全局宏
'ols'
>>> end
-----------------------------------------------------------------------------------------
3 小結
正如上文所言,我們的最終目的是將Python的方法封裝到Stata程序中,或者將Stata的命令封裝到Python函數中。當我們實現了數據類型的轉換后,封裝命令就一點也不難了。
但以我剛接觸的感受來講,Stata16的SFI真的非常蠢,寫到這里我竟一時間不知道如何解釋下去:
變量的長度不能自適應,我們必須先通過set obs或addobs來確定樣本量
Python的數據框(來自pandas)和Stata的數據集之間不能直接轉換
變量要先創建再填充,目標數據類型需要手動設置,缺少獲取變量名稱列表的函數
缺少獲取數字-文字對照表的函數,也沒有提取全部命令結果(包括圖形命令結果)的函數
特別地,如果我們希望將Stata數據集完整的讀入Python或反過來,我個人建議使用Excel工作表作中介。效率上差一點,但節約很多代碼。舉例如下:
. python // Stata-> Python
----------------------------------------------- python (type end to exit) ---------------
>>> import pandas as pd
>>> stata: sysuse auto.dta
(1978 Automobile Data)
>>> stata: export excel price weight length using "D:\auto.xlsx" if rep78!=., firstrow(variables) replace
>>> df = pd.read_excel(r'd:\auto.xlsx',header=0)
>>> df.head(1)
price weight length
0 4099 2930 186
>>> end
-----------------------------------------------------------------------------------------
. python // Python -> Stata
----------------------------------------------- python (type end to exit) ---------------
>>> import tushare as ts
>>> import pandas as pd
>>> tsToken = 'f***7'
>>> pro = ts.pro_api(tsToken)
>>> df = pro.daily(ts_code='600000.SH')
>>> df.to_excel(r'd:\quotes.xlsx',header=True,index=False)
>>> stata: import excel "D:\quotes.xlsx", sheet("Sheet1") firstrow
(11 vars, 4,000 obs)
>>> end
-----------------------------------------------------------------------------------------
文章作者: 金融系程老師(微信公眾號)
最后,啟發大家去讀一讀SFI的手冊,我留下了set族命令沒有介紹。這是一類在Python中設定Stata單值、暫元、矩陣等元素的命令,使用方法和get族非常類似,希望同學們有空去了解一下。
在這愉快悠長假期里,小伙伴們興高采烈的旅游、聚會、逛街、訪學、曬娃、美食,并享受其中,還沒有安排適當的學習計劃吧!趁著假期的尾巴,想不想來一場光速學習充電之旅呢!
第三屆Stata用戶大會 & Stata夏季訓練營震撼來襲,頂配雙男神|2 1模式,2019現場直擊全程八天(含會議)Stata魔鬼訓練營(機器學習 計量方法應用)。相約上海財經大學不見不散,感興趣的小伙伴一起加入吧!