温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。
作者: 徐云娇 (厦门大学)
邮箱: 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 工具包、如何进行机器学习分析等。
中文编译稿列表如下:
目录
在大数据时代,数据无处不在。许多政府部门、金融机构、大学、以及网络平台上都会通过应用程序接口 (Application Programming Interface, API) 提供数据。而 API 通常会将爬取的数据保存为 JSON 文件。
在这篇推文中,我们将展示如何利用 Python 爬取 API 数据、以及如何处理得到的 JSON 数据。
如果你是 Python 新手的话,那建议阅读一下 Stata/Python 深度融合系列的前四篇推文,这也许会对你有帮助。
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 中是否安装这两个包。
首先,用 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 拆分为两个字符串:API 和 date。字符串 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 将我们的查询地区限制在了美国。之后,我们可以合并字符串 API、date 和 country,将它存储为字符串 URL。这里需要注意,我们必须要在 date 和 country 之间加上 “+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
--------------------------------------------------
现在,我们准备将 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
--------------------------------------------------
我们可以使用 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))
>>> end
--------------------------------------------------
>>> fda_df
time count
0 20180101 1
1 20180102 16
2 20180103 20
3 20180104 25
4 20180105 24
最后,我们利用 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)
至此,我们已成功地将 API 指令发送给了「openFDA」,得到了 JSON 数据,并且将其转化为了 Stata 数据集格式。
类似的方法,我们也可以在「巨潮资讯」网站,根据链接 http://www.cninfo.com.cn/new/data/szse_stock.json 获取股票信息。
Note:产生如下推文列表的命令为:
lianxh Stata Python +
安装最新版lianxh
命令:
ssc install lianxh, replace
连享会-直播课 上线了!
http://lianxh.duanshu.com
免费公开课:
直击面板数据模型 - 连玉君,时长:1小时40分钟 Stata 33 讲 - 连玉君, 每讲 15 分钟. 部分直播课 课程资料下载 (PPT,dofiles等)
支持回看,所有课程可以随时购买观看。
专题 | 嘉宾 | 直播/回看视频 |
---|---|---|
⭐ 最新专题 ⭐ | DSGE, 因果推断, 空间计量等 | |
⭕ Stata数据清洗 | 游万海 | 直播, 2 小时,已上线 |
研究设计 | 连玉君 | 我的特斯拉-实证研究设计,-幻灯片- |
面板模型 | 连玉君 | 动态面板模型,-幻灯片- |
面板模型 | 连玉君 | 直击面板数据模型 [免费公开课,2小时] |
Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。
关于我们
课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法
等
连享会小程序:扫一扫,看推文,看视频……
扫码加入连享会微信群,提问交流更方便
✏ 连享会学习群-常见问题解答汇总:
✨ https://gitee.com/arlionn/WD
New!
lianxh
命令发布了:
随时搜索连享会推文、Stata 资源,安装命令如下:
. ssc install lianxh
使用详情参见帮助文件 (有惊喜):
. help lianxh