极简 R 包建立方法

作者

黄俊文

前言

最近想试一下捣腾一个 R 包出来,故参考了一些教程。现在看到的最好的就是谢益辉大大之前写过的开发R程序包之忍者篇,以及 Hadley 大神(ggplot2 devtools 等一系列包的作者)的 教程。但是前者有一些过时,后者是全英文的,所以我这里记录一下比较简单的过程,给读者们一个参考思路。如果你有一些 R 程序,想塞到去一个自创的 R 包中,那么这篇文章就可能是你想要的。为了方便说明,这里用我的包来进行示例。

准备工作

  1. 安装好 R。
  2. 可能需要 RStudio,没有的话也没有影响。
  3. 如果你是 Windows 下,请安装 rtools,去官网下载 exe 安装;如果你是 Linux 下,请安装对应的 R 开发包,Debian/Ubuntu 下就是运行命令 sudo apt-get install r-base-dev;如果你是 OS X 下,要装好 command-line-tools,如果你没有装过的话,Terminal 运行 git 或者 xcode-select 应该会弹出安装提示,按提示安装即可。
  4. 打开 R 环境,运行 install.packages('devtools',dependencies=T)
  5. 有一个编辑代码的程序,比如说 Sublime Text,notepad++,请不要用记事本编辑代码!另外要记得把文件保存成 UTF-8 (without BOM) 编码!
  6. 你要有一堆已经正确运行成功的 R function(s),你想把它们塞到你的 R 包中。

