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 = "")
2 数据获取
数据获取包含两层意思,其一是数据收集,其二是数据搜集。数据收集,往往意味着自己做实验设计,执行实验,回收数据,掌握第一手资料。而数据搜集,往往意味着自己从各个地方搜罗数据,再清洗整理校验,得到可靠的二手或三手数据。从前,统计学家下到试验田,在不同NPK(氮肥、磷肥和钾肥)配比的情况下,收集小麦的产量数据,以确定最佳配比。如今,许多互联网公司都有自己的 App,通过 App 收集大量用户及其行为数据,再以一定的数据模型加工整理成可用的二维表格。此外,许多政府和非政府的组织机构网站也发布大量的数据,比如各个国家的国家统计局和地方统计局,世界银行,国际货币基金组织等。这其中,有的以图片形式发布,有的以二维表格形式发布,有的以数据 API 服务形式发布,比起散落在各个公告中要好多了。
数据收集的方式有线下发放问卷、从网络爬取、网络调查问卷、线下市场调查、走访、有奖征集、埋点等。在真实的数据分析中,有时候需要借助 SQL 或浏览器开发者工具,从不同的数据源获取数据,清洗整理,再将数据导入 R 环境。
2.1 从本地文件读取
利用 Base R 提供的基础函数从各类文件导入数据
2.1.1 csv 文件
小的 csv 文件,可用 Base R 提供的 read.csv()
函数读取。 大型 csv 文件,可用 data.table 的 fread()
函数读取。
2.1.2 xlsx 文件
readxl 读 xls 和 xlsx 文件,writexl 写 xlsx。
openxlsx 读/写 xlsx 文件
2.1.3 arrow 文件
Apache Arrow 的 R 语言接口 arrow 超出内存的大规模数据操作。比如在时空数据处理场景,数据文件往往比较大,需要在远程服务器上处理超出本地计算机内存的数据,geoarrow包和sfarrow包都是应对此类需求。
2.2 从数据库中导入
从各类数据库导入数据,比如 RSQLite 等
2.2.1 RSQLite
2.2.2 odbc
2.2.3 RJDBC
很多数据库都有 Java 接口驱动
2.3 从各类网页中抓取
rvest 包从网页、网站抓取数据, 再用 xml2 和 httr2 解析处理网页数据。
2.3.1 豆瓣排行榜
2.3.2 链家二手房
2.4 从数据接口中获取
2.4.1 Github
从 Github API 接口中获取托管在 Github 上的 R 包的信息,比如点赞、关注和转发的数量。首先从 CRAN 上获得 R 包元数据信息,接着筛选出托管在 Github 上的 R 包,清理出 R 包在 Github 上的网址。
获取某代码仓库信息的 Github API 是 https://api.github.com/repos ,为了批量地访问 API ,收集想要的数据,将数据请求、结果整理的过程打包成一个函数。
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 的点赞、关注和转发的人数。
repo stargazers_count subscribers_count forks_count
1 yihui/knitr 2359 116 873
理论上,使用函数 lapply()
遍历所有 R 包可得所需数据,将数据收集函数应用到每一个 R 包上再合并结果,即如下操作。
实际上,在没有访问令牌的情况下,Github API 的访问次数是有限制的,只有 60 次(一段时间内)。首先在 Github 开发者设置中申请一个应用,获得应用名称(appname)、客户端 ID(key)和密钥(secret),下面借助 httr 包配置 OAuth 凭证。
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 请求。
此外,请求难免出现意外,按照上面的方式,一旦报错,数据都将丢失。因此,要预先准备存储空间,每获取一条数据就存进去,如果报错了,就打个标记。
# 准备存储数据
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。
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)
repo stargazers_count subscribers_count forks_count
8434 dmlc/xgboost 25266 909 8707
5553 facebook/prophet 17415 425 4474
4307 mlflow/mlflow 16365 292 3793
3807 Microsoft/LightGBM 15821 437 3798
265 apache/arrow 13080 356 3220
3080 h2oai/h2o-3 6624 384 2016
2790 tidyverse/ggplot2 6210 308 2028
3430 interpretml/interpret 5894 141 706
6921 rstudio/shiny 5180 339 1818
4317 mlpack/mlpack 4668 185 1577
1754 tidyverse/dplyr 4612 246 2131
640 rstudio/bookdown 3565 122 1263
1430 Rdatatable/data.table 3437 170 977
6316 rstudio/rmarkdown 2758 146 977
2269 wesm/feather 2708 97 174
5324 plotly/plotly.R 2467 117 628
5084 thomasp85/patchwork 2344 49 159
1389 r-lib/devtools 2340 120 760
3658 yihui/knitr 2326 115 877
6868 satijalab/seurat 2034 75 867
将发布在 Github 上的受欢迎的 R 包列出来了,方便读者选用,也看到一些有意思的结果。
- 机器学习相关的 R 包靠在最前面,实际上,它们(占十之七八)多是对应软件的 R 语言接口,点赞的数目应当算上其它语言接口的贡献。
- 在机器学习之后,依次是数据可视化(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 包等,应当是少量的。
2.4.2 中国地震台网
中国地震台网 可以想象后台有一个数据库,在页面的小窗口中输入查询条件,转化为某种 SQL 语句,传递给数据库管理系统,执行查询语句,返回查询结果,即数据。
2.4.3 美国地质调查局
美国地质调查局提供一些选项窗口,可供选择数据范围,直接下载 CSV 或 XLS 文件。
2.4.4 美国人口调查局
tidycensus 需要注册账号,获取使用 API 接口的访问令牌,可以想象后台不仅有一个数据库,在此之上,还有一层数据鉴权。
2.4.5 世界银行
wbstats 包封装世界银行提供的数据接口 REST API