如何在Stata16中调用Python

论坛 期权论坛 期权     
金融系程老师   2019-7-20 20:09   11617   0
(代码较宽,建议横屏阅读)
面对大数据时代,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的用户却毫无激励。来都来了,我们简单看看这个接口吧。
[h2]1 启动Python环境[/h2][h3]1.1 配置Stata与Python的关联[/h3]首先,你需要在电脑上同时安装好Stata和Python。如果你还没有安装Python,建议下载Anaconda,版本为3.X。地址如下:
https://www.anaconda.com/distribution/
假设我们将Anaconda安装在D盘根目录下,会发现Python软件的地址为
D:\Anaconda3\python.exe
打开Stata16,在命令栏输入
  1. set python_exec D:\Anaconda3\python.exe
复制代码
Stata和Python就算关联好了。
尝试一下:在命令栏键入
  1. python
复制代码
,出现以下结果就算成功了。
  1. . python
  2. ----------------------------------------------- python (type end to exit) ---------------
  3. >>>
复制代码
[h3]1.2 进入Python环境[/h3]在Stata界面下进入Python环境的方式有3种:
完全进入键入
  1. python
复制代码
可完全进入Python环境,命令提示符从
  1. .
复制代码
变为
  1. >>>
复制代码
。此后,我们可以自由输入Python代码,直到输入
  1. end
复制代码
命令退出该环境。举例如下:
  1. . python
  2. ----------------------------------------------- python (type end to exit) ---------------
  3. >>> print('Hello NJUPT!')
  4. Hello NJUPT!
  5. >>> city = 'NJUPT'[:2]
  6. >>> print('I LOVE', end=' '); print(city)
  7. I LOVE NJ
  8. >>> end
  9. -----------------------------------------------------------------------------------------
复制代码
半完全进入键入
  1. python:
复制代码
可进入Python环境,但这种进入并不稳定,一旦出现错误就会回到Stata环境中。举例如下:
  1. . python:
  2. ----------------------------------------------- python (type end to exit) ---------------
  3. >>> print('I am a teacher.')
  4. I am a teacher.
  5. >>> print(1/0)
  6. Traceback (most recent call last):
  7.   File "", line 1, in
  8. ZeroDivisionError: division by zero
  9. -----------------------------------------------------------------------------------------
  10. r(7102);
  11. .
复制代码
非完全进入在
  1. python:
复制代码
后直接键入一个或多个命令,可以在不跳出Stata环境的前提下,临时性地执行Python语法。举例如下:
  1. . python: print('I Love You.'); print('I Love You, too.')
  2. I Love You.
  3. I Love You, too.
复制代码
非完全进入非常有用,能使我们在撰写Stata小程序时直接调用Python的方法。
[h3]1.3 在Python环境下调用Stata[/h3]当(半)完全地进入Python环境时,又如何临时调用Stata命令呢?很简单,使用
  1. stata:
复制代码
前缀即可。举例如下:
  1. . python
  2. ----------------------------------------------- python (type end to exit) ---------------
  3. >>> stata: sysuse auto.dta, clear
  4. (1978 Automobile Data)
  5. >>> stata: regress price weight, nohead nocons
  6. ------------------------------------------------------------------------------
  7.        price |      Coef.   Std. Err.      t    P>|t|     [95% Conf. Interval]
  8. -------------+----------------------------------------------------------------
  9.       weight |   2.041977   .0926943    22.03   0.000     1.857238    2.226717
  10. ------------------------------------------------------------------------------
  11. >>> end
  12. -----------------------------------------------------------------------------------------
复制代码
[h3]1.4 数据类型不兼容问题[/h3]刚拿到Stata16,我还无法完全摸清Stata和Python在数据类型上的兼容性,比如:
  1. . python
  2. ----------------------------------------------- python (type end to exit) ---------------
  3. >>> a = 1+1
  4. >>> stata: display a
  5. a not found
  6. r(111);
  7. >>> stata: scalar x = a
  8. >>> stata: display x
  9. 2
  10. >>> end
  11. -----------------------------------------------------------------------------------------