前面那些(除了第 6 点)是你想要写 R 程序包的先决条件(开发链),接下来就是开始写包的节奏了。注意这里不是所谓官方的写法,也不是最完美的写法,写出来也不能够保证能够放到 CRAN 上面啦。但是生成的东西应该是能够被别人安装并且运行的(要求真低 =_=///)。

编写

骨架

library('devtools') # 开发 R 包黑魔法工具
create('~/somebm') # 建立 R 包的目录, somebm 就是你想要的包的名称
setwd('~/somebm') # 把工作目录放到 R 包中的目录,开发 R 包过程中始终推荐这样做。
dir() # 列出当前工作目录的文件和文件夹

以上的过程,就是建立一个最基本的 R 包的目录骨架,并且把骨架文件夹作为当前工作空间。看一下生成的文件夹有什么东西:一个叫 R 的文件夹,一个叫 man 的空文件夹,一个叫 DESCRIPTION 的文件。

添加 DESCRIPTION

实际上,我们最简单(但能用)的 R 包,只需要操作 R 文件夹中的文件,和 DESCRIPTION 文件即可!

简单一点,先看看 DESCRIPTION 文件内容(用代码编辑器或者 file.edit('DESCRIPTION')

Package: somebm
Title: 
Description: 
Version: 0.1
[email protected]: # getOptions('devtools.desc.author')
Depends: R (>= 3.0.2)
License: # getOptions('devtools.desc.license')
LazyData: true

一一填写就可以。比如说我开发包,是关于布朗运动的,想要 MIT 协议发行我的代码,我就把这个文件的内容改成这样:

Package: somebm
Title: some Brownian motions simulation functions
Description: some Brownian motions simulation functions
Version: 0.1
Author: Laowu Wang <[email protected]>
Depends:
    R (>= 3.0.2)
License: MIT
LazyData: true

保存就可以了!如无意外,这个文件不需要再多的改动了!

添加 *.R 文件

接下来我们的关注点就是包文件夹中 R 文件夹中的文件了。

这个文件夹下,应该放着所有的自创的 R 代码。至于怎样放,放到哪个文件中,几乎无所谓,只要(你觉得)有美感,不凌乱,即可。

需要说明的是,在此目录下一个 somebm-package.r<packagename>-package.r)的文件已经被创建了,这个文件应该被保留下作为这个包的描述文件,最好不要放自创函数进去这里。

Talk is cheap。我这里给一个例子。

在此目录中,建立一个叫 bm.R 的文件。由于我这个包是用于模拟布朗运动的,这里把已经写好的模拟布朗运动的函数塞进去,在 bm.R 中写入:

fbm <- function(hurst=0.7, n=100){
  delta <- 1/n
  r <- numeric(n+1)
  r[1] <- 1
  for(k in 1:n)
    r[k+1] <- 0.5 * ((k+1)^(2*hurst) - 2*k^(2*hurst) + (k-1)^(2*hurst))
  r <- c(r, r[seq(length(r)-1, 2)])
  lambda <- Re((fft(r)) / (2*n))
  W <- fft(sqrt(lambda) * (rnorm(2*n) + rnorm(2*n)*1i))
  W <- n^(-hurst) * cumsum(Re(W[1:(n+1)]))
  X <- ts(W, start=0, deltat=delta)
  return(X)
}

保存。在 R 或 RStudio 中运行

#setwd('~/somebm') # 如果之前的 R 环境没有关闭的话,这一步是不需要的。
load_all() # 把包骨架文件夹中的 R 文件夹中的所有 .R 文件读进来
fbm() # 测试自己写的程序
fbm(hurst=0.2, n=1000) # 再测试自己写的程序

load_all() 函数很神奇地把包骨架文件夹中的 R 文件夹中的所有 .R 文件读进来了;每一次你改进你的 *.R 文件,只要运行一次 load_all() 就会把最新的自创函数们拉进来,在 R 环境中就可以测试最新的代码是否正常。

慢着…… 你可能忘记了一些东西……

文档和注释

代码不写注释是万恶之源
— 阿不思*邓布利多

别的可以省略,文档和注释是绝对不可以省略的。

实际上,R 包规定了每一个(对外)的函数和变量和数据结构,都要有对应的解释等;在 man 文件夹中会有对应的 *.Rd 文件,里面是由奇奇怪怪的东西(R+LaTeX)写成的。我们可以用比较简洁的方式来写函数注释,然后用一些方法来生成对应的 *.Rd 文件。

具体地说,先修改 bm.R 文件:

#' Generate a time series of fractional Brownian motion.
#'
#' This function generatea a time series of one dimension fractional Brownian motion.
#' adapted from http://www.mathworks.com.au/matlabcentral/fileexchange/38935-fractional-brownian-motion-generator .
#'
#' @param hurst the hurst index, with the default value 0.71
#' @param n the number of points between 0 and 1 that will be generated, with the default value 100
#' @export
#' @examples
#' fbm()
#' plot(fbm())
#' d <- fbm(hurst=0.2, n=1000)
#' plot(d)
fbm <- function(hurst=0.7, n=100){
  delta <- 1/n
  r <- numeric(n+1)
  r[1] <- 1
  for(k in 1:n)
    r[k+1] <- 0.5 * ((k+1)^(2*hurst) - 2*k^(2*hurst) + (k-1)^(2*hurst))
  r <- c(r, r[seq(length(r)-1, 2)])
  lambda <- Re((fft(r)) / (2*n))
  W <- fft(sqrt(lambda) * (rnorm(2*n) + rnorm(2*n)*1i))
  W <- n^(-hurst) * cumsum(Re(W[1:(n+1)]))
  X <- ts(W, start=0, deltat=delta)
  return(X)
}

函数头顶上的一连串注释就是了。注意这种注释是 #' 开头的,会由 devtools 里面的辅助函数来进行处理。先是函数简短说明,再是具体说明,然后是由 #' @param 开头的行就是对每个参数的说明。接下来,对用户使用的函数都要顶上一个 #' @export 行。最后,#' @examples 接下来的行就是示例用法啦。

只要运行

document()

就会生成对应的 *.Rd 文件在 man 文件夹中。

打包

一个命令

build()

就会在与包文件夹平行的文件夹中生成 somebm_0.1.tar.gz 类似的打包文件。可以在 R 环境中使用 install.packages('~/somebm_0.1.tar.gz', type='source') 来安装!

恭喜!你基本完成了一个包了!

提交到 CRAN!

什么!想提交到 CRAN 上面?

首先你要在包的工作空间里面运行

check()

尽量排除所有的 errors notes。

如果不放心的话,还可以在 terminal 环境,对前面 build() 生成的包用 R 自带的命令检查:

R CMD check --as-cran ~/somebm_0.1.tar.gz

尽量排除所有的 errors notes。

以上两个命令应该没啥大区别,当然检查多几次是好的。

接着阅读 CRAN Repository Policy,确保没有违反什么 policy 啦。

最后在 http://cran.r-project.org/submit.html 提交即可。按照提示上传 tar 包,填写资料等。有问题的话过不久管理员会发信息到电子邮件,按照电子邮件修改之后再上传。

忽然间就完结了…… 吗?

经过刚才的步骤,可以说已经建造好一个包了。成就感满满的!

当然,官方文档那些详尽(又臭又长)的说明文是有着更加细致和更加多功能的介绍的。比如说 demo 啊,dataset 啊,test 啊,还有关于 S3 S4 的函数啊什么的。各位有兴趣的还可以继续深入考察!

最后的最后,本文示例的所有代码在这里, man 文件夹和 NAMESPACE 文件都是自动生成的。

关于COS编辑部

本账户为COS编辑部公共账户,目前由朱雪宁任主编,由王小宁和张心雨担任副总编,编辑有:蔡占锐,常象宇,邓金涛,邓一硕,丁维悦,范超,冯璟烁,冯凌秉,高涛,郎大为,吕翔,彭晨昱,邱怡轩,施涛,覃文锋,王健桥,魏太云,吴佳萍,谢益辉,熊熹,杨舒仪,于嘉傲,主要负责主站文章的规范化编辑以及相关论文、书籍、手册的整理、编纂、出版等工作。

极简 R 包建立方法》有34个想法

  1. 运行到中间出现问题:
    错误于parse(text = lines, n = -1, srcfile = srcfile, keep.source = TRUE) :
    参数((keep.source = TRUE)) 没有用。这怎么解决的?

    1. 这是什么东西?运行到什么到中间?提问应该提供尽量详细的信息。

      此外,强烈强烈建议去论坛 http://cos.name/cn/ 相关的板块进行提问,这样能够帮助更加多人。。

    1. 之前就是苦于不知道怎样建立 r 包,各种概念不清晰。如果要开发的 R 包中只有 R 函数的话,按照这篇文章走再加上思考能力一定能够搞定。

  2. 弱弱问下,准备工作中说:Windows 下,请安装 rtools。但后面的步骤中似乎没有直接调用过rtools啊。请问rtools在此过程中的作用是什么?

    1. 嗯,怎么说呢。build() 和 check() 函数里面其实有很多动作的。rtools 是在 windows 下安装了一些 linux 下的命令行工具来支持那些操作。

      比如说,build() 会打包出一个 *.tar.gz ,这个过程是调用了 rtools 里面 tar.exe 和 gzip.exe(山寨版 linux 的 tar gzip 命令程序)来运行的。

  3. 按照这个方法终于做出来了!谢谢大神了!
    需要注意的地方:
    (1)Author: Laowu Wang <[email protected]>中Author需要用单数。
    (2)下面这一部分每行都要用#‘,否则后面会报错。
    #’ Generate a time series of fractional Brownian motion.
    #’
    #’ This function generatea a time series of one dimension fractional Brownian
    motion.(这样就不可以哦!)
    (3)还有一个最关键的:建立的工作目录要用英语,不要夹杂汉语!否则check通不过。

  4. 我了个去,刚准备来cos搜点打包的论坛文章,居然首页就是这篇,太有缘分了,握爪

  5. checking Rd line widths … NOTE
    ……
    These lines will be truncated in the PDF manual.

    对于Rd文件中某一个usage的表达式太长该怎么办?我试过回车分割,没有用。

  6. 这个包建立以后,下次用的时候怎么加载呀?如何利用其中的函数,以及查看帮助呀?

    1. 当成普通包那样。建立包,install.packages(‘s-0.1.tar.gz’, type=’source’) 安装到本地不就行了么。

      1. 首先,谢谢你能回答!linstall.packages(‘s-0.1.tar.gz’, type=’source’) 这个之后,要用library()函数吗?要是用library()函数,函数中的参数应该改写什么呀?就是这个包的名字叫什么啊?这个包中我们写的文档和注释,我们怎么才能看到啊?也用?之类的吗?

      2. 很抱歉,看完你的文章第一次在电脑上怎么运行都不能成功。又尝试了一下,终于成功了 。具体的步骤如下:
        (1)install.packages(‘~/somebm_0.1.tar.gz’, type=’source’) #发现第一次需要,第二次可以跳过这一步。
        (2)library(somebm)
        (3)fbm(hurst=0.3, n=1000)
        新的问题就是,当不知到函数名时,如何查找加载的“somebm”包中的函数进行步骤(3)。就是当运行步骤(3)时,如何查看关于函数fbm的帮助,以便以后运行。试了一下,函数?fbm不能奏效。

      3. 安装完之后就是普通包那样用啊了。。。当然要 library() 来引入当前的环境之中。

        查找包中所有函数帮助
        help(package=’somebm’)

        查找已知函数名称的帮助
        library(‘somebm’) #注意要先运行这句
        ?fbm #我这里运行成功,注意英文问号
        help(‘fbm’, package=’somebm’) #如果上面的不行试试这个

      4. 看了你的帮助,怀着无必兴奋的心情,有尝试了一下,还是不行,R里面出现这样的提示:
        > library(‘somebm’) #注意要先运行这句 #没问题
        > ?fbm #我这里运行成功,注意英文问号 # 出现问题

        No documentation for ‘fbm’ in specified packages and libraries:
        you could try ‘??fbm’

        > help(‘fbm’, package=’somebm’) #如果上面的不行试试这个#问题

        No documentation for ‘fbm’ in specified packages and libraries:
        you could try ‘??fbm’

        希望不吝赐教!!!

      5. 我也是初学者,大家共同探讨。。

        我这里重新试过了,也出现了你说的离奇现象 = =///

        你有没有设置过 R_LIBS_USER 之类的?目测问题出于这里。你有设置的话删除这个试一下。

        然后不使用 rstudio。用最原始的 R.exe:

        你安装 R 的时候,应该已经把 R.exe 放到 path 中了(自己搜索方法)。那么,打开命令提示符,输入 R 再输入 install.packages(‘~/somebm_0.1.tar.gz’, type=’source’) (路径要正确)再输入 help(‘fbm’, package=’somebm’) 应该是没有问题的。。。吧 。。。。

        建议去论坛问吧!这个应该涉及到 windows 的某些设置问题。

      6. 哦,十分谢谢! 说实话,这次的回答感觉已经超出了我的理解,不过,还是充满了好奇。希望自己能怀着好奇心能学得更多,再次谢谢。

  7. 請問這樣的流程生成R包,裡面會有包含DOC的資料夾嗎?就是這個DOC的資料夾會有R document.

  8. 按照上面步骤建立后,library,正常。可是,关闭R,library,没有提示错误,但是函数不能用。每次都要从library(‘devtools’)再来一次,请问这是怎么回事?

  9. #照着楼主的方法做了一遍,结果check的时候出错,应该是LaTex的问题,LaTex小白一个,这个问题怎么解决呢。另外,为什么加载后不能看到包里有多少个函数呢,是因为最后的描述性文档没生成,所以没document和help pages吗?谢了
    check()
    Updating somebm documentation
    Loading somebm
    "C:/PROGRA~1/R/R-215~1.3/bin/x64/R" –vanilla CMD build "F:Rworkplacesomebm" –no-manual
    –no-resave-data

    * checking for file ‘F:Rworkplacesomebm/DESCRIPTION’ … OK
    * preparing ‘somebm’:
    * checking DESCRIPTION meta-information … OK
    * checking for LF line-endings in source and make files
    * checking for empty or unneeded directories
    * building ‘somebm_0.1.tar.gz’

    "C:/PROGRA~1/R/R-215~1.3/bin/x64/R" –vanilla CMD check
    "C:UsersADMINI~1AppDataLocalTempRtmp61Mca7/somebm_0.1.tar.gz" –timings

    * using log directory ‘C:/Users/ADMINI~1/AppData/Local/Temp/Rtmp61Mca7/somebm.Rcheck’
    * using R version 2.15.3 (2013-03-01)
    * using platform: x86_64-w64-mingw32 (64-bit)
    * using session charset: ASCII
    * checking for file ‘somebm/DESCRIPTION’ … OK
    * this is package ‘somebm’ version ‘0.1’
    * checking package namespace information … OK
    * checking package dependencies … OK
    * checking if this is a source package … OK
    * checking if there is a namespace … OK
    * checking for executable files … OK
    * checking whether package ‘somebm’ can be installed … OK
    * checking installed package size … OK
    * checking package directory … OK
    * checking for portable file names … OK
    * checking DESCRIPTION meta-information … WARNING
    Invalid license file pointers: LICENSE
    * checking top-level files … OK
    * checking for left-over files … OK
    * checking index information … OK
    * checking package subdirectories … OK
    * checking R files for non-ASCII characters … OK
    * checking R files for syntax errors … OK
    * checking whether the package can be loaded … OK
    * checking whether the package can be loaded with stated dependencies … OK
    * checking whether the package can be unloaded cleanly … OK
    * checking whether the namespace can be loaded with stated dependencies … OK
    * checking whether the namespace can be unloaded cleanly … OK
    * checking for unstated dependencies in R code … OK
    * checking S3 generic/method consistency … OK
    * checking replacement functions … OK
    * checking foreign function calls … OK
    * checking R code for possible problems … OK
    * checking Rd files … OK
    * checking Rd metadata … OK
    * checking Rd cross-references … OK
    * checking for missing documentation entries … OK
    * checking for code/documentation mismatches … OK
    * checking Rd usage sections … OK
    * checking Rd contents … OK
    * checking for unstated dependencies in examples … OK
    * checking examples … OK
    * checking PDF version of manual … WARNING
    LaTeX errors when creating PDF version.
    This typically indicates Rd problems.
    LaTeX errors found:
    ! LaTeX Error: File `inconsolata.sty’ not found.

    Type X to quit or <RETURN> to proceed,
    or enter new name. (Default extension: sty)

    ! Emergency stop.
    <read *>

    l.281

    ! ==> Fatal error occurred, no output PDF file produced!
    * checking PDF version of manual without hyperrefs or index … ERROR
    #最后一步出错了,这个

    1. 楼主这个用到latex了吗? 是我没看见吗? 我也是照着做的,没有用到什么latex啊

  10. 感谢!安装inconsolata这个宏包后,有出现这个问题,google了一下,是说没有zi4这个红包,结果再miktex package manager里没有找到这个宏包啊。按照http://qandasys.info/inconsolatazi4-font-issue-miktex/和https://groups.google.com/forum/#!topic/r-help-archive/EPU4_ZlpDzE给的说法,前者按照zi4宏包,后者是说重新安装CTEX等,轻易不敢重新安装呢。不知您有什么好的建议呢,谢谢!
    * checking PDF version of manual … WARNING
    LaTeX errors when creating PDF version.
    This typically indicates Rd problems.
    LaTeX errors found:
    !pdfTeX error: pdflatex.EXE (file t1-zi4r-0): Font t1-zi4r-0 at 600 not found
    ==> Fatal error occurred, no output PDF file produced!
    * checking PDF version of manual without hyperrefs or index … ERROR

  11. 写得很棒,照着做了一遍,除了开始出了点问题之外就很顺利的成功了! 就是不明白rtools到底用到了吗? 还有DESCRIPTION里面的AUTHOR信息得按照里面的格式写才行,要不最后build的时候通不过。总之,很感谢

    1. Error:
      LaTeX errors when creating PDF version.
      This typically indicates Rd problems.
      LaTeX errors found:
      !pdfTeX error: pdflatex.EXE (file t1-zi4r-0): Font t1-zi4r-0 at 540 not found
      ==> Fatal error occurred, no output PDF file produced!

      Solution!
      initexmf –update-fndb
      initexmf –edit-config-file updmap
      The latter command should open updmap.cfg in your default editor, commonly Notepad.
      Add the line

      Map zi4.map

      to updmap.cfg, save and close. Then, in the command window, type

      initexmf –mkmaps

  12. 谢谢楼主分享,[email protected],后来一直出错,export只是让读者可见,这个为什么不可以省略?麻烦楼主解答下,非常感谢。

  13. 请问在创建R package时如何,如何实现自动安装依赖的包。试了Depends和Imports,安装了自建的包后,library(),可以将依赖的包加载进来。但是别人没按依赖的包时,安装我创建的包,怎样实现自动安装依赖的包?

发表评论

电子邮件地址不会被公开。 必填项已用*标注