Stata-Python交互-6:调用APIs和JSON数据

发布时间:2021-02-22 阅读 44

Stata 连享会   主页 || 视频 || 推文

温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。

课程详情 https://gitee.com/arlionn/Course   |   lianxh.cn

课程主页 https://gitee.com/arlionn/Course

作者: 徐云娇 (厦门大学)
邮箱: jilyo@stu.xmu.edu.cn

致谢: 本文摘自以下文章,特此感谢!
Source: Chuck Huber, The Stata Blog: Stata/Python integration part 6: Working with APIs and JSON data -Link-

Stata/Python 交互系列推文 源自 Stata 公司的统计项目总监 Chuck Huber 博士发表于 Stata 官网的系列博文,一共 9 篇。较为系统地介绍了 Stata 与 Python 的交互方式,包括:如何配置你的软件、如何实现 Stata 与 Python 数据集互通、如何调用 Python 工具包、如何进行机器学习分析等。

  • Part 1: Setting up Stata to use Python -Link-
  • Part 2: Three ways to use Python in Stata -Link-
  • Part 3: How to install Python packages -Link-
  • Part 4: How to use Python packages -Link-
  • Part 5: Three-dimensional surface plots of marginal predictions -Link-
  • Part 6: Working with APIs and JSON data -Link-
  • Part 7: Machine learning with support vector machines, -Link-
  • Part 8: Using the Stata Function Interface to copy data from Stata to Python, -Link-
  • Part 9: Using the Stata Function Interface to copy data from Python to Stata, -Link-

中文编译稿列表如下:


目录


1. 引言

在大数据时代,数据无处不在。许多政府部门、金融机构、大学、以及网络平台上都会通过应用程序接口 (Application Programming Interface, API) 提供数据。而 API 通常会将爬取的数据保存为 JSON 文件。

在这篇推文中,我们将展示如何利用 Python 爬取 API 数据、以及如何处理得到的 JSON 数据。

如果你是 Python 新手的话,那建议阅读一下 Stata/Python 深度融合系列的前四篇推文,这也许会对你有帮助。

2. API 和 JSON

API 是一个应用程序接口,它可以为我们抓取平台数据提供诸多便利。API 有许多种类,并且每一个 API 的语法结构都是独一无二的。一个典型的 API 由包含查询选项的 URL 组成,比如美国 「openFDA」 API 的 URL 可以抓取美国药监局关于药品不良事件的数据。

https://api.fda.gov/drug/event.json?

可以通过对上述 API 的 URL 添加选项,以达到抓取更精确数据的目的。举个例子,下面的 URL 可以帮你抓取 2018 年 1 月 1 日至 2018 年 1 月 5 日美国发生药品不良事件次数的数据。

https://api.fda.gov/drug/event.json?search=receivedate:[20180101+TO+20180105]+AND+occurcountry:"US"+AND+patient.drug.openfda.brand_name:"Fentanyl"&count=receivedate

将上面的 URL 输入你的浏览器地址栏中时,浏览器会呈现以下 JSON 格式的数据。

JSON 是一种很受欢迎的数据格式,它包含了一系列的「键值对」,其中 “键” 和 Stata 数据集中的变量很相似,“值” 和 Stata 中的观测值相似。如,{"time" : "20180101"} 中,键就是 "time:" ,而值就是 "20180101"。

JSON 数据通常是嵌套的。如,"time:"、"count:" 键都嵌套在 "results:" 键下,并且 JSON 嵌套格式下最顶端的两个键是 "meta:" 和 "results:"。

我们的目标是利用 「openFDA」 的 API 爬取关于药品不良事件的数据,并且将嵌套的 JSON 数据转化为 Stata 的数据集。

接下来,我们将调入 requests 和 pandas 包,所以需要确认一下 Python 中是否安装这两个包。

3. 为 API 定义一个 URL

首先,用 Python 定义字符串 URL,并将 https://open.fda.gov/ 赋值给 URL

