创建R包学习教程

Github地址:https://github.com/ShixiangWang/learn-devel-Rpkg

内容源自《R实战》第二版,酌情删改。

写在阅读之前

如果你想要的是快速构建R包骨架,推荐阅读在巨人的肩膀前行 催化R包开发进行学习;如果你想了解更为基本的R包创建知识和过程,推荐阅读开发自己的R包sayHello、谢益辉写的编写R包章节以及本文创建包的文档建立包章节。阅读一篇文档就想写好R包是远远不够的,还需要不断地实战和理解,才能融会贯通。不仅如此,一些函数和文档写法、技巧也是值得学习的,这正是本文的价值所在。

Let’s begin!

Table of Contents ## Table of Contents * [介绍](#intro) * [非参分析和npar包](#npar-pkg) * [开发包](#devel-pkg) * [创建包的文档](#document-pkg) * [建立包](#build-pkg) * [深入学习](#further-reading)

介绍

技术上,包只不过是一套函数、文档和数据的合集,以一种标准的格式保存。包让你能以一种定义良好的完整文档化方式来组织你的函数,而且便于你将程序分享给他人。

下面是几条我们可能想要创建包的理由:

  • 让一套常用函数及其使用说明文档更加容易取用。
  • 创造一个能解决重要分析问题(比如对缺失值的插值)的程序(一套相关函数)。

创造一个有用的包也是自我介绍和回馈R社区的好办法,R包可以直接分享或者通过CRAN和Github的在线软件库分享。

这里我们一起来学习如何从头到尾开发一个R包。我们将要开发的包名为npar,它提供非参组间比较的函数。如果结果变量是非正态或者异方差的,这套分析技术可以用来比较两组或多个组。这是分析师经常遇到的一个问题。

请使用以下代码下载包,然后保存到你现在的工作目录,接着把它安装到默认的R库当中。

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")
## Installing package into '/home/wangshx/R/x86_64-pc-linux-gnu-library/3.4'
## (as 'lib' is unspecified)

在接下来的内容中,我们将把npar包当做一个测试的地方,描述和展示它的功能特性和函数。然后接着我们从头开始创建包。

非参分析和npar包

非参分析是一种数据分析方法,它在传统参数分析的假设(比如正态性和同方差)不成立的情况下特别有用。这里我们会着重比较两组间或多组相互独立的数值结果变量的方法。

我们对npar包中的life数据集进行探究,它提供了对2007-2009年美国每个州65岁人的健康预期寿命(Healthy Life Expectancy, HLE)。估计值分别针对男性(hlem)和女性(hlef)。

数据集也提供了一个名为region(地区)的变量,此变量分为东北部、中北部、南部和西部。我从R标准安装中的state.region数据框中提取该变量并添加到所关注的数据集中。

假设我们想指导女性的HLE估计值是否在不同地区有显著的不同,一种方式是使用单因素方差分析,不过方差分析假设结果变量时正态分布的,并且不同地区之间的方差是同质的,让我们对这两个假设进行检查。

女性的HLE估值的分布可以用直方图来可视化。

library(npar)
hist(life$hlef, xlab="Healthy Life Expectancy (years) at Age 65",
     main="Distribution of Healthy Life Expectancy for women",
     col="grey", breaks = 10)

plot of chunk unnamed-chunk-2

上图可以看出因变量是负偏的,较低的值数量较少。

不同地区的HLE分数的方差可以用并排点图来可视化:

library(ggplot2)
ggplot(data=life, aes(x=region, y=hlef)) + 
    geom_point(size=3, color="darkgrey") + 
    labs(title="Distribution of HLE Estimates by Region",
         x="US Region", y="Healthy Life Expectancy at Age 65") + 
    theme_bw()

plot of chunk unnamed-chunk-3

上图中的每个点代表一个州,每个地区的方差都有所不同,东北部和南部的方差差异最大。

因此此数据不符合方差分析的两个重要假设,所以我们需要不同的分析方法。不像方差分析,非参方法不作出正态性和同方差的假设。在这个例子中,我们只需要假设数据是有序的——更高的分值意味着更高的HLE。因此,对于此问题,非参方法是一个合理的选择。