复制代码
为了保证代码执行的正确性,我们尽可能在两种软件的数据类型转换上做到“全手动”。下一节可以帮助我们了解这种转换的基本规律。
[h2]2 数据交互[/h2]本节介绍如何在Stata的数据类型和Python的数据类型之间进行转换。其适用情境为:
  • 数据已经在Stata内存(数据编辑器)中,但想要使用Python的方法;比如,我们已经加载auto.dta数据集,但希望使用Python的matplotlib库绘制一个以length为横轴、weight为纵轴、price为尺寸的散点图,并保存为dpi为300的png格式。
  • 使用Python获取数据或运算出数据结果,但想要使用Stata的命令;比如,我们通过tushare获取财经数据,但希望使用Stata进行Garch(1,1)估计,求出长期波动率。
  • 当然更复杂的是两者间相互调用,一方的执行结果决定了另一方的执行内容,反之亦然。
[h3]2.1 Stata Function Interface (sfi)[/h3]首先必须明确,
  1. sfi
复制代码
是一个Python库,因此我们始终在Python环境下载入和使用它。
  1. sfi
复制代码
的文档在以下地址中:
https://www.stata.com/python/api16/
从功能(而非模块)上梳理SFI的命令,主要包括以下4类:
  • 创建数据表的命令,主要包括设置样本量和添加变量,形式上以
    1. add
    复制代码
    开头
  • 将Stata数据转化为Python数据的命令,形式上以
    1. get
    复制代码
    开头
  • 将Python数据转化为Stata数据的命令,形式上以
    1. store
    复制代码
    1. set
    复制代码
    开头
  • 其他命令,比如删除变量、处理异常、管理内存等
第4类命令其实很奇怪,我们完全不需要使用
  1. sfi
复制代码
库中的相关命令,转换之后使用Stata或Python自带的功能处理就好。因此,我们主要介绍前3类。
[h3]2.2 add族[/h3]当你希望将Python数据集转换为Stata数据集时,这一步非常重要。因为此时,Stata内存中什么也没有,没有数据、没有变量、甚至没有工作表;因此我们需要先告诉Stata,从Python转过来的数据集有怎样的“长”和“宽”,Stata才能准确接纳该数据集。
假设我们使用tushare库获取了浦发银行的日线行情数据,我们必须先确定要将多长、多宽的数据放入Stata;本例中,我们希望放入全部的样本(索引)和2个变量(trade_date/close),确定工作表大小的代码如下:
  1. . python
  2. ----------------------------------------------- python (type end to exit) ---------------
  3. >>> import tushare as ts
  4. >>> tsToken = 'f***7'
  5. >>> pro = ts.pro_api(tsToken)
  6. >>> df = pro.daily(ts_code='600000.SH')
  7. >>> df.shape
  8. (4000, 11)
  9. >>> end
复制代码
我们通过数据框的shape属性了解到df数据框中有4000行(样本)和11列(变量),根据我们的要求,最终进入Stata的是一个4000×2的数据集。接下来,我们就可以通过
  1. addObs
复制代码
  1. addVar*
复制代码
命令新建行列。
  1. . python
  2. ----------------------------------------------- python (type end to exit) ---------------
  3. >>> from sfi import Data       # 引入sfi.Data
  4. >>> Data.addObs(len(df))       # 创建与df行数一致的样本量
  5. >>> Data.addVarStrL('Date')    # 创建字符串变量Date
  6. >>> Data.addVarFloat('Price')  # 创建浮点型变量Price
  7. >>> end
  8. -----------------------------------------------------------------------------------------
复制代码
打开数据编辑器,我们会看到工作表中已经有两个变量和4000行了,其中Date是空格,Price是缺失值符号
  1. .
复制代码
。其实,这两步完全可以在Stata环境下完成,使用Python环境的优势在于我们有len函数可以确定df数据框的样本量,这是一个动态过程。
[h3]2.3 store族[/h3]确定了工作表的长宽后,我们就可以使用
  1. store
