欢迎大家学习一门新的课程,在这一门课程中,我们会学习到如何开发一个R语言包,学习完这门课程之后,同学们可以自己属于自己的R包,从一个R用户开始转变成为一个R语言开发者。

1. 第一节


1.1 简介


在R中,可以分享代码的基本单位是包。包把代码,数据,文档和测试整合在一起,这样可以很容易的和别人分享。截止到目前,R包的公共发布网站CRAN,已经有了6000多个包可以使用。正是这么多的资源,使得R语言如此的成功,每当我们遇到一个问题,往往已经有人解决了你的问题,你则可以通过下载他们的包从他们的工作中受益。


本课程的目的是教会同学们如何开发包,让同学们写出自己的包,而不只是使用别人的包。我们为什么要开发包:首先是与他们分享,做成包他人也可以很方便的使用。其次,做成包可以很方便自己的使用,节省自己的时间。


1.2 准备工作


首先,确保安装了了最新版的R(至少是3.1.2以上),然后需要安装以下的包:

install.packages('c("devtools","roxygen2","testthat","knitr")')

其次,确保Rstudio 是最新版的

2. 开始


2.1 包的命名


命名有三个正式的要求:


  1. 名称只能包含字母,数字,和点号(.)

  2. 必须以一个字母开始

  3. 不能以点号结束

这意味着名字不能使用下划线或者横杠,并且建议不要使用点号,因为其有令人混淆的含义,比如文件扩展名字或者S3方法


对于起名字还是有一些好的建议的:


  1. 选择一个比较容易被搜索到的名字,这让潜在的用户能够找到你的包

  2. 避免同时使用大写和小写,这样会难以输入并且难以记住

  3. 找到一个与这个问题相关的词,修改他比如:knitr [knitr + r]

  4. 使用缩略词 :Rcpp = R+c++(plus plus)

  5. 在单词后面加上一个r:stringr


2.3 创建一个包


一旦我们确定了包的结构,就可以创建包,使用Rstudio:


  1. File->New Project

  2. 选择 New Directory

  1. 选择 R Package

  2. 最后,对包进行命名,然后点击Create Project 进行创建


3. 包的组成部分


3.1 代码


使用包的第一个原则,就是包中的所有的R语言代码都要放到R/目录当中,本节,我们会学习到一些关于R/目录的知识,有关在文件中组织函数的建议,以及形成良好的代码风格的提示


3.1.1 组织函数 与命名


在文件中编写函数的时候,有两种极端的做法不是很好:把所有的函数都放在一个文件中,以及把每个函数都发在不同的文件,一般而言,文件名要包含函数的功能,并且文件名字要有意义并且以.R结尾


一个好的文件名: fit_model.R


这样我们就能知道这个文件是做什么事情的


对象的名称


变量名称和函数名称应该小写,使用下划线进行分割,一般情况下,变量名应该是名词,函数名应该是动词


好的名字:day_one


3.1.2 顶层代码


到目前为止,大家可能已经在编写R脚本,通过source()来加载文件运行R代码。脚本中的代码和R包中的代码有两个主要的区别:


  1. 脚本中,代码在加载的时候运行,代码在编译的时候运行(下载好这个号的时候),这就意味着包的代码应该只包含对象,对象的绝大部分能容应该是函数

  2. 包的函数会被使用到任何环境,因此需要小心处理包与调者的关系


当使用source 加载脚本的时候,每一行代码都会被执行,执行的结果可以立即使用。对于包而言,有一点不一样,加载过程分为两步,包编译的时候,目录下的所有代码都会被执行,结果会保存下来。使用library 的时候,这些保存的结果就可以用了。


举个例子:x = Sys.time() 如果放在一个脚本里面,x显示的是脚本什么时候被source运行的。如果放在包中,则会告诉你什么包什么时候被编译的。


这就是说不应该在包里面直接运行代码:包只能创建对象,主要是函数。举一个例子:


我们有一个包叫做test,包含的代码如下:


library(ggplot2)
show_mtcars = function(){
qplot(mpg,wt,data=mtcars)
}