npar包比较分组

我们可以使用npar包比较独立组别的数值型因变量,至少要求它是有序的。对于数值型因变量和分类型分组变量,它提供了一下几方面功能:

  • 综合Kruskal-Wallis检验,检验组间是否有差异。
  • 每一组的描述性统计量
  • 事后比较(Wilcoxon秩和检验),即每次进行两组之间的比较,检验得到的p值可以调整,以进行多重比较。
  • 都带有注释的并排箱线图,用于可视化不同组别之间的差异。

以下代码实现了用npar包对不同地区女性的HLE估值进行比较:

library(npar)
results <- oneway(hlef ~ region, life)
summary(results)
## data: hlef on region 
## 
## Omnibus Test
## Kruskal-Wallis chi-squared = 17.8749, df = 3, p-value = 0.0004668
## 
## Descriptive Statistics
##        South North Central   West Northeast
## n      16.00         12.00 13.000     9.000
## median 13.00         15.40 15.600    15.700
## mad     1.48          1.26  0.741     0.593
## 
## Multiple Comparisons (Wilcoxon Rank Sum Tests)
## Probability Adjustment = holm
##         Group.1       Group.2    W       p   
## 1         South North Central 28.0 0.00858 **
## 2         South          West 27.0 0.00474 **
## 3         South     Northeast 17.0 0.00858 **
## 4 North Central          West 63.5 1.00000   
## 5 North Central     Northeast 42.0 1.00000   
## 6          West     Northeast 54.5 1.00000   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
plot(results, col="lightblue", main="Multiple Comparisons",
     xlab="US Region", ylab="Healthy Life Expectancy (years) at Age 65")

plot of chunk unnamed-chunk-4

首先,代码运行了一个Kruskal-Wallis检验,这是对不同地区间HLE差异的总体检验,p值0.0005指出确实存在差异。

接着,计算了每一个地区的描述性统计量,东北部HLE估值最高,南部最低;地区变化量最小的是东北部,最大的是南部。

尽管K-W检验表示不同地区的HLE有差异,但没有指出有多大的差异。若要得出此信息,需要用Wilcoxon秩和检验来成对地分组比较。对于四个组别,有4x(4-1)/2=6次比较。

南部和中北部的差异是统计上显著的(p=0.009),而东北部和中北部之间的差异并不显著(p=1.0),实际上,南部和其他地区都有所差异,但是其他地区之间的差异并不显著。

在计算多重比较的时候,必须考虑到alpha膨胀的可能性:实际上组别之间并没有显著差异,但是计算出存在差异的概率有所上升。对于六次独立的比较过程,至少有一次出错误差异的概率是$1-(1-0.05)^6$或$0.26$。

发现至少有一个错误的概率在四分之一左右,所以我们会想对每一次比较的p值进行调整,这样使得整体错误率保持在一个合理的水平(比如0.05)。

oneway()函数使用R标准安装的p.adjust()函数来完成这个功能。p.adjust()函数用很多种方法的其中一种来对多重比较的p值进行调整。尽管Bonferonni校正可能最广为人知,但是Holm校正更加强大,因此后者被设置为默认选项。

使用图形最容易看出不同组别之间的差异。plot()语句生成并排的箱线图。一条水平虚线表示所有观测值总体的中位数。

这些分析明显地指出南部女性很可能在65岁之后的预期寿命更短。这对健康服务的分布和侧重点也有所启示。你如果想分析一下男性的HLE估计值,看看是否会有类似的结论。

下一节描述了npar包的代码文件

开发包

npar包有四个函数:oneway()print.oneway()summary.oneway()plot.oneway()。第一个是主函数,计算相关的统计量;另外三个是用于输出和画图的S3面向对象泛型函数。

一个不错的办法是把每个函数分布放在扩展名为.R的不同文本文件中。尽管这不是严格要求的,但是能够使你更好地组织工作。此外、尽管并不要求函数名和文件名相同,不过这是一个好的代码习惯。