复制代码
命令将Python数据存入Stata中。本例中,我们将df数据框中的trade_date列存入Stata的Date列,将df数据框中的close列存入Stata的Price列;操作如下:
  1. . python
  2. ----------------------------------------------- python (type end to exit) ---------------
  3. >>> Data.store('Date',None,df['trade_date'])
  4. >>> Data.store('Price',None,df['close'])
  5. >>> end
  6. -----------------------------------------------------------------------------------------
复制代码
我们来了解一下
  1. store
复制代码
命令的参数。
  1. store
复制代码
命令的形式为store(var, obs, val[, selectvar])。参数var表示目标列名称,可以是数值/字符串/列表/None;参数obs表示样本量,可以是数值/列表/None,None列示全部样本;参数val表示待存入的数据,要求是数组。selectvar是一个可选参数,默认为None,表示对存入变量的选取。需要特别注意的是obs选项,因为没有缺省值,我们不能省略None。
[h3]2.4 get族[/h3]
  1. get
复制代码
族命令用于Python读取Stata数据(和一些非数据内容)。根据读取的数据类型差异,我们分成Data、Scalar、Matrix和Macro这4种主要类型来讲:
Data.get和Data.getAsDict因为Python是动态类型语言,因此不需要先确定数据的存储类型和名称。以读取auto数据集为例:
  1. . python
  2. ----------------------------------------------- python (type end to exit) ---------------
  3. >>> from sfi import Data
  4. >>> lst = Data.get('price weight length')        # 列表形式
  5. >>> lst[0]
  6. [4099, 2930, 186]
  7. >>> dct = Data.getAsDict('price weight length')  # 字典形式
  8. >>> import pandas as pd
  9. >>> df = pd.DataFrame(dct)                       # 转化为数据框
  10. >>> df.head(1)
  11.    price  weight  length
  12. 0   4099    2930     186
  13. >>> end
  14. -----------------------------------------------------------------------------------------
复制代码
  1. get
复制代码
命令提取出的数据是列表形式,丢失了列标签;
  1. getAsDict
复制代码
命令提取出的数据是字典形式,保留了列标签(键)。我个人建议使用后者,并及时保存为数据框格式。此外,当我们只需要数据集中的单个值时,可以使用
  1. Data.getAt
复制代码
命令,该命令通过变量参数和索引参数确定唯一的位置并提取对应元素。
Scalar.getValue和Scalar.getString除了数据集外,我们还会在Stata中使用一些“单值”,比如常数、结果、参数值等等。我们使用
  1. sfi.Scalar
复制代码
模块来读取这些内容。举例如下:
  1. . scalar s = "I Love You."
  2. . display s
  3. I Love You.
  4. . quietly regress price weight length
  5. . display e(r2)
  6. .34756307
  7. . python
  8. ----------------------------------------------- python (type end to exit) ---------------
  9. >>> from sfi import Scalar
  10. >>> Scalar.getString('s')     # 提取字符串
  11. 'I Love You.'
  12. >>> Scalar.getValue('e(r2)')  # 提取数值
  13. 0.3475630724239044
  14. >>> end
  15. -----------------------------------------------------------------------------------------
复制代码
当我们需要重复使用Stata命令时,Python迭代可能比Stata自己的循环效率更高,提取并记录单值就显得格外重要。
Matrix.get当数据结果为矩阵时,我们使用
  1. Matrix.get
复制代码
命令实现提取。举例如下:(接上例回归结果)
  1. . matrix list e(b)
  2. e(b)[1,3]
  3.         weight      length       _cons
  4. y1   4.6990649  -97.960312   10386.541
  5. . python
  6. ----------------------------------------------- python (type end to exit) ---------------
  7. >>> from sfi import Matrix
  8. >>> Matrix.get('e(b)')
  9. [[4.699064878412987, -97.9603118181582, 10386.540540844977]]
  10. >>> end
  11. -----------------------------------------------------------------------------------------
