R Package 制作攻略
R Package 制作攻略
我们都会使用 R Package,但是为什么要学习如何制作 R Package 呢?
一个很好的理由是:分享自己的代码,并且让他人能够更加轻松地使用它。即使你不打算分享代码,以 R Package 的方式组织自己的代码也能够节省时间、规范工作。
工具集
R Package 的制作离不开 RStudio 和 devtools 包。devtools 是一个使通用开发流程自动化的包,它希望使用函数完成尽可能多的开发工作,让开发者关注包的功能,而不是包的结构。
提到 devtools 就不能不说到它的第一作者 Hadley Wickham。他同时也是
ggplot2
的作者,对 R 语言的发展贡献颇多。
另外,我们还需要使用 roxygen2 程序包构建函数文档,使用 testthat 程序包进行单元测试。
一个简单的示例
有时候我们需要对数据框中选定的字段进行运算,得到的结果再添加到该数据框中,如下所示:
graph TD subgraph "df_2" C["a"] D["b"] E["c"] end subgraph "df_1" A["a"] B["b"] end A --> F["c = a + b"] B --> F --> E
原数据框
df_1
有a
和b
两个字段,通过a + b
产生新字段c
,添加到df_1
中,产生新数据框df_2
。
有时候还需要对数据框按照特定的字段进行排序,比如对 df_2
按照字段 a+b
的值进行降序排序。
将上述 2 个过程(函数)封装到一个 R Package 中。
加载 devtools
1 | library(devtools) |
初始化程序包目录
1 | create_pacakge("~/path/to/pkgName") |
指定 R Package 的名字和源代码的存放目录,devtools 就会自动创建目录并新建一个 R 工程,然后填充好文件框架。
.Rbuildignore
列出了我们编写 R 包时需要的,但是从源代码构建 R 包时并不需要的文件。.gitignore
为 Git 的使用做好准备。它将忽略一些由 R 或 RStudio 创建的文件。DESCRIPTION
描述了程序包的元数据。NAMESPACE
声明了程序包导出的,供外部使用的函数以及从其他程序包导入的外部函数。目前,它包含暂未起作用的用于占位的内容。R/
目录是程序包的主要部分。它很快将包含带有函数声明的.R
文件。dfOperation.Rproj
是使得该目录成为 RStudio 项目的文件。
使用 Git 做版本控制
1 | use_git() |
在 R Package 的制作过程中,难免会出现想要放弃对代码的修改,恢复到上一个版本的情况,这时候使用版本控制系统会十分方便。当然,也可以选择不使用。
使用了 Git 版本控制系统后,RStudio 右上方窗格中出现 Git 选项卡,可以在这里方便地进行存档和提交更改。
编写函数
1 | use_r("df_mutate") |
程序包中的函数应该放在 /R
子目录中的 *.R
脚本文件内。一般而言,我们会为每一个函数创建一个 *.R
文件。
use_r()
函数能够自动化地帮助我们在 /R
目录下创建或打开脚本文件。
导入其他程序包的函数
1 | use_package("dplyr") |
在 R Package 中调用其他程序包中的函数时,不能够使用 library()
,需要使用 pkg_name::func_name
的格式,通过命名空间进行调用。
使用 use_package
相当于告诉使用者,这个 R Package 需要使用其他 R Package 中的函数。
撰写并生成文档
R 函数的帮助文档也是 R Package 的重要组成部分。函数文档以特殊的标记格式放在 man/*.Rd
文件中,但是我们不需要掌握这种特殊标记语法,roxygen2 能够将特殊的注释转换成 *.Rd
文件。
将鼠标光标放在指定的函数体内,在 RStudio 中使用如下快捷键或菜单命令,就能够产生特殊格式的注释框架:
1 | Ctrl + Shift + Alt + R |
1 | #' Title |
Title
替换为函数文档的标题,Title
之下还可以添加简要的说明文字;@param
字段是函数参数的解释;@return
是对函数返回值的说明;@export
表示该函数能够被导出,以供用户使用;@example
是该函数的使用示例。
可以按照如下格式填写:
1 | #' Data Frame Mutate |
我们还需要用 document()
将这个新的 roxygen 注释转变为 man/df_mutate.Rd
。
1 | document() |
初步测试函数
1 | load_all() |
在程序包开发过程中,时刻对函数进行测试是常见且必要的。使用 load_all()
能够在防止干扰全局命名空间的情况下加载程序包。
但是,如果全局命名空间内存在同名函数,则会覆盖掉程序包中的对应函数。
Check
1 | check() |
check()
能够检查程序包结构是否完整,代码是否有问题,是否处于良好的工作状态。它会输出相当多的内容,但是阅读输出内容并尽早解决出现的问题是一个良好的习惯。
RStudio 提供了
check()
的快速调用。它位于右上角的 Build 选项卡中,通过 Check 或者键盘快捷键 Ctrl + Shift + E (WIndows & Linux) 或者 Cmd + Shift + E (MacOS)调用。
编辑程序包的基本信息
DESCRIPTION
文件提供了程序包的元数据,包括:
- 程序包名称
Package
- 程序包标题
Title
- 版本
Version
- 相关开发人员
Author@R
- 功能描述
Description
- 开源许可证
License
- 依赖的其他 Packages
Imports
- ……
其中,Packge
、Title
、Version
、Author@R
、Description
字段需要我们手动填写,其他字段能够通过函数进行设定,比如添加开源许可证。
1 | Package: dfOperation |
添加开源许可证
1 | use_cc0_license("Your_Name") |
此类函数的名称格式为:use_[License_Name]_license("Your_Name")
。比如,如果使用 MIT
协议,就调用函数 use_mit_license("Your_Name")
安装测试
1 | check() |
当 check()
的输出没有 Errors、Warnings 和 Notes 时,说明我们的 R Package 已经是一个成熟完整的程序包了,可以进行安装测试:
1 | install() |
现在,我们可以向使用其他程序包一样使用 dfOperation
了。
添加 README
将 R Package 发布到 CRAN 上是一项耗时的工作,不仅需要经过严格的测试,还需要撰写详细的文档和 Vignette。因此,我们通常会将制作的 R Package 发布到 GitHub 上。
一些 CRAN 上的 R Package 也会出现在 GitHub 上,通常在 CRAN 上发布稳定版本,在 GitHub 上发布开发版本。
既然程序包位于 GitHub 上,那么 README.md
作为程序包的主页就很重要。使用如下命令创建一个 README.Rmd
文件,它会为 README.md
文件的生成提供很多的便利。
1 | use_readme_rmd() |
生成的 README.Rmd
文件已经包含了一般的框架,我们只需要填充上内容即可(通常直接从 DESCRIPTION
文件中复制粘贴)。然后使用如下命令生成 README.md
文件:
1 | rmarkdown::render("README.Rmd") |
或者使用 Knit
菜单命令:
这一步之后,工程根目录下的文件有:
导出
可以导出为 Source Package 或者 Binary Package。二者的主要区别在于:Binary Package 已经经过了 RTools 的编译,可以直接安装;而 Source Package 属于源代码,安装时会先使用 RTools 进行编译,然后再安装。
生成源码包:
生成二进制包:
References
Hadley Wickham, Jennifer Bryan. R Packages second edition. https://r-pkgs.org/