点击下方函数名可阅读文件内容:

每个文件的开头都包含了一系列以#'开头的注释。R解释器会忽略它们,不过我们可以使用roxygen2包来把这些注释转换为我们的R包文档。

oneway()函数计算相关的统计量,print()summary()plot()展示结果。下面我们学习开发oneway()函数。

计算统计量

oneway.R文件中的oneway()函数计算所有所需的统计量。

下面我们分段解析

#' @title 非参组间比较
#'
#' @description
#' \code{oneway} 计算非参组间比较,包括综合检验和事后成对组间比较
#' 
#' @details
#' 这个函数计算了一个综合Kruskal-Wallis检验,用于检验组别是否相等,接着使用
#' Wilcoxon秩和检验来进行成对比较。如果因变量之间没有相互依赖的话,可以计算精确
#' 的Wilcoxon检验。使用\code{\link{p.adjust}}来对多重比较所得到的p值进行调整
#' 
#' @param formula 一个formula对象。用于表示因变量和分组变量之间的关系
#' @param data 一个包含了模型里变量的数据框
#' @param exact logical变量。如\code{TRUE},计算精确的Wilcoxon检验
#' @param sort logical变量,如果\code{TRUE},用因变量中位数来对组别进行排序
#' @param method 用于调整多重比较的p值的方法
#' @export
#' @return 一个有7个元素的列表
#' \item{CALL}{函数调用}
#' \item{data}{包含因变量和组间变量的数据框}
#' \item{sumstats}{包含每组的描述性统计量的数据框}
#' \item{kw}{K-W检验的结果}
#' \item{method}{用于调整p值的方法}
#' \item{wmc}{包含多重比较的数据框}
#' \item{vnames}{变量名} 
#' @author Rob Kabacoff <rkabacoff@@statmethods.net>
#' @examples
#' results <- oneway(hlef ~ region, life)
#' summary(results)
#' plot(results, col="lightblue", main="Multiple Comparisons",
#'      xlab="US Region", ylab="Healthy Life Expectancy at Age 65")

头部包含以#'开头的注释会被roxygen2包用于生成包文档。接下来你会看到列出的函数参数。用户提供一个形为因变量~分组变量的模型公式和一个包含了数据的数据框。默认会计算近似的p值并按照因变量中位数对组别排序。用户可以从八种调整p值的方法选择一种,其中holm方法(列出的第一个选项)为默认选项。

oneway <- function(formula, data, exact=FALSE, sort=TRUE,               
                method=c("holm", "hochberg", "hommel", "bonferroni",      
                         "BH", "BY", "fdr", "none")){
  ######### 检查参数 ##########
  if (missing(formula) || class(formula) != "formula" ||
        length(all.vars(formula)) != 2)                                   
       stop("'formula' is missing or incorrect")  

  method <- match.arg(method) # 参数匹配
    
    
  ######## 设定数据 ###########
  df <- model.frame(formula, data)                           
  y <- df[[1]]
  g <- as.factor(df[[2]])
  vnames <- names(df)
  
  ######## 重新排序 ############
  if(sort) g <- reorder(g, y, FUN=median)                          
  groups <- levels(g)
  k <- nlevels(g)
  
  
  ######## 计算总体统计量 ########
  getstats <- function(x)(c(N = length(x), Median = median(x),      
                          MAD = mad(x)))   
  sumstats <- t(aggregate(y, by=list(g), FUN=getstats)[2])
  rownames(sumstats) <- c("n", "median", "mad")
  colnames(sumstats) <- groups
  
  ######## 统计检验 ###########
  kw <- kruskal.test(formula, data)                          
  wmc <- NULL
  for (i in 1:(k-1)){
    for (j in (i+1):k){
      y1 <- y[g==groups[i]]
      y2 <- y[g==groups[j]] 
      test <- wilcox.test(y1, y2, exact=exact)
      r <- data.frame(Group.1=groups[i], Group.2=groups[j], 
                      W=test$statistic[[1]], p=test$p.value)
      # note the [[]] to return a single number
      wmc <- rbind(wmc, r)
    }
  }
  wmc$p <- p.adjust(wmc$p, method=method)
  
  
  data <- data.frame(y, g)                                    
  names(data) <- vnames
  results <- list(CALL = match.call(), 
                  data=data,
                  sumstats=sumstats, kw=kw, 
                  method=method, wmc=wmc, vnames=vnames)
  class(results) <- c("oneway", "list")
  return(results)
}


