写一个R包,大概要经过以下几个步骤:
1)准备一个R包,含有一些必需文件和文件夹。
2)准备R包需要用到的函数和测试数据,并写函数和测试数据的注释。
3)将函数封装成一个包。
4)写说明书。(非必须)
5)将R包上传到github,方便修改和维护。(非必须)
6)补充:遇到问题
以下介绍各个步骤的要点:
1、准备一个R包
在介绍这个之前,需要先了解一下R包的结构。 我们可以通过Rstudio来获得一个基本的R包模板,模板里就是R包的结构和必需内容。通过Rstudio→File→New Project→New Directory→R package,填写R包名称和创建路径,获得一个名为“myPackage”的R包,里面有以下的文件或文件夹,是一个R包必须有的:
myPackage(见图1)
| ------ man文件夹:用于存放.Rd文件的文件夹,里面有一个示例文件hello.Rd。
| ------ R文件夹:用于存放R函数的文件夹,里面有一个示例文件hello.R。
| ------ DESCRIPTION: R包的描述文件。
| ------ NAMESPACE:用来指定R包输入和输出的包,还有一些特殊的名称。
此外,以后如果想要加载这个包的编辑环境,可以用Rstudio→File→Open Project加载myPackage.Rproj文件,很方便就可以加载环境。
图1:R包的创建和结果
其中,由两个部分不需要做任何人工改动:
1)NAMESPACE 命名空间文件,最好不要经过人工编辑,roxygen2包可以帮助你新建这个文件,建议你先把这个文件删掉,否则没办法新建。
2)man文件夹,存放.Rd文件,全部可以由roxygen2包自动生成,不经过人工编辑。
以下是需要人工进行修改的部分:
1.1 R文件夹
这个文件夹用来存放你的所有R函数。如果你的包使用了其他的语言,需要新建一个src文件夹来分开存放。详见参考。
1.2 DESCRIPTION文件
这个文件描述R包的重要信息,详细可以参照Hadley WickhamR包描述部分说明,里面包含内容用名称+冒号+空格为开头。我们就根据Rstudio生成的模板进行修改即可,里面的项目和内容,基本上都能见名知义。可能会影响后面检查包(check)出现warning或note的地方如下:
1) Depends
格式为R+空格+左括弧+大于号+等于号+空格+版本号
如果你想声明R (>= 3.4.3),需要写成R (>= 3.4.0), 否则会出现警告:Dependence on R version '3.4.3' not with patchlevel 0。
另外,depends可以写R版本,以及你需要声明特殊版本号的R包,如果不需要就仅写R版本即可。否则check时会出现提示。
import和suggest主要取决于这个包是否参与你包的主要功能,如果是一些辅助功能,有了这个包你的软件功能会更加强大,可以放在suggest里。
2)每个内容末尾句号的问题
除了Description末尾必须有实心句号外,其他所有,包括Title 末尾均不能有句号,否则就会出现警告。(这点网上也有人吐槽,这不合理,标题也应该是完整的句子。)
3)Encoding
该文件里必须声明 Encoding: UTF-8,没有这一行就会报错。
4)License
该文件里必须声明License,不知道选哪个可以参考R CRAN上关于License的说明。
还需要注意的是这个文件,包括后面的R脚本注释,最好不要超过每行80个字符,否则在自动生成注释文件和manual的时候会超出边框,check的时候也会警告。
2、准备R包需要用到的函数和测试数据,并写函数和测试数据的注释。
这一部分是R包的核心。关于R包函数怎么写笔者就不详述了,这里介绍一下如何写函数的注释。原本在写R包的时候,需要些一些后缀为rd的文件作为函数的说明文件,工作量很大,又需要学习latex,很不方便。自从有了roxygen2(roxygen现在已经不更新了,请安装roxygen2)这个R包,写rd文档就非常方便了。只需在R函数前面写一段话格式化的文字就可以。
建议使用Rstudio来写,因为它会把注释重要的地方标亮,还会提示错用的特殊符号。
2.1 编写注释
比如一个R函数和注释如下,虽然不是每一个注释都需要写成这样,但是这里为了讲解,写的具体一点:
#' @title Poisson vector.
#' @description Creating poisson vector.
#' @details Input an integer and return the log density of a poisson distribution with lambda equals the input integer.
#' @param x A non-negative integer. or vector.
#' @return A numeric vector of log density.
#' @export
#' @import stats
#' @importFrom stats dpois
#' @examples
#' poiVec(5)
#' poiVec(6)
poiVec <- function(x) {
dpois(x , x)
}
1)注释的每一行开头用#'或者##'开始。注释基本上都是完整的句子,请用实心句号结束。
2)注释的内容和格式,每一个项目和其内容之间用空格隔开:
@title 定义函数的标题,这个标题里的词可以搜索到
@description 是函数的描述,如果描述内容很长,则可以写在@details里。
@param 声明函数的参数,每个参数都需要声明,如果用一行同时声明2个输入类型一致的
参数,则用@param x,y 来表示,参数之间只用逗号隔开,不能有空格。
@return 非必须,声明返回的内容和类型,如果没有返回就不需要加这个项,否则会报错。
@export 非必须,如果需要输出该函数到NAMESPACE里,则必须加上这个项
@import 和@importFrom 虽然不是必须的,但是非常需要注意,如果你的函数里用到了其他包的函数,最好声明一下,当然基本的函数可以不用声明。这些内容会写到NAMESPACE文件里。如果不声明的话,在check包的时候,就会因为运行不了函数的例子而报错。
而且不建议在函数里加library()或者require()这种命令,也不建议在函数里使用包名::函数名这种命令,最好通通写在import和importFrom里,这样在生成rd文件的时候就被roxygen2自动输出到NAMESPACE文件中,方便维护和修改。
@examples 也不是必须的,但是一旦你写了,就要保证里面的脚本能够运行。所以一定要声明好import的包。还有如果example里用到了其他的包,需要写require(包名),否则check的时候会找不到这个包。
3)需要注意格式化,这里只简单地列举一些,详细见Hadley Wickham写的Format说明:
斜体 \emph{italics}
加粗: \strong{bold}
标注R函数和代码: \code{r_function_call(with = "arguments")}
标注NULL、TRUE或FALSE: \code{NULL}
标注R包 : \pkg{package_name}
引用本R包中的函数说明 : \code{\link{function}}
引用其他R包里的函数说明,如MASS包中的abbey: \code{\link[MASS]{abbey}}
插入链接: \url{http://rstudio.com}
给某个字段插入链接 : \href{http://rstudio.com}{Rstudio}
插入email,注意要写两个@: \email{*@@rstudio.com}
4)如果想要在一个注释里添加多个函数,可以用@Rdname, 而且一定要记得这个函数也要export,否则运行例子的时候如果写了这个函数,就会因为找不到函数而报错。
# ' @rdname 写了注释的那个函数的名称
# ' @export
5)注释的内容最好不要超过80个字符每行,否则在生成manuel.pdf文件的时候就会超出边界。Rstudio有一个工具可以帮助我们自动换行:
devtools::install_github("tjmahr/WrapRmd")
安装这个包以后,在Rstudio里面,选中注释然后Ctrl/Cmd + Shift + / 就可以自动换行。
6)注释因为是转成rd文件,有一些需要转义的符号,比如@号,\号,{}和[]都是latex里的重要符号,都需要转义。
2.2 生成rd文件
用roxygen2包的roxygenize函数即可生成rd文件。生成前最好先把之前Rstudio创建的NAMESPACE文件删掉。如果注释写错了就会报错,这一步需要反复修改。
roxygen2::roxygenize(package.dir = ".")
3、创建R包
3.1 check
在build之前,需要先check一下R包有没有问题,我们可以打开命令行工具,用以下命令来check。
R CMD check RPkgName
不过这里推荐使用devtools来进行R包的检查和创建:
devtools::check()
如果想检查manual,可以运行下面的命令,就会在temp文件夹里生成一个pdf文件:
devtools::check(manual = TRUE)
这一步可能会遇到很多的error和warning,这都是因为之前的文件没有写对导致的。
笔者遇到的报错或者warning有:
1) 警告:no visible binding for global variable.
这个warning最好还是解决一下,比如ggplot2就经常出这个warning。可以aes改成aes_string, 然后x和y就可以传入字符串了。还有比如你在load Rdata的时候没有给Rdata赋值,之后直接调用也会报这个警告。
2)警告:no visible global function definition for '函数名'
这可能是因为你调用了其他包的函数,但是没有在NAMESPACE里声明(也就是没有在rd注释的import里声明)。也可能是你写的函数没有export到NAMESPACE里。
3)报错:checking examples 时“could not find function ‘函数名’”
如果上述问题2)没有解决,那运行example的时候就会遇到这个报错。如果解决了,还遇到报错,那就可能是你的example里调用了其他包,你没有声明require(包)。
这步需要反复地修改和check,尽量修改到没有报错和警告为止。笔者改到出现如下结果的时候差点没有哭出来。(1 note是因为笔者的手册和测试数据太大了。)
0 errors √ | 0 warnings √ | 1 note x
3.2 Build
在确保R包没有错误之后,便可以创建了。你可以打开命令行工具,cd到R包文件夹所在的目录,然后build R包。它默认会创建vignettes和manual,然后打包成*.tar.gz。
R CMD build RPkgName
然后安装R包:
R CMD INSTALL RPkgName.tar.gz
不过这里推荐使用devtools来进行R包的build和install。
devtools::build()
会创建一个*.tar.gz, 然后
devtools::install()
则可以安装R包。
如果你的R包由vignettes,则为了能够使用browseVignettes()命令看到你的vignettes,需要build vignettes:
devtools::build_vignettes()
devtools::build_manual(pkg = ".", path = './doc')
devtools::build(vignettes = TRUE, manual = TRUE)
并且用以下命令安装
devtools::install(build_vignettes = TRUE)
这样就可以看到这个R包的vignette。
browseVignettes(RPkgName)
4、写说明书
这部分并不是R包的必要组成部分,但是有一个使用说明书能够让别人方便的使用,也方便自己回顾。很多人都把vignettes写的像文献一样详细和严谨。
在R包主目录里新建一个名为vignettes的文件夹(注意,不是vignette而是vignettes,否则会识别错误),说明书则存放在这个文件夹下。vignettes文件顾名思义就是手册,需要自己编写,当然可以越详细越好,可以是.Rd或者.Rmd。编写完后将.Rmd文件放到vignettes文件夹里。由于文件题头里声明了文件是pdf或者html,在打包的时候会自动生成。
推荐使用R markdown来编写,可以使用下述代码创建vignettes文件夹,它会在文件夹里创建vignettes模板,名字就是你传入的字符串,比如下述代码传入"my-vignette",模板名称就是"my-vignette.Rmd"。 模板里已经预先写好了vignettes需要声明的header等信息,自己再修改内容就可以了。怎么写就不详述了,具体可以看R markdown的使用手册。
usethis::use_vignette("my-vignette")
还有如果是想写bioconductor的包,他们的vignettes有一个推荐的模板, 具体可以看BiocStyle。
此外,*-manual.pdf文件里面是把上述所有rd文件整理在一个pdf文件里,可以在build包之后自动生成,不用自己写。笔者原来还以为得自己写呢。
devtools::build_manual(pkg = ".", path = './doc')
5、将R包上传到github
5.1 R包的上传
将R包发布到github可以方便版本管理和收集反馈。我们可以新建一个本地仓库,然后再把本地仓库远程到github仓库。可以按照以下步骤来执行,可见参考:
1) 申请一个github账号,并创建一个新的仓库repository,以R包命名.
2)cd到你创建R包的文件夹,然后执行以下命令git初始化你的R包文件夹为本地仓库。当然这需要你先安装git。
git init
然后执行以下命令把R包文件夹里的所有文件添加到仓库中, 并加上注释message。
git add .
git commit -m 'submit'
3)将本地仓库远程到github仓库
git remote add origin https://github.com/usrName/repositoryName
然后将文件传输到远程仓库,作为master:
git push -u origin master
以后每次更新,只需在本地仓库更新后再远程传输一次即可。
另外补充说明,从github上安装R包,也可以使用'devtools'包。
devtools::install_github("usrName/repositoryName")
如果你想查看手册需要build这个包,可以执行:
devtools::install_github('repositoryName/repositoryName',
build = TRUE, build_opts = c("--no-resave-data"), force = TRUE)
5.2 R包维护
通常,我们上传github之后,会写一个README.md,我们需要把它第一时间同步到我们的本地库里。
1)查看远程仓库:
git remote -v
2)把远程库更新到本地
git fetch origin master
3)比较远程更新和本地版本库的差异
git log master.. origin/master
4)合并远程库
git merge origin/master
然后我们一般会在本地仓库进行代码修改(如果是自己维护的话),修改完后将本地库更新
git add .
git commit -m "message"
最后将本地库更新推送到远程库:
git push origin master
查看本地库的状态:
git status