Stata 16 中代码如下:

python:
URL = 'https://api.fda.gov/drug/event.json'
URL
end

第三行输入 URL 是为了展示字符串 URL 中的内容。

. python:
------------------ python (type end to exit) -----
>>> URL = 'https://api.fda.gov/drug/event.json'
>>> URL
'https://api.fda.gov/drug/event.json'
>>> end
--------------------------------------------------

然后,我们可以通过在 API 指令中加入搜索范围和数值来定制需要抓取的数据。具体限定条件,我们可以在 「openFDA」 官网查看相关查询指令的「语法结构」,包括「可检索范围」「例子」等。

如,我们将搜索的范围限制在 2018 年 1 月 1 日至 2018 年 1 月 5 日间,可以通过添加 ?search 选项来实现。为了提高代码的可读性,我们将 URL 拆分为两个字符串:APIdate。字符串 API 包含了基础 API 指令的 URL 地址,字符串 date 则将搜索范围限制在 2018 年 1 月 1 日至 2018 年 1 月 5 日。之后,我们将两个字符串合并起来即可。

python:
API  = 'https://api.fda.gov/drug/event.json?search='
date = 'receivedate:[20180101+TO+20180105]'
URL  = API + date
URL
end
. python:
------------------ python (type end to exit) -----
>>> API     = 'https://api.fda.gov/drug/event.json?search='
>>> date    = 'receivedate:[20180101+TO+20180105]'
>>> URL     = API + date
>>> URL
'https://api.fda.gov/drug/event.json?search=receivedate:[20180101+TO+20180105]'
>>> end
--------------------------------------------------

进一步,我们将地区限制在美国。在下例中,字符串 country 将我们的查询地区限制在了美国。之后,我们可以合并字符串 APIdatecountry,将它存储为字符串 URL。这里需要注意,我们必须要在 datecountry 之间加上 “+AND+”

python:
API     = 'https://api.fda.gov/drug/event.json?search='
date    = 'receivedate:[20180101+TO+20180105]'
country = 'occurcountry:"US"'
URL     = API + date + "+AND+" + country
URL
end

此时,虽然 API 指令变得更加复杂了,但是我们的代码可读性依旧很高。

. python:
------------------ python (type end to exit) -----
>>> API     = 'https://api.fda.gov/drug/event.json?search='
>>> date    = 'receivedate:[20180101+TO+20180105]'
>>> country = 'occurcountry:"US"'
>>> URL     = API + date + "+AND+" + country
>>> URL
'https://api.fda.gov/drug/event.json?search=receivedate:[20180101+TO+20180105]+
> AND+occurcountry:"US"'
>>> end
--------------------------------------------------

同理,我们通过添加字符串 drug 可以进一步限制药品不良事件涉及的药品为 Fentanyl。

python:
API     = 'https://api.fda.gov/drug/event.json?search='
date    = 'receivedate:[20180101+TO+20180105]'
country = 'occurcountry:"US"'
drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
URL     = API + date + "+AND+" + country + "+AND+" + drug
URL
end

最后,我们在我们的数据中加入每一天药品不良事件发生的次数。要注意的是,连接字符串 data 的是 & 而不是 +AND+

python:
API     = 'https://api.fda.gov/drug/event.json?search='
date    = 'receivedate:[20180101+TO+20180105]'
country = 'occurcountry:"US"'
drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
data    = 'count=receivedate'
URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
URL
end
. python:
------------------ python (type end to exit) -----
>>> API     = 'https://api.fda.gov/drug/event.json?search='
>>> date    = 'receivedate:[20180101+TO+20180105]'
>>> country = 'occurcountry:"US"'
>>> drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
>>> data    = 'count=receivedate'
>>> URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
>>> URL
'https://api.fda.gov/drug/event.json?search=receivedate:[20180101+TO+20180105]+
> AND+occurcountry:"US"+AND+patient.drug.openfda.brand_name:"Fentanyl"&count=re
> ceivedate'
>>> end
--------------------------------------------------