结果被打包并作为一个列表返回。最后该列表的类被设置为c("oneway", "list")这是使用泛型函数处理对象的重要步骤

尽管列表提供了所有需要的信息,但你一般不会直接获取单个分量的信息。相反,你可以创建泛型函数print()summary()plot()以更加具体而有意义的方法来表达它们。

打印结果

各个领域的大部分分析函数都伴随着对应的泛型函数print()summary()print()提供了对象的基本或原始信息,summary()提供了更加具体或处理(汇总)过的信息。如果图形在上下文中是有意义的,plot()函数也经常一起提供。

在S3面对对象中,如果一个对象有类属性”foo”,则print(x)print.foo()函数存在时运行print.foo(),在print.foo()函数不存在时运行print.default()summary()plot()也有着相同的规则。因为oneway()函数返回一个类为”oneway”的对象,所以你需要定义print.oneway()summary.oneway()plot.oneway()函数。

对于life数据集,print(results)生成了多重比较的基本信息:

print(results)
## data: hlef by region 
## 
## Multiple Comparisons (Wilcoxon Rank Sum Tests)
## Probability Adjustment = holm
##         Group.1       Group.2    W       p
## 1         South North Central 28.0 0.00858
## 2         South          West 27.0 0.00474
## 3         South     Northeast 17.0 0.00858
## 4 North Central          West 63.5 1.00000
## 5 North Central     Northeast 42.0 1.00000
## 6          West     Northeast 54.5 1.00000

代码打印了一个有信息量的头部,接着是Wilcoxon统计量和调整后的每一对组别的p值(Group.1和Group.2)。

其源代码文件print.R内容如下:

#' @title 打印多重比较的结果
#'
#' @description
#' \code{print.oneway} 打印多重组间比较的结果
#'
#' @details
#' 这个函数打印出用 \code{\link{oneway}} 函数所创建的Wilcoxon成对多重比较的结果 
#' 
#' @param x 一个 \code{oneway}类型的变量
#' @param ... 要传输给函数的额外的变量
#' @method print oneway
#' @export
#' @return 静默返回输入的变量
#' @author Rob Kabacoff <rkabacoff@@statmethods.net>
#' @examples
#' results <- oneway(hlef ~ region, life)
#' print(results)
print.oneway <- function(x, ...){
  if (!inherits(x, "oneway"))       
    stop("Object must be of class 'oneway'")
  
  cat("data:", x$vnames[1], "by", x$vnames[2], "\n\n")  
  cat("Multiple Comparisons (Wilcoxon Rank Sum Tests)\n")
  cat(paste("Probability Adjustment = ", x$method, "\n", sep=""))
  
  print(x$wmc,  ...)
}

头部包含的以#'开头的注释会被roxygen2包生成包文档。inherits()函数用于确保被提交的对象有”oneway”这个类。一系列cat()函数打印对分析过程的描述。最后调用print.default()把多重比较打印出来。

汇总结果

print()函数相比,summary()函数生成的输出更加全面,处理得更好。对于健康预期寿命数据,summary(results)的语句生成如下结果:

summary(results)
## data: hlef on region 
## 
## Omnibus Test
## Kruskal-Wallis chi-squared = 17.8749, df = 3, p-value = 0.0004668
## 
## Descriptive Statistics
##        South North Central   West Northeast
## n      16.00         12.00 13.000     9.000
## median 13.00         15.40 15.600    15.700
## mad     1.48          1.26  0.741     0.593
## 
## Multiple Comparisons (Wilcoxon Rank Sum Tests)
## Probability Adjustment = holm
##         Group.1       Group.2    W       p   
## 1         South North Central 28.0 0.00858 **
## 2         South          West 27.0 0.00474 **
## 3         South     Northeast 17.0 0.00858 **
## 4 North Central          West 63.5 1.00000   
## 5 North Central     Northeast 42.0 1.00000   
## 6          West     Northeast 54.5 1.00000   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