然后我们加载test包,运行函数:


library(foo)
show_mtcars()


因为,ggplot2的qplot函数将不可用:library(test)的时候不会重新执行library(ggplot2),因此需要改成这个样子:


show_mtcars = function(){
library(ggplot2)
qplot(mpg,wt,data = mtcars)
}


3.1.3 运行环境


脚本和包的一个巨大的区别是:别人会使用你的包,并在一个你从未想到的环境中使用,这意味着你需要注意R的运行环境。如果使用library()加载的一个包,或者使用options()修改了一个全局变量,那么你就已经修改了R的运行环境,这样会造成R的代码非常难以理解,因此最好不要做以下事情:


  • 不要使用library()或者require()
  • 不要使用source()从文件中加载代码
  • 修改了全部变量options()或者图形的par(),先把旧的设置保存下来,然后恢复到原来的值
3.1.4 总结
  1. 所有的R代码写到R/目录下
  2. 包主要创建对象和函数
  3. 不要修改环境

4.包的元数据


DESCRIPTION 的作用是储存包中重要的元数据,每一个包都必须有一个DESCRIPTION,他看起来是这个样子的:


Package: test
Type: Package
Title: What the Package Does (Title Case)
Version: 0.1.0
Author: Who wrote it
Maintainer: The package maintainer <yourself@somewhere.net>
Description: More about what it does (maybe more than one line)
    Use four spaces when indenting paragraphs within the Description.
License: What license is it under?
Encoding: UTF-8
LazyData: true


4.1 依赖:包需要什么


描述文件需要列出这个包所有的依赖包,以下几行代码表示需要依赖dplyr和ggvis:


Imports:  
    dplyr,
    ggvis


下面几行说明,可以使用ggvis和dplyr,但是并不是必须的:


Suggests:
    dplyr,
    ggvis
    


在DESCRIPTION中添加Imports和Suggests 很简单的方法是使用


devtools::user_package()


这样会自动的把需要得到写入DESCROPTION文件中正确的位置,例如:


devtools::use_package("dplyr")


4.2 标题和描述:包是用来做什么


  • TItle 是包的一行描述,经常显示在包列表中,他应该是纯文本
  • Description 比标题更加详细的描述


ggplot2 的Title和Description如下:

Title: Create Elegant Data Visualisations Using the Grammar of Graphics
Description: A system for 'declaratively' creating graphics,
    based on "The Grammar of Graphics". You provide the data, tell 'ggplot2'
    how to map variables to aesthetics, what graphical primitives to use,
    and it takes care of the details.


4.3 作者


我们还需要指定作者是谁,因为这样使用者知道如果包有什么问题可以联系谁。下面是添加作者的一个例子:

