-
Notifications
You must be signed in to change notification settings - Fork 18
/
wrangling-collection.qmd
executable file
·185 lines (131 loc) · 8.91 KB
/
wrangling-collection.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# 数据获取 {#sec-data-collection}
数据获取包含两层意思,其一是数据收集,其二是数据搜集。数据收集,往往意味着自己做实验设计,执行实验,回收数据,掌握第一手资料。而数据搜集,往往意味着自己从各个地方搜罗数据,再清洗整理校验,得到可靠的二手或三手数据。从前,统计学家下到试验田,在不同NPK(氮肥、磷肥和钾肥)配比的情况下,收集小麦的产量数据,以确定最佳配比。如今,许多互联网公司都有自己的 App,通过 App 收集大量用户及其行为数据,再以一定的数据模型加工整理成可用的二维表格。此外,许多政府和非政府的组织机构网站也发布大量的数据,比如各个国家的国家统计局和地方统计局,世界银行,国际货币基金组织等。这其中,有的以图片形式发布,有的以二维表格形式发布,有的以数据 API 服务形式发布,比起散落在各个公告中要好多了。
数据收集的方式有线下发放问卷、从网络爬取、网络调查问卷、线下市场调查、走访、有奖征集、埋点等。在真实的数据分析中,有时候需要借助 SQL 或浏览器开发者工具,从不同的数据源获取数据,清洗整理,再将数据导入 R 环境。
## 从本地文件读取 {#sec-file}
利用 Base R 提供的基础函数从各类文件导入数据
### csv 文件 {#sec-csv}
小的 csv 文件,可用 Base R 提供的 `read.csv()` 函数读取。 大型 csv 文件,可用 **data.table** 的 `fread()` 函数读取。
### xlsx 文件 {#sec-xlsx}
[**readxl**](https://github.com/tidyverse/readxl) 读 xls 和 xlsx 文件,[**writexl**](https://github.com/ropensci/writexl) 写 xlsx。
[openxlsx](https://github.com/ycphs/openxlsx) 读/写 xlsx 文件
### arrow 文件 {#sec-arrow}
Apache Arrow 的 R 语言接口 [arrow](https://github.com/apache/arrow/tree/master/r) 超出内存的大规模数据操作。比如在时空数据处理场景,数据文件往往比较大,需要在远程服务器上处理超出本地计算机内存的数据,[geoarrow](https://github.com/paleolimbot/geoarrow)包和[sfarrow](https://github.com/wcjochem/sfarrow/)包都是应对此类需求。
## 从数据库中导入 {#sec-database}
从各类数据库导入数据,比如 RSQLite 等
### RSQLite
### odbc
### RJDBC
很多数据库都有 Java 接口驱动
## 从各类网页中抓取 {#sec-web-scraping}
rvest 包从网页、网站抓取数据, 再用 xml2 和 httr2 解析处理网页数据。
### 豆瓣排行榜 {#sec-douban}
### 链家二手房 {#sec-lianjia}
## 从数据接口中获取 {#sec-rest-api}
### Github
从 Github API 接口中获取托管在 Github 上的 R 包的信息,比如点赞、关注和转发的数量。首先从 CRAN 上获得 R 包元数据信息,接着筛选出托管在 Github 上的 R 包,清理出 R 包在 Github 上的网址。
```{r}
pdb <- readRDS(file = "data/cran-package-db-20231231.rds")
# 过滤出 Github
pdb <- subset(
x = pdb, subset = !duplicated(Package) & grepl(pattern = "github", x = BugReports),
select = c("Package", "Maintainer", "Title", "BugReports")
)
# 掐头去尾
pdb$repo <- sub(x = pdb$BugReports, pattern = "(http|https)://(www\\.){0,1}github\\.com/", replacement = "")
pdb$repo <- sub(x = pdb$repo, pattern = "/{1,}(issues|blob).*", replacement = "")
pdb$repo <- sub(x = pdb$repo, pattern = "/{1,}(discussions|wiki)", replacement = "")
pdb$repo <- sub(x = pdb$repo, pattern = "/$", replacement = "")
```
获取某代码仓库信息的 Github API 是 <https://api.github.com/repos> ,为了批量地访问 API ,收集想要的数据,将数据请求、结果整理的过程打包成一个函数。
```{r}
github_stats <- function(repo) {
url <- paste("https://api.github.com/repos", repo, sep = "/")
# 最多允许失败 5 次,每失败一次休息 5s
req <- xfun::retry(curl::curl_fetch_memory, url = url, .times = 5, .pause = 5)
x <- jsonlite::fromJSON(rawToChar(req$content))
# 爬失败的标记一下
if(is.null(x$stargazers_count)) x$stargazers_count <- x$subscribers_count <- x$forks_count <- -1
# 爬一个休息 1s
Sys.sleep(1)
data.frame(
repo = repo,
# 点赞 仓库上 star 的人数
stargazers_count = x$stargazers_count,
# 关注 仓库上 watch 的人数
subscribers_count = x$subscribers_count,
# 转发 仓库上 fork 的人数
forks_count = x$forks_count
)
}
```
下面测试一下这段代码,获取代码仓库 [yihui/knitr](https://github.com/yihui/knitr) 的点赞、关注和转发的人数。
```{r}
# 测试代码
github_stats(repo = "yihui/knitr")
```
理论上,使用函数 `lapply()` 遍历所有 R 包可得所需数据,将数据收集函数应用到每一个 R 包上再合并结果,即如下操作。
```{r}
#| eval: false
# 合并数据
gh_repo_db <- data.table::rbindlist(lapply(pdb$repo, github_stats))
```
实际上,在没有访问令牌的情况下,Github API 的访问次数是有限制的,只有 60 次(一段时间内)。首先在 Github 开发者设置中申请一个应用,获得应用名称(appname)、客户端 ID(key)和密钥(secret),下面借助 **httr** 包配置 OAuth 凭证。
```{r}
#| eval: false
library(httr)
# Github API Oauth2
oauth_endpoints("github")
# 应用名称(appname)、客户端 ID(key)和密钥(secret)
myapp <- oauth_app(
appname = "Application Name", key = "Client ID",
secret = "Client Secrets"
)
# 获取 OAuth 凭证
github_token <- oauth2.0_token(oauth_endpoints("github"), myapp)
# 使用 API
gtoken <- config(token = github_token)
```
修改函数 `github_stats()` 中请求 Github API 的一行代码,发送带密钥的 GET 请求。
```{r}
#| eval: false
req <- xfun::retry(GET, url = url, config = gtoken, .times = 5, .pause = 5)
```
此外,请求难免出现意外,按照上面的方式,一旦报错,数据都将丢失。因此,要预先准备存储空间,每获取一条数据就存进去,如果报错了,就打个标记。
```{r}
#| eval: false
# 准备存储数据
gh_repo_db <- data.frame(
repo = pdb$repo, stargazers_count = rep(-1, length(pdb$repo)),
subscribers_count = rep(-1, length(pdb$repo)),
forks_count = rep(-1, length(pdb$repo))
)
# 不断更新数据
while (any(gh_repo_db$stargazers_count == -1)) {
tmp <- gh_repo_db[gh_repo_db$stargazers_count == -1, ]
for (repo in tmp$repo) {
gh_repo_db[gh_repo_db$repo == repo, ] <- github_stats(repo = repo)
}
if(repo == tmp$repo[length(tmp$repo)]) break
}
```
最后,将收集整理好的数据保存到磁盘上,下面按点赞数量给 R 包排序,篇幅所限,仅展示前 20。
```{r}
gh_repo_db <- readRDS(file = "data/gh-repo-db-2023.rds")
gh_repo_db <- gh_repo_db[!duplicated(gh_repo_db$repo),]
gh_repo_db <- gh_repo_db[order(gh_repo_db$stargazers_count, decreasing = T),]
head(gh_repo_db, 20)
```
将发布在 Github 上的受欢迎的 R 包列出来了,方便读者选用,也看到一些有意思的结果。
1. 机器学习相关的 R 包靠在最前面,实际上,它们(占十之七八)多是对应软件的 R 语言接口,点赞的数目应当算上其它语言接口的贡献。
2. 在机器学习之后,依次是数据可视化(ggplot2、shiny、plotly.R、patchwork)、数据操作(dplyr、data.table、feather)和可重复性计算(bookdown、rmarkdown、knitr)、R 包开发(devtools)和生物信息(seurat)。
最后,简要说明数据的情况:以上观察结果是基于 CRAN 在 2023-12-31 发布的 R 包元数据,8475 个 R 包在 Github 托管源代码,这些 R 包的点赞、关注和转发数据是在 2024-01-30 爬取的。其中,共有 29 个 R 包不按规矩填写、改名字、换地方、甚至删库了,这些 R 包是可忽略的。当然,也存在一些 R 包并未托管在 Github 上,但质量不错,比如 glmnet 包、colorspace 包、fGarch 包等,应当是少量的。
### 中国地震台网 {#sec-earthquake-cenc}
[中国地震台网](https://news.ceic.ac.cn/) 可以想象后台有一个数据库,在页面的小窗口中输入查询条件,转化为某种 SQL 语句,传递给数据库管理系统,执行查询语句,返回查询结果,即数据。
### 美国地质调查局 {#sec-earthquake-usgs}
[美国地质调查局](https://earthquake.usgs.gov/earthquakes/search/)提供一些选项窗口,可供选择数据范围,直接下载 CSV 或 XLS 文件。
### 美国人口调查局 {#sec-census-bureau}
[美国人口调查局](https://www.census.gov/)
[**tidycensus**](https://github.com/walkerke/tidycensus/) 需要注册账号,获取使用 API 接口的访问令牌,可以想象后台不仅有一个数据库,在此之上,还有一层数据鉴权。
### 世界银行 {#sec-world-bank}
[世界银行](https://www.shihang.org/zh/home)和[国际货币基金组织](https://www.imf.org/zh/Home)
[**wbstats**](https://github.com/nset-ornl/wbstats) 包封装世界银行提供的数据接口 REST API