此输出包含了K-W检验的结果、每一组的描述性统计量(样本量、中位数和绝对离差中位数)以及多重比较的结果。此外,多重比较的表格用星号来标记出显著的结果。

下方代码列出summary.oneway()函数的具体内容:

#' @title 汇总单因子非参分析的结果
#'
#' @description
#' \code{summary.oneway} 汇总了单因子非参分析的结果
#'
#' @details
#' 这个函数对\code{\link{oneway}}函数所分析的结果进行汇总并打印。这包括了每一组的
#' 描述性统计量,一个综合的Kruskal-Wallis检验的结果,以及一个Wilcoxon成对多重比较
#' 的结果
#' 
#' @param object 一个\code{oneway}类型的对象
#' @param ... 额外的参数
#' @method summary oneway
#' @export
#' @return 静默返回输入的对象
#' @author Rob Kabacoff <rkabacoff@@statmethods.net>
#' @examples
#' results <- oneway(hlef ~ region, life)
#' summary(results)
summary.oneway <- function(object, ...){
  if (!inherits(object, "oneway")) 
    stop("Object must be of class 'oneway'")
    
  if(!exists("digits")) digits <- 4L
  
  kw <- object$kw
  wmc <- object$wmc
  
  cat("data:", object$vnames[1], "on", object$vnames[2], "\n\n")
  
  ############ K-W 检验 #############
  cat("Omnibus Test\n")                        
  cat(paste("Kruskal-Wallis chi-squared = ", 
             round(kw$statistic,4), 
            ", df = ", round(kw$parameter, 3), 
            ", p-value = ", 
               format.pval(kw$p.value, digits = digits), 
            "\n\n", sep=""))
  
  #### 描述性统计量 #####
  cat("Descriptive Statistics\n")     
  print(object$sumstats, ...)
  
  #### 表格标记 ######
  wmc$stars <- " "                  
  wmc$stars[wmc$p <   .1] <- "."
  wmc$stars[wmc$p <  .05] <- "*"
  wmc$stars[wmc$p <  .01] <- "**"
  wmc$stars[wmc$p < .001] <- "***"
  names(wmc)[which(names(wmc)=="stars")] <- " "                          
  
  cat("\nMultiple Comparisons (Wilcoxon Rank Sum Tests)\n")    
  cat(paste("Probability Adjustment = ", object$method, "\n", sep=""))
  print(wmc, ...)
  cat("---\nSignif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1\n")
}

绘制结果

最后的函数plot()oneway()函数返回的结果进行可视化:

plot(results, col="lightblue", main="Multiple Comparisons",
     xlab="US Region", ylab="Healthy Life Expectancy (years) at Age 65")

plot of chunk unnamed-chunk-7

不像标准的箱线图,这幅图提供了展示每一组中位数和样本量的标记,还有一条展现出总体中位数的虚线。下面是函数的源代码:

#' @title 对非参组间比较的结果进行绘图
#'
#' @description
#' \code{plot.oneway} 对非参组间比较的结果进行绘图
#'
#' @details
#' 这个函数使用标记了的并排箱线图对\code{\link{oneway}}函数所生成的非参组间比较
#' 结果进行绘图。中位数和样本量被放置在图的上方、总体中位数用一条虚线进行表示
#' 
#' @param x 一个\code{oneway}类型的对象
#' @param ... 被传递给
#' \code{\link{boxplot}} 的额外参数
#' @method plot oneway
#' @export
#' @return NULL
#' @author Rob Kabacoff <rkabacoff@@statmethods.net>
#' @examples
#' results <- oneway(hlef ~ region, life)
#' plot(results, col="lightblue", main="Multiple Comparisons",
#'      xlab="US Region", ylab="Healthy Life Expectancy at Age 65")
plot.oneway <- function(x, ...){  

  ###### 检查输入 ##########
  if (!inherits(x, "oneway")) 
    stop("Object must be of class 'oneway'")
    
  ##### 生成箱线图 ########
  data <- x$data                                    
  y <- data[,1]
  g <- data[,2]
  stats <- x$sumstats
  lbl <- paste("md=", stats[2,], "\nn=", stats[1,], sep="")
  opar <- par(no.readonly=TRUE)
  par(mar=c(5,4,8,2))
  boxplot(y~g,  ...)
  ###### 为图添加标记 #######
  abline(h=median(y), lty=2, col="darkgray")
  axis(3, at=1:length(lbl), labels=lbl, cex.axis=.9)
  par(opar)
}