复制代码
单使用
  1. Matrix.get
复制代码
命令会丢失矩阵的行列名称,因此Stata还提供了
  1. Matrix.getColNames
复制代码
  1. Matrix.getRowNames
复制代码
来提取行列名称。与
  1. Data.getAt
复制代码
类似,我们也可以通过
  1. Matrix.getAt
复制代码
命令提取矩阵中的单个元素,注意该命令的参数有3个,分别是矩阵名、行和列。
Macro.getLocal和Macro.getGlobal在Stata中,Macro被称为“宏”,分为局部宏(local)和全局宏(global)两种;宏名称在细分类中不能重复,但局部宏和全局宏的名称可以相同(实际上只是命名时相同,存储时并不同)。同样,我们可以通过
  1. Macro.getLocal
复制代码
  1. Macro.getGlobal
复制代码
获取宏。举例如下:
  1. . local x 1+1
  2. . display `x'
  3. 2
  4. . python
  5. ----------------------------------------------- python (type end to exit) ---------------
  6. >>> from sfi import Macro
  7. >>> Macro.getLocal('x')          [i]# 切记:[/i][i]宏是即用即解的[/i]
  8. '1+1'
  9. >>> Macro.getGlobal('e(model)')  [i]# ereturn的宏是全局宏[/i]
  10. 'ols'
  11. >>> end
  12. -----------------------------------------------------------------------------------------
复制代码
[h2]3 小结[/h2]正如上文所言,我们的最终目的是将Python的方法封装到Stata程序中,或者将Stata的命令封装到Python函数中。当我们实现了数据类型的转换后,封装命令就一点也不难了。
但以我刚接触的感受来讲,Stata16的SFI真的非常蠢,写到这里我竟一时间不知道如何解释下去:
  • 变量的长度不能自适应,我们必须先通过
    1. set obs
    复制代码
    1. addobs
    复制代码
    来确定样本量
  • Python的数据框(来自pandas)和Stata的数据集之间不能直接转换
  • 变量要先创建再填充,目标数据类型需要手动设置,缺少获取变量名称列表的函数
  • 缺少获取数字-文字对照表的函数,也没有提取全部命令结果(包括图形命令结果)的函数
特别地,如果我们希望将Stata数据集完整的读入Python或反过来,我个人建议使用Excel工作表作中介。效率上差一点,但节约很多代码。举例如下:
  1. . python // Stata-> Python
  2. ----------------------------------------------- python (type end to exit) ---------------
  3. >>> import pandas as pd
  4. >>> stata: sysuse auto.dta
  5. (1978 Automobile Data)
  6. >>> stata: export excel price weight length using "D:\auto.xlsx" if rep78!=., firstrow(variables) replace
  7. >>> df = pd.read_excel(r'd:\auto.xlsx',header=0)
  8. >>> df.head(1)
  9.    price  weight  length
  10. 0   4099    2930     186
  11. >>> end
  12. -----------------------------------------------------------------------------------------
  13. . python // Python -> Stata
  14. ----------------------------------------------- python (type end to exit) ---------------
  15. >>> import tushare as ts
  16. >>> import pandas as pd
  17. >>> tsToken = 'f***7'
  18. >>> pro = ts.pro_api(tsToken)
  19. >>> df = pro.daily(ts_code='600000.SH')
  20. >>> df.to_excel(r'd:\quotes.xlsx',header=True,index=False)
  21. >>> stata: import excel "D:\quotes.xlsx", sheet("Sheet1") firstrow
  22. (11 vars, 4,000 obs)
  23. >>> end
  24. -----------------------------------------------------------------------------------------
复制代码
最后,启发大家去读一读SFI的手册,我留下了
  1. set
复制代码
族命令没有介绍。这是一类在Python中设定Stata单值、暂元、矩阵等元素的命令,使用方法和
  1. get
复制代码
族非常类似,希望同学们有空去了解一下。
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:10
帖子:2
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP