Shiny平台构建与R包开发(六)——R包开发与发布

本节将向读者展示如何将自己撰写的R函数集打包,并部署到CRAN或Github上。R包的开发和部署是让人心烦的事,因为您会遇到种种意想不到的问题,可能还会与CRAN官方来回周旋。本节不仅介绍R包开发和部署的流程,还向读者展示R包开发和部署的过程中尽量需要规避的一些坑,希望读者由此能加快R包开发和部署的效率。

主要流程

您已经撰写完了大量函数,现在您需要为它们编写帮助文档,并打成R包。详细教程事实上在R in Action(《R语言实战》)第2版第21章中已有说明。本教程以简化的方式讲述这些步骤:

  1. 利用下述代码下载并安装R in Action中推荐的R包模版npar包。随后找到npar包的本地文件。
# R in action -- 2nd Edition, P492
pkg <- "npar_1.0.tar.gz"
loc <- "http://www.statmethods.net/RiA"
url <- paste(loc, pkg, sep="/")
download.file(url, pkg)
install.packages(pkg, repos=NULL, type="source")
  1. 观察npar包文件结构可知,R包的结构为:一个名为npar的文件夹,下级目录包含一个名为R的文件夹、一个data文件夹、一个DESCRIPTION文件。因此您也模仿npar包的文件结构,为自己的包建立相应的文件目录。
  2. R文件夹中放置所有的R脚本文件,注意一个脚本文件只放一个R函数。打开npar包的R脚本文件,模仿该文件的撰写方式,保持脚本名与脚本所定义的函数名相同。开头注释块部分在后续在R包构建完成后将成为函数的帮助文档,注视块后为函数主体。注释块部分有三个标记极为重要:
  1. @importFrom 包名 函数名:由于npar包中没有引用其他包的函数,因此不需要@importFrom标签。但如果您的脚本中引用了其他包的函数,请务必在注释块中用该标签标注出您是从哪个包引入哪个函数。
  2. @export标签:如果在开头注释块中加上这一标签,则最终使用该R包的用户能够使用当前脚本所定义的函数,否则不能。时刻提醒自己,在函数主体部分,一旦引用其他包的函数,必须用包名::函数名(参数)的形式,不要直接用函数名(参数)的形式引用其他包的函数,否则后续打包时会出现NOTE警告。
    3.@example标签:用于为R包checker以及用户提供测试样例。进行R包检查的时候checker会自动运行该样例代码,如果样例运行不成功,就会报出ERROR。基于Shiny开发的R包会遇到这样的问题,即运行了ShinyAPP函数后,除非用户关掉GUI,函数运行才会停止。但R checker不会关掉GUI,因此checker会一直保持运行状态,造成问题。对此,CRAN官方的建议是"Functions which are supposed to only run interactively (e.g. shiny) should be wrapped in if(interactive())"。
  • 将测试数据的镜像文件放入data文件夹中。如果没有测试数据,请删除data文件夹,否则会引来后续的NOTE警告。
  • 撰写DESCRIPTION文件:同样模仿npar包的DESCRIPTION文件进行撰写。容易招引后续警告或错误的关键字段是DescriptionImports。由于npar包不用引入其他依赖包,因而没有Import字段。但如果您的包需要引入其他包时,请加上Imports: 包名1, 包名2, ..., 包名n。撰写Description的时候要格外小心,不要引入一些奇怪的字符,否则很容易导致包被CRAN拒掉。不要以"This package"字样作为Description字段的开头,否则依然会遭到CRAN的拒绝。
  • 构建包:将您的包文件拖到工作路径(一般会是"C:/Users/Lenovo/Documents",如果不是,请用getwd()函数查看)中,并运行以下代码。完成后将会在工作路径中产生名为 您的包名_版本号.tar.gz的压缩包。
if(!require(roxygen2)) install.packages("roxygen2")
roxygenize("您的包名") #运行完后会自动生成NAMESPACE文件与man文件夹
system("R CMD build 您的包名")
  • 检查包:运行以下代码。运行结束后,如果没有出现任何的Error、Warning或Note,您就可以将包投稿到CRAN或上传到Github上。当然,修复Error、Warning和Note的过程很让人心烦。您可以在出现几个Note的情况下依然将包部署到Github上,因为这并不一定会造成用户无法使用您开发的包。但这样的包几乎无法通过CRAN的自动审核。
