2  数据获取

数据获取包含两层意思,其一是数据收集,其二是数据搜集。数据收集,往往意味着自己做实验设计,执行实验,回收数据,掌握第一手资料。而数据搜集,往往意味着自己从各个地方搜罗数据,再清洗整理校验,得到可靠的二手或三手数据。从前,统计学家下到试验田,在不同NPK(氮肥、磷肥和钾肥)配比的情况下,收集小麦的产量数据,以确定最佳配比。如今,许多互联网公司都有自己的 App,通过 App 收集大量用户及其行为数据,再以一定的数据模型加工整理成可用的二维表格。此外,许多政府和非政府的组织机构网站也发布大量的数据,比如各个国家的国家统计局和地方统计局,世界银行,国际货币基金组织等。这其中,有的以图片形式发布,有的以二维表格形式发布,有的以数据 API 服务形式发布,比起散落在各个公告中要好多了。

数据收集的方式有线下发放问卷、从网络爬取、网络调查问卷、线下市场调查、走访、有奖征集、埋点等。在真实的数据分析中,有时候需要借助 SQL 或浏览器开发者工具,从不同的数据源获取数据,清洗整理,再将数据导入 R 环境。

2.1 从本地文件读取

利用 Base R 提供的基础函数从各类文件导入数据

2.1.1 csv 文件

小的 csv 文件,可用 Base R 提供的 read.csv() 函数读取。 大型 csv 文件,可用 data.tablefread() 函数读取。

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 上的网址。

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 ,收集想要的数据,将数据请求、结果整理的过程打包成一个函数。

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 的点赞、关注和转发的人数。

# 测试代码
github_stats(repo = "yihui/knitr")
         repo stargazers_count subscribers_count forks_count
1 yihui/knitr             2359               116         873

理论上,使用函数 lapply() 遍历所有 R 包可得所需数据,将数据收集函数应用到每一个 R 包上再合并结果,即如下操作。

# 合并数据
gh_repo_db <- data.table::rbindlist(lapply(pdb$repo, github_stats))

实际上,在没有访问令牌的情况下,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 请求。

req <- xfun::retry(GET, url = url, config = gtoken, .times = 5, .pause = 5)

此外,请求难免出现意外,按照上面的方式,一旦报错,数据都将丢失。因此,要预先准备存储空间,每获取一条数据就存进去,如果报错了,就打个标记。

# 准备存储数据
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 包列出来了,方便读者选用,也看到一些有意思的结果。

  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 包等,应当是少量的。

2.4.2 中国地震台网

中国地震台网 可以想象后台有一个数据库,在页面的小窗口中输入查询条件,转化为某种 SQL 语句,传递给数据库管理系统,执行查询语句,返回查询结果,即数据。

2.4.3 美国地质调查局

美国地质调查局提供一些选项窗口,可供选择数据范围,直接下载 CSV 或 XLS 文件。

2.4.4 美国人口调查局

美国人口调查局

tidycensus 需要注册账号,获取使用 API 接口的访问令牌,可以想象后台不仅有一个数据库,在此之上,还有一层数据鉴权。

2.4.5 世界银行

世界银行国际货币基金组织

wbstats 包封装世界银行提供的数据接口 REST API