Authors@R:person('Hadley','Wickham',email = '...',role = c('aut','cre))


这一条命令再说,包的作者(aut)和维护者(cre)都是Hadley Wickham,他的邮件是什么。 person有四个主要的参数:


  • 名字(名和姓)
  • email
  • 角色,包括维护者(cre),作者(aut),贡献者(ctb),版权所有人(cph)


4.4 许可证


如果计划发布包,许可证是非常重要的,如果不打算发布,可以忽略这个部分。 对于R包,需要考虑三种许可证:


  • MIT 其允许人们使用和发布包的代码,唯一的限制是许可证必须和代码一起发布
  • GPL-2 意味着任何包含你的代码的包都必须使用GPL兼容的许可证来发布,此外,任何人发布代码的修改版本都必须公布源代码
  • CC0 这个许可证放弃了你对代码和数据的所有权,任何人都可以自由的把它用于任何地方


4.5 设置版本


发布的版本包括三个数字: <主版本号>.<此版本号>.<补丁版本>


4.6 总结


  1. 添加依赖
  2. 添加标题和描述
  3. 添加作者
  4. 添加许可证
  5. 添加版本


5.文档


文档是包最重要的一部分。通过文档,用户才知道如何使用这个包,如何使用 这个包的某一个函数。通过?来访问某一函数的文档举例子:


?mean


5.1 文档的工作流程


制作文档一般有基本的四个步骤


  1. 添加roxygen注释到.R文件
  2. 运行devtools::document() 将roxygen注释转为.Rd文件
  3. 利用?预览文档
  4. 修改注释,重复上面的步骤


我们会一步一步介绍,首先是在源文件添加注释:roxygen注释从#开始,区别于一般的注释.下面是一个简单的函数文档,这个文件名为add.R:


#'Add together two number
#'
#' @param x A number
#' @param y A number
#' @return  The sum of \code{x} and 、code{y}
#' @example
#' add(1,1)
#' add(10,1)

add <- function(x,y){
  x+y
}


然后运行:


devtools::document()


在man的目录下会出现一个文件add.Rd,看起来如下:


% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/add.R
\name{add}
\alias{add}
\title{Add together two number}
\usage{
add(x, y)
}
\arguments{
\item{x}{A number}

\item{y}{A number}
}
\value{
The sum of \code{x} and code{y}
}
\description{
Add together two number
}


此时可以访问这个函数的帮助文档:?add


5.2 roxygen 详解


roxygen 的注释以#’开始,第一句是文档的标题,第二段是描述,写在文档的最前面,简要说明函数的功能,第三段以及以后的段落是细节描述


@seealso 用于添加其他指向其他有用资源
@family 用于添加一族的相关函数
@keywords 用于添加关键词
@param 用于添加函数参数
@examples 用于添加例子
@return 用途田间返回值描述


包文档,是一个页面对于这个包的描述,通过packages?包名来访问它。因为没有一个包的对象,所以需要为NULL提供文档.这里提供一个包文档的例子:


#‘ test :A package for test
#'
#' The test packages provides some functions :
#'
#' @section  test Functions:
#' The foo function ...
#'
#' @docType  package
#' @name  test

NULL



其他的文档还包括类,范型和方法的文档,这里就不详细介绍


6.发布到github


为什么要使用Github


其让我们可以非常容易的分享包,任何用户只需要使用两行代码就可以安装包:

install.packages('devtools')
devtools::install_github('username/packagename')


6.1 初始设置


  1. 安装Git



  1. 设置Git,姓名和邮箱


  • git config –global user.name “YOUR FULL NAME”
  • git config –global user.email “YOUR EMAIL ADDRESS”


  1. 创建Github账户


  1. 生成SSH秘钥


在Rstudio中选择Git/SVN,点击Create RSA Key


5.把你的SSH共钥上传给GitHub:https://github.com.settings/ssh。找到秘钥最简单的方法是在Rstudio中Git/SVN偏好中点击“View public key”(也可以不操作这一步)


6.2 创建本地Git仓库


  1. 在Rstudio中选择project option ,再选择Git/SVN面板,把Version control system 从“None”改成“Git”
  1. 然后在shell中运行


git init


重启Rstudio,再次打开Rstudio,就会出现Git一栏:


6.3 同步到github


  1. 首先,在Github创建新的仓库,它的名称和包的名称一样,然后点击提交

  2. 打开shell,输入提交操作:


git init 
git add .
git commit -m 'first commit'
git remote add origin https://github.com/liamamilin/Fibonacci.git
 git push -u origin master


意思是告诉Git你的本地仓库有一个GitHub的远程版本,第二行是将目前的工作推送到那个远程仓库


这样,其他人可以通过:


install.packages('devtools')
devtools::install_github('username/packagename')


来安装你写的包了


7. 案例


写一个函数计算斐波那契数列,并封装成为一个包,上传到Github.


斐波那契数列的代码如下:

Fibonacci = function(n){
  if (x ==1 || x == 2){
      return(1)
    }else{
      return(Fibonacci(x-1) + Fibonacci(x-2))
   }
}

8.更多内容


本课程还有很多内容没有提到,如需要参考更多的内容,可以查看:


  1. Writing R Extensions :https://cran.r-project.org/doc/manuals/r-release/R-exts.html
  2. R packages:http://r-pkgs.had.co.nz/