system("R CMD check 您的包名_版本号.tar.gz")
  • 部署包:您可以选择将包部署到Github上,或者准备开始与CRAN的审稿人周旋。

Troubleshooting

如果是第一次发表R包,不断纠错是很头痛的事。这里列出一些常见错误与对应解决方法,希望能帮助大家少走弯路。之后我也会不断更新其他可能遇到的错误。

如果您准备将自己的包发布到CRAN上,请先仔细检查以下错误是否在您的包中存在,以尽可能节省与CRAN周旋的时间(往往自动审核约3小时,人工审核需要3–7天左右才有反馈)。

Check Package

  • checking package namespace information … ERROR
    Invalid NAMESPACE file, parsing gives:
    Error in asChar(ivars): empty name in directive ‘importFrom’ in ‘NAMESPACE’ file

    原因: 没有在R脚本的开头注释块中加入@ImportFrom 包名 函数名标签,却直接引用了其他包的函数。

  • checking dependencies in R code … NOTE
    Namespaces in Imports field not imported from:
    ‘包名1’ ‘包名2’ … ‘包名n’
    All declared Imports should be used.

    原因: DESCRIPTION文件的Imports字段中提到的部分包没有在R脚本的@ImportFrom 包名 函数名中被引用。

  • Checking whether package ‘xxx’ can be installed … ERROR
    object ‘function’ is not exported by ‘namespace:package’

    原因: 您所指定的function并不在package中,请检查function的名字是不是拼错了,或者package是不是拼错了。

  • Checking should be performed on sources prepared by ‘R CMD build’ NOTE
    原因: system("R CMD check 您的包名_版本号.tar.gz")没有写完整,直接用了system("R CMD check 您的包名)

CRAN自动审核

  • Possibly misspelled words in DESCRIPTION
    原因: DESCRIPTION文件中任何单词拼错都会导致自动审核报告NOTE。

  • The Title field should be in title case
    原因: DESCRIPTION文件title字段每个单词必须是大写字母开头(介词、冠词等不含实际意义的词除外)。

  • The Description field should not start with the package name, ‘This package’ or similar.
    原因: 在前面也有提到过,DESCRIPTION文件的Description字段不要以"This package"等开头。

  • Please enclose URLs in angle brackets (<…>).
    原因: DESCRIPTION文件中的网址必须用尖括号括起来。

  • ‘LazyData’ is specified without a ‘data’ directory
    原因: 开发的R包明明没有测试数据,但在DESCRIPTION文件中却还指定了LazyData字段(该字段用于确定测试数据是否直接被加载到环境中,还是需要用户运行data(测试数据集名)后才能加载到环境中),应该把该字段删去或增加测试数据集。

CRAN人工审核

  • Some code lines in examples are commented out. Please never do that. Ideally find toy examples that can be regularly executed and checked. Lengthy examples (> 5 sec), can be wrapped in \donttest{}. Functions which are supposed to only run interactively (e.g. shiny) should be wrapped in if(interactive()).
    原因: 前面提到,基于Shiny开发的包往往只要不关掉GUI,函数就会一直运行,因此最好加上if(interactive())

  • Please add \value to .Rd files regarding exported methods and explain
    the functions results in the documentation. Please write about the
    structure of the output (class) and also what the output means. (If a
    function does not return a value, please document that too, e.g.
    \value{No return value, called for side effects} or similar)

    原因: R脚本的注释块中缺少@output等标签。可能我们会认为shiny函数不会有什么返回值,或者说返回的就是个APP,所以没必要在帮助文档中对函数的output进行说明。但即使对于没有返回值的函数,也要加上@output等 标签说明它没有返回值,保证说明文档的结构是完整的。

  • If there are references describing the methods in your package, please
    add these in the description field of your DESCRIPTION file in the form
    authors (year)
    authors (year)
    authors (year, ISBN:…)
    or if those are not available:
    with no space after ‘doi:’, ‘arXiv:’, ‘https:’ and angle brackets for
    auto-linking.
    (If you want to add a title as well please put it in quotes: “Title”)

    原因: 代码中使用了别人的分析方法需要标注参考文献…

上一篇: Shiny平台构建与R包开发(五)——ui美化
下一篇: Shiny平台构建与R包开发(七)——Shiny APP部署



你可能感兴趣的:(r语言)