首先,要检查被传到函数的参数的类型。然后,代码提取出原始的数据,画出箱线图。接下来添加标记(中位数、样本量和总体中位数)。使用abline()来添加总体中位数那条线,使用axis()函数在图像的顶部添加中位数和样本量。

添加样本数据到包

在创建包时,加上一个或多个可用于试验所提供的数据集是一个好注意。对于npar包添加life数据库,将该数据框以.rda的文件形式添加到包里面,最后把这个文件移动到包文件树的data子文件夹里。

同时我们需要创建一个.R文件来作为此数据框的文档,代码点击life.R (此文档不再翻译,自行理解)。

创建包的文档

每个R包都符合一套对文档的强制方案。包里的每一个函数都必须使用LaTeX来以同样的风格撰写文档;LaTeX是一种文档标记语言和排版系统。每个函数都被分别放置在不同的.R文件里,函数对应的文档则被放置在一个.Rd文件中。

这种方式有两个限制。第一,文档和它所描述的函数是分开放置的。如果你改变了函数代码,就必须搜索出对应的文档并进行改写。第二,用户必须学习LaTeX,而它的学习曲线比较陡峭。

roxygen2包能够极大地简化文档的创建过程。你在每一个.R文件的头部放置一段注释作为对应的文档。然后使用一种简单的标记语言来创建文档。当Roxygen2处理文件的时候,以#'开始的行会被用来自动生成LaTeX文档(.Rd文件)。

下面描述了每个文件头部的注释所使用的标签,标签(roclet)是使用Roxygen2创建LaTeX文档的基础。

@title          函数名
@description    一行的函数描述
@details        多行的函数描述
@param          函数参数
@export         添加函数到NAMESPACE
@method generic class   泛型S3方法的文档
@return         函数返回的值
@author         作者和联系地址
@examples       使用函数的例子
@note           使用函数的注意事项
@aliases        用户能够找到文档的额外别名
@references     函数所涉及的方法的参考文档

在创建文档的时候,另外一些标记元素也很有用。标签\code{text}用代码字体把text打印出来,\link{function}创建一个指向本包或者别处函数的超级链接。最后item{text}创建一个分项列表,这对于描述函数的返回的结果特别有用。

除此之外,我们应该(这个任务是可选的)提供包的介绍文档,以便于?npar时能够找到,点击npar.R可查阅以下代码:

#' 非参组间比较的函数
#' 
#' npar提供了计算和可视化组间差异的工具
#' 
#' @docType package
#' @name npar-package
#' @aliases npar
NULL

.......这个文件必须在NULL后有一个空白行........

最后创建一个名为DESCRIPTION的文本文件用于描述包。以下是一个样例

Package: npar
Type: Package
Title: Nonparametric group comparisons
Version: 1.0
Date: 2015-01-26
Author: Rob Kabacoff
Maintainer: Robert Kabacoff <robk@statmethods.net>
Description: This package assesses group differences using nonparametric
    statistics. Currently a one-way layout is supported. Kruskal-Wallis tests
    followed by pairwise Wilcoxon tests are provided. p-values are adjusted
    for multiple comparisons using the p.adjust() function.
    Results are plotted via annotated boxplots.
LazyData: true
License: GPL-3
Packaged: 2014-11-05 19:06:44 UTC; rkabacoff
RoxygenNote: 6.0.1