4. 通过 API 指令抓取数据

现在,我们准备将 API 指令发送至 openFDA 数据服务器。首先调入 requests 包,之后通过 requests.get() 将 API 指令的 URL 发送出去,最后得到的 JSON 数据,并放在 data 中。

python:
import requests
API     = 'https://api.fda.gov/drug/event.json?search='
date    = 'receivedate:[20180101+TO+20180105]'
country = 'occurcountry:"US"'
drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
data    = 'count=receivedate'
URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
data    = requests.get(URL).json()
data
end

我们可以输入 data 以查看 data 中的内容,但此时屏幕列示的 data 可读性较差,如下所示:

. python:
------------------ python (type end to exit) -----
>>> import requests
>>> API     = 'https://api.fda.gov/drug/event.json?search='
>>> date    = 'receivedate:[20180101+TO+20180105]'
>>> country = 'occurcountry:"US"'
>>> drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
>>> data    = 'count=receivedate'
>>> URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
>>> data    = requests.get(URL).json()
>>> data
{'meta': {'disclaimer': 'Do not rely on openFDA to make decisions regarding med
> ical care. While we make every effort to ensure that data is accurate, you sh
> ould assume all results are unvalidated. We may limit or otherwise restrict y
> our access to the API in line with our Terms of Service.', 'terms': 'https://
> open.fda.gov/terms/', 'license': 'https://open.fda.gov/license/', 'last_updat
> ed': '2020-10-24'}, 'results': [{'time': '20180101', 'count': 1}, {'time': '2
> 0180102', 'count': 16}, {'time': '20180103', 'count': 20}, {'time': '20180104
> ', 'count': 25}, {'time': '20180105', 'count': 24}]}
>>> end
--------------------------------------------------

为了提高数据的可读性,我们可以加载 json 模块。利用 dumps() 对 JSON 数据进行编码,indent=4 选项可以让每一层级的嵌套数据在显示时实现缩进,sort_keys=True 选项可以对数据进行排序,print()告诉 Python 把 dumps() 得到的结果列示在结果窗口。

python:
import requests
import json
API     = 'https://api.fda.gov/drug/event.json?search='
date    = 'receivedate:[20180101+TO+20180105]'
country = 'occurcountry:"US"'
drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
data    = 'count=receivedate'
URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
data    = requests.get(URL).json()
print(json.dumps(data, indent=4, sort_keys=True))
end

现在,结果窗口列示的数据可读性大大提高了。我们可以看到数据嵌套在 "meta" 和 "results" 键下,"meta" 键包含了免责声明、数据最新的更新时间、URL 的许可证以及有效期限。虽然这些都是有用的信息,但是可能并不需要,而 "results" 键下存储的数据才是我们想要的。

. python:
------------------ python (type end to exit) -----
>>> import requests
>>> import json
>>> API     = 'https://api.fda.gov/drug/event.json?search='
>>> date    = 'receivedate:[20180101+TO+20180105]'
>>> country = 'occurcountry:"US"'
>>> drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
>>> data    = 'count=receivedate'
>>> URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
>>> data    = requests.get(URL).json()
>>> print(json.dumps(data, indent=4, sort_keys=True))
{
    "meta": {
        "disclaimer": "Do not rely on openFDA to make decisions regarding medic
> al care. While we make every effort to ensure that data is accurate, you shou
> ld assume all results are unvalidated. We may limit or otherwise restrict you
> r access to the API in line with our Terms of Service.",
        "last_updated": "2020-10-24",
        "license": "https://open.fda.gov/license/",
        "terms": "https://open.fda.gov/terms/"
    },
    "results": [
        {
            "count": 1,
            "time": "20180101"
        },
        {
            "count": 16,
            "time": "20180102"
        },
        {
            "count": 20,
            "time": "20180103"
        },
        {
            "count": 25,
            "time": "20180104"
        },
        {
            "count": 24,
            "time": "20180105"
        }
    ]
}
>>> end
--------------------------------------------------

5. 将 JSON 数据转化为 Stata 数据集

我们可以使用 requests.get() 来提取 "results" 键下存储的数据,并且将它放在 fdadata 中。

python:
import requests
import json
API     = 'https://api.fda.gov/drug/event.json?search='
date    = 'receivedate:[20180101+TO+20180105]'
country = 'occurcountry:"US"'
drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
data    = 'count=receivedate'
URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
data    = requests.get(URL).json()
fdadata = data.get('results', [])
print(json.dumps(fdadata, indent=4, sort_keys=True))
end
. python:
------------------ python (type end to exit) -----
>>> import requests
>>> import json
>>> API     = 'https://api.fda.gov/drug/event.json?search='
>>> date    = 'receivedate:[20180101+TO+20180105]'
>>> country = 'occurcountry:"US"'
>>> drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
>>> data    = 'count=receivedate'
>>> URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
>>> data    = requests.get(URL).json()
>>> fdadata = data.get('results', [])
>>> print(json.dumps(fdadata, indent=4, sort_keys=True))
[
    {
        "count": 1,
        "time": "20180101"
    },
    {
        "count": 16,
        "time": "20180102"
    },
    {
        "count": 20,
        "time": "20180103"
    },
    {
        "count": 25,
        "time": "20180104"
    },
    {
        "count": 24,
        "time": "20180105"
    }
]
>>> end
--------------------------------------------------

fdadata 中的数据仍然是以键值对存在,我们需要把它进一步转化为 dataframe 形式的。因此,我们需要调入 pandas 模块,然后利用 read_json() 来读取 fdadata 中的数据并存为 fda_df

python:
import requests
import json
import pandas as pd
API     = 'https://api.fda.gov/drug/event.json?search='
date    = 'receivedate:[20180101+TO+20180105]'
country = 'occurcountry:"US"'
drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
data    = 'count=receivedate'
URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
data    = requests.get(URL).json()
fdadata = data.get('results', [])
fda_df  = pd.read_json(json.dumps(fdadata))
fda_df
end
. python:
------------------ python (type end to exit) -----
>>> import requests
>>> import json
>>> import pandas as pd
>>> API     = 'https://api.fda.gov/drug/event.json?search='
>>> date    = 'receivedate:[20180101+TO+20180105]'
>>> country = 'occurcountry:"US"'
>>> drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
>>> data    = 'count=receivedate'
>>> URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
>>> data    = requests.get(URL).json()
>>> fdadata = data.get('results', [])
>>> fda_df  = pd.read_json(json.dumps(fdadata))
>>> fda_df
       time  count
0  20180101      1
1  20180102     16
2  20180103     20
3  20180104     25
4  20180105     24
>>> end
--------------------------------------------------

最后,我们利用 to_stata()fda_df 保存为「fentanyl.dta」,选项 version=118 表示数据将被存储为 Stata 16 版本的数据文件。

python:
import requests
import json
import pandas as pd
API     = 'https://api.fda.gov/drug/event.json?search='
date    = 'receivedate:[20180101+TO+20180105]'
country = 'occurcountry:"US"'
drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
data    = 'count=receivedate'
URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data
data    = requests.get(URL).json()
fdadata = data.get('results', [])
fda_df  = pd.read_json(json.dumps(fdadata))
fda_df.to_stata('fentanyl.dta', version=118)
end

列示「fentanyl.dta」中的数据:

. use fentanyl.dta, clear

. list

     +--------------------------+
     | index       time   count |
     |--------------------------|
  1. |     0   20180101       1 |
  2. |     1   20180102      16 |
  3. |     2   20180103      20 |
  4. |     3   20180104      25 |
  5. |     4   20180105      24 |
     +--------------------------+

现在,我们已经学会修改 API 指令来拓展数据的范围,如,将日期变更为 2010 年 1 月 1 日至 2020 年 1 月 1 日 (见最下面的代码块),并绘制下图:

python:
# Import packages
import requests
import json
import pandas as pd

# Construct the URL for the API call
API     = 'https://api.fda.gov/drug/event.json?search='
date    = 'receivedate:[20100101+TO+20200101]'
country = 'occurcountry:"US"'
drug    = 'patient.drug.openfda.brand_name:"Fentanyl"'
data    = 'count=receivedate'
URL     = API + date + "+AND+" + country + "+AND+" + drug + "&" + data

# Submit the API data request
data = requests.get(URL).json()

# Extract the 'results' part of the JSON data
fdadata = data.get('results', [])

# Convert the JSON data to a pandas data frame
fda_df  = pd.read_json(json.dumps(fdadata))

# Use pandas to write the data frame to a Stata 16 dataset
fda_df.to_stata('fentanyl.dta', version=118)

end

use fentanyl.dta, clear
drop index
generate date = mofd(date(string(time, "%8.0f"),"YMD"))
format date %tm
collapse (sum) count, by(date)
tsset date, monthly

twoway (line count date, lcolor(blue) lwidth(medthick)),    ///
ytitle("Adverse Events Reported to the FDA")         ///
ylabel(0(2000)8000, angle(horizontal) grid)          ///
xtitle("")                                           ///
title("Fentanyl Adverse Events Reported to the FDA") ///
caption(Data Source: openFDA, size(small))           ///
scheme(s1color)

6. 结语

至此,我们已成功地将 API 指令发送给了「openFDA」,得到了 JSON 数据,并且将其转化为了 Stata 数据集格式。

类似的方法,我们也可以在「巨潮资讯」网站,根据链接 http://www.cninfo.com.cn/new/data/szse_stock.json 获取股票信息。

7. 相关推文

Note:产生如下推文列表的命令为:
lianxh Stata Python +
安装最新版 lianxh 命令:
ssc install lianxh, replace

相关课程

连享会-直播课 上线了!
http://lianxh.duanshu.com

免费公开课:


课程一览

支持回看,所有课程可以随时购买观看。

专题 嘉宾 直播/回看视频
最新专题 DSGE, 因果推断, 空间计量等
Stata数据清洗 游万海 直播, 2 小时,已上线
研究设计 连玉君 我的特斯拉-实证研究设计-幻灯片-
面板模型 连玉君 动态面板模型-幻灯片-
面板模型 连玉君 直击面板数据模型 [免费公开课,2小时]

Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。


关于我们

  • Stata连享会 由中山大学连玉君老师团队创办,定期分享实证分析经验。直播间 有很多视频课程,可以随时观看。
  • 连享会-主页知乎专栏,300+ 推文,实证分析不再抓狂。
  • 公众号推文分类: 计量专题 | 分类推文 | 资源工具。推文分成 内生性 | 空间计量 | 时序面板 | 结果输出 | 交乘调节 五类,主流方法介绍一目了然:DID, RDD, IV, GMM, FE, Probit 等。
  • 公众号关键词搜索/回复 功能已经上线。大家可以在公众号左下角点击键盘图标,输入简要关键词,以便快速呈现历史推文,获取工具软件和数据下载。常见关键词:课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法

连享会主页  lianxh.cn
连享会主页 lianxh.cn

连享会小程序:扫一扫,看推文,看视频……

扫码加入连享会微信群,提问交流更方便

✏ 连享会学习群-常见问题解答汇总:
https://gitee.com/arlionn/WD

New! lianxh 命令发布了:
随时搜索连享会推文、Stata 资源,安装命令如下:
. ssc install lianxh
使用详情参见帮助文件 (有惊喜):
. help lianxh