Closed ixxmu closed 1 year ago
最小必须:
包的名字(包的名称只能包含字母、数字和点号。建议不要用点号,因为点号在函数名称有特殊含义)
|
|--DESCRIPTION(项目描述文件,包括包名、版本号、标题、描述、依赖关系等,用于设置项目的全局的配置)
|--NAMESPACE(包的命名空间文件,用于设置项目的全局的配置)
|--R(函数源码)
|--hello.R
|--……
|--man(帮助文档,存放函数说明文件的目录,语法大致基于 LaTeX。这些文件会被编译为 HTML、纯文本和 PDF 以供查看。)
|--hello.Rd
|--……
|--……
你可以使用系统的原生的package.skeleton()
函数,生成项目的骨架,也可以自己手动创建,或者使用Rstudio的("文件(File)" → " 新项目(New Project)" → " 新目录(New DIrectory)" → "R 包(R Package)" ,输入R包名字,点击创建项目(Create Project)创建一个新的 R 包)。但是都比较麻烦,因为创建之后,都是需要手动写相关的帮助文档(latex)、NAMESPACE等文件,并且测试起来也不是太方便。
这里给大家推荐由Hadley Wickham重新定义的开发流程。有3个武器,即devtools、roxygen2和testthat。
首先安装如下几个R包:
if(!require("devtools"))
install.packages("devtools")
if(!require("roxygen2"))
install.packages("roxygen2")
if(!require("testthat"))
install.packages("testthat")
if(!require("testthat"))
install.packages("testthat")
dest_path="/Users/xxx/Desktop/20200911-How_to_build_Rpkg"
pkg_path <- paste(dest_path, "demo", sep=.Platform$file.sep)
devtools::create(pkg_path)
✓ Creating '/Users/xxx/Desktop/20200911-How_to_build_Rpkg/demo/'
✓ Setting active project to '/Users/xxx/Desktop/20200911-How_to_build_Rpkg/demo'
✓ Creating 'R/'
✓ Writing 'DESCRIPTION'
Package: demo
Title: What the Package Does (One Line, Title Case)
Version: 0.0.0.9000
Authors@R (parsed):
* First Last <first.last@example.com> [aut, cre] (<https://orcid.org/YOUR-ORCID-ID>)
Description: What the package does (one paragraph).
License: `use_mit_license()`, `use_gpl3_license()` or friends to
pick a license
Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.1
✓ Writing 'NAMESPACE'
✓ Writing 'demo.Rproj'
✓ Adding '.Rproj.user' to '.gitignore'
✓ Adding '^demo\\.Rproj$', '^\\.Rproj\\.user$' to '.Rbuildignore'
✓ Opening '/Users/wangqingzhong/Desktop/20200911-How_to_build_Rpkg/demo/' in new RStudio session
✓ Setting active project to '<no active project>'
可以直接编辑该文件,或者在R里用use_description
函数进行修改。
setwd(pkg_path)
author.description = list(
`Authors@R` = 'person("Ziru", "Chen", email = "xxx@xxx.cn", role = c("aut", "cre"),
comment = c(ORCID = "YOUR-ORCID-ID"))',
License = "GPL-3",
Language = "es",
Title = "How to build an R package",
Description = "How to build an R package-20200911")
use_description(fields = author.description, check_name = TRUE, roxygen = TRUE)
Overwrite pre-existing file 'DESCRIPTION'?
1: Nope
2: Negative
3: I agree
Selection: 3
✓ Writing 'DESCRIPTION'
Package: demo
Title: How to build an R package
Version: 0.0.0.9000
Authors@R (parsed):
* Ziru Chen <chenziru@picb.ac.cn> [aut, cre] (<https://orcid.org/YOUR-ORCID-ID>)
Description: How to build an R package-20200911
License: GPL-3
Encoding: UTF-8
Language: es
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.1
如果你的代码用到了别的包的函数,那么需要将这个依赖关系导入到DESCRIPTION文件。
use_package("magrittr")
✓ Adding 'magrittr' to Imports field in DESCRIPTION
● Refer to functions with `magrittr::fun()`
依赖关系可以用type来指定,如果需要增加版本,可以设定min_version = T
> use_package(package = "dplyr", type = "Depends", min_version = TRUE)
✓ Adding 'dplyr' to Depends field in DESCRIPTION
● Are you sure you want Depends? Imports is almost always the better choice.
type:
Depends
以上边例子为例,当使用demo包的时候,需要加载dplyr包到主搜索路径(即search()返回的环境列表)中,demo包才可以使用(即其使用了library()来加载包)。如果希望用户每次加载你的软件包时都加载其他软件包,就使用这种方式。
存在的问题:如果加载的两个包有相同名称的函数,则后加载的包的函数会覆盖前面加载的。
除非一个包是被设计为与另一个包一起使用,没有前者,后者也没办法使用,可以用这种方式。否则一般不建议使用,更好的方式是, 将这些包使用@import导入命名空间(NAMESPACE).
Imports
在写代码的时候,使用Roxygen2注释的@import或@importFrom语句引用的包,或通过::运算符使用函数的包,都会显示在Imports下边。
Imports在安装包的时候会被一同安装,但不会被加载到环境。
Suggests
适用于并非确实必要,但在示例、编译使用指南或测试中用到的软件包。这里列出的R包不会与软件包一起被安装。
Enhance
不是很常见。
NAMESPACE文件用于函数的访问控制。这个文件无需手动编辑,使用roxygenize
或document
函数的时候,会将相关函数导出到这个文件。
在使用roxygen2包来写R包的好处是:
来看一个例子:
我们先在R下边编辑一个名为"bmi.R"的文件。并输入如下代码:
#' Add together two numbers.
#' @author Ziru Chen
#' \email{xxx@@xxx.cn}
#' @param weight weight.
#' @param height height.
#' @return The sum of \code{x} and \code{y}.
#' @importFrom magrittr %>%
#' @export
#' @examples
#' bmi(50, 1.65)
bmi <- function(weight, height) {
(weight/(height^2)) %>% ceiling
}
#'
开始,用以区别其他的注释。@
在 roxygen 中有特殊的意义,如果想在文档中添加 一个 @
符号,需要多加一个@
,即使用@@
,以下情况会出现@符号:@importFrom magrittr %>%
,你也可以只写@import magrittr
,但是前者会使得代码更加清晰,有助于自己和别人查看代码。> roxygenise(pkg_path)
Loading demo
Loading required package: dplyr
Attaching package: ‘dplyr’
The following object is masked from ‘package:testthat’:
matches
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
Writing NAMESPACE
Writing NAMESPACE
Writing bmi.Rd
或者使用document(pkg_path)
,This function is a wrapper for the roxygen2::roxygenize()
生成了帮助文档:bmi.Rd
将依赖的函数添加到了命名空间(NAMESPACE)文件,以及导出刚刚写的函数:
加载R包里所有的函数:load_all()可以让你跳过安装而直接把源码包装到内存中。
load_all()
> weight <- c(50,60)
> height <- c(1.65,1.60)
> bmi(weight, height)
[1] 19 24
更可重复的测试方法:testthat
# 开始testthat
usethis::use_testthat()
tests/testthat
目录tests/testthat.R
,当运行 R CMD check
时,会自动运行所有代码测试。DESCRIPTION
的 Suggests
部分。在demo项目下刚才新建tests/testthat目录下新建“test.源文件名.R”,用来写测试代码。
比如上边的文件名为bmi.R,其对应的测试脚本则命名为:test.bmi.R。
test_that("str_length is number of characters", {
expect_equal(bmi(50, 1.65), 19)
expect_equal(bmi(c(50,60),c(1.65,1.60)), c(19,24))
})
然后运行测试:
devtools::test()
其他expect_
请查看《R Packages·Chapter 11 Testing》:https://r-pkgs.org/tests.html
check(pkg_path)
因为我刚才为了讲解方便,刚才导入了'dplyr',但其实并没有用上。
我们手动删除,再来检查一次。
check(pkg_path)
> devtools::build()
✓ checking for file ‘/Users/wangqingzhong/Desktop/20200911-How_to_build_Rpkg/demo/DESCRIPTION’ ...
─ preparing ‘demo’:
✓ checking DESCRIPTION meta-information ...
─ checking for LF line-endings in source and make files and shell scripts
─ checking for empty or unneeded directories
─ building ‘demo_0.0.0.9000.tar.gz’
[1] "/Users/wangqingzhong/Desktop/20200911-How_to_build_Rpkg/demo_0.0.0.9000.tar.gz"
> install.packages(paste(dest_path, "demo_0.0.0.9000.tar.gz", sep = .Platform$file.sep),repos=NULL, type='source')
> library(demo)
一定要把设置repos=NULL
,否则会到CRAN去寻找包(当然,找不到),于是会报错。
remove.packages()
函数本文写于2020.09.10(当时为了给学生讲解,犹疑再三还是发出来了),重度参考了以下资料:
https://mp.weixin.qq.com/s/7847toikls80qstZbqBP9Q