DESCRIPTION部分可以包含多行,但是第一行之后的行必须缩进。lazyData:yes语句表示此包里的数据集应该在载入后尽快变得可用。如果参数设置为no用户将不得不用data(life)来获取这个数据集。

建立包

终于到了建立包的时候,开发者创建包的圣经是R核心开发团队“Writing R Extensions”。Friedrich Leishch也提供了一个创建包的优秀指南(http://mng.bz/Ks84

以下创建包流程适用Windows、Mac以及Linux平台:

  1. 安装必要的工具。用install.packages("roxygen2", depend=TRUE)来下载和安装roxygen2包。如果使用的是Windows平台,你还需要安装Rtools.exe(https://cran.r-project.org/bin/windows/Rtools)和MiKTeX(http://miktex.org)。如果使用的是Mac,则需要安装MacTeX(http://www.tug.org/mactex)。Rtools、MiKTeX和MacTeX都不是包而是软件,因此需要在R的外部安装它们。

  2. 配置文件夹。在你的home目录下(启动R时的当前工作目录),创建一个叫作npar的子文件夹。在这个文件夹中创建两个子文件夹,分别为R和data。把DESCRIPTION文件放在npar文件夹里,并把源代码文件放在子文件夹R里。把life.rda文件放在子文件夹data里。

  3. 生成文档。加载roxygen2包,然后使用roxygenize()函数处理每一个代码文件头部文档。

library(roxygen2)
roxygenise("npar") # 可以使用npar的相对路径或绝对路径

roxygenize()函数创建一个新的子文件夹man,这个文件夹内部有与每个函数对应的.Rd文档文件。

此外,该函数对DESCRIPTION文件添加了信息,也创建了一个NAMESPACE文件

# Generated by roxygen2: do not edit by hand

S3method(plot,oneway)
S3method(print,oneway)
S3method(summary,oneway)
export(oneway)

NAMESPACE文件控制函数的可视性:是所有函数都直接暴露给用户,还是有些函数在内部被其他函数使用?本例中所有函数都是直接对用户可用的。

  1. 建立包。用系统以下命令建立包
system("R CMD build npar")

这段代码在当前目录中创建了npar_1.0.tar.gz文件。现在这个包的格式可以分发给其他用户。

若要为Windows平台创建二进制.zip文件,可以运行以下代码:

system(Rcmd INSTALL --build npar)

注意,只有在Windows平台工作才能用这个方法。如果你不在Windows机器下运行R,但是想创建一个适用于Windows的二进制包,可以使用http://win-builder.r-project.org提供的在线服务。

  1. 检查包(可选)。要在包上运行全部的一致性检查,可以运行以下代码:
system("R CMD check npar")

如果你想分发你的包到CRAN上,检查结果必须是没有任何错误和警告的。

  1. 创建一个PDF手册(可选)。运行:
system("R CMD Rd2pdf npar")

创建一本类似于你在CRAN上看到的PDF参考手册。

  1. 本地安装包(可选)。运行语句
system("R CMD INSTALL npar")

在你的机器上安装这个包,另一个本地安装包的方法是使用

install.packages("./npar_1.0.tar.gz", repos=NULL, type="source")

在开发周期中,你也许想从本地机器删除一个包从而能够安装一个新的版本。这种情况下,用代码:

detach(package:npar, unload=TRUE)
remove.packages("npar")

来得到一个全新的开始。

  1. 上传包到CRAN(可选),如果你想把包添加到CRAN库从而分享给他人,只需要进行以下三步:

深入学习

大部分包所包含的代码都是完全用R完成的,不过你也可以在R中调用编译后的C、C++、Fortran和Java代码。引入外来代码的典型情况是希望以此来提升运行速度,或者作者想在R代码中使用现有的外部软件库

有很多种方法可以引入编译后的外部代码,有用的函数包括.C()Fortran()External().Call()等。还有一些包的创造是为了简化流程,比如inline(C、C++、Fortran)、Rcpp(C++)和rJava(Java)。

为R包添加外部的编译后代码参考“Writing R Extensions”以及相关函数和包的帮助文件。

人该是自己生活的主宰,而不是别人手里的行货