数据科学42 |数据产品开发-创建R包
7. 创建R包
R包是函数或其他数据、对象的集合,以一种系统的方式组织起来。R包可以由世界各地的用户/开发者撰写,可以扩展R的基本功能,实现很多强大的功能。
7.1 获取R包
👉可以从CRAN和Bioconductor获得
通过install.packages()进行安装来自CRAN / Bioconductor的R包
👉可以从GitHub,Bitbucket,Gitorious等其他地方获得
可以使用devtools包中的install_github()安装来自GitHub的R包
7.2 R包的开发过程
- 在R脚本文件(.R)中编写代码
- 将R脚本文件合并到R包结构中
- 编写用户功能文档,包括其他的一些材料(示例,演示,数据集,教程)
- 打包并提交到CRAN或Bioconductor
- 可以将源代码库上传到GitHub或其他源代码共享网站
- 用户发现代码存在的问题,可以向开发者反馈或直接向开发者提供更改方案
- 开发者可以合并更改并发布新版本
7.3 构成R包的必需文件
- 一个命名为R包名称的目录
- DESCRIPTION描述文件,包含有关这个R包的信息
- 含有R源代码的R脚本文件(.R),存放在命名为R的子目录中
- 存放在命名为man的子目录中的Documentation文件
- NAMESPACE文件
7.3.1 DESCRIPTION文件
必需包含的描述内容:
・Package:R包的名称
・Title:R包的全称
・Description:对R包的详细介绍
・Version:版本号
・Author:原作者名字
・Maintainer:维护者的名字及邮件
・License:源代码的许可证,常见许可证:GNU、BSD、MIT
非必需包含但常见的描述内容:
・Depends:编写R代码所依赖的其他R包
・Suggests:可能希望用户安装的R包
・Date:发布日期,格式:YYYY-MM-DD
・URL:R包的主页
・可以添加其他字段
图2.DESCRIPTION文件示例(gpclib包)7.3.2 R代码文件
・将R代码文件保存到子目录R中
・子目录R中可以有任意数量的文件
・将代码按逻辑功能分成多个不同的文件(不必将所有代码放在同一个文件中)
・实现R包所有功能的代码都放在子目录R中
7.3.3 NAMESPACE文件
・NAMESPACE文件用于指示要导出的函数
・导出的函数可以作为公共API由用户调用 ・非导出函数不能由用户直接调用(但是可以查看代码) ・对用户隐藏实施细节,并提供更简洁的R包界面
・NAMESPACE文件还可以指示从其他包导入的函数
・允许R包使用其他包,而不会使其他包对用户可见 ・导入函数会加载程序包,但不会将其附加到搜索列表中
关键指令:
NAMESPACE文件示例(gpclib包):
#从graphics包导入plot函数
importFrom(graphics, plot)
import(methods)
#导出两个S4类对象
exportClasses("gpc.poly", "gpc.poly.nohole")
#导出到用户的多种方法,适用于已定义的这些新类的用户
exportMethods("show", "get.bbox", "plot", "intersect", "union",
"setdiff", "[", "append.poly", "scale.poly", "area.poly",
"get.pts", "coerce", "tristrip", "triangulate")
#导出两个函数
export("read.polyfile", "write.polyfile")
useDynLib(gpclib)
7.3.4 Documentation文件
・Documentation文件(.Rd)放在子目录man中,使用户可以了解应该如何使用函数
・用特定的标记语言编写
・每一个导出的函数都需要一个Documentation文件 限制导出到用户的函数数量的原因之一
・此外,还可以记录其他内容,如概念,R包概述
例:R的基本安装中Line函数的帮助文件,?line
可以查看生成的帮助文件
\name{line}
\alias{line}
\alias{residuals.tukeyline}
\title{Robust Line Fitting}
\description{
Fit a line robustly as recommended in \emph{Exploratory Data Analysis}.
}
\usage{
line(x, y)
}
\arguments{
\item{x, y}{the arguments can be any way of specifying x-y pairs. See
\code{\link{xy.coords}}.}
}
\details{
Cases with missing values are omitted.
Long vectors are not supported.
}
\value{
An object of class \code{"tukeyline"}.
Methods are available for the generic functions \code{coef},
\code{residuals}, \code{fitted}, and \code{print}.
}
\references{
Tukey, J. W. (1977).
\emph{Exploratory Data Analysis},
Reading Massachusetts: Addison-Wesley.
}
图3.Line函数帮助文件
可以看到Documentation文件以特定的标记语言编写并生成了函数的帮助文件,包含了函数的标题、描述、用法、参数、细节、返回值、参考等内容。
7.4 构建和检查R包
・R CMD build是一个命令行程序,用于创建R包归档文件(.tar.gz)
・R CMD check将对R包进行一系列检查测试
检查每个导出函数是否有对应的Documentation文件 检查代码是否可以加载且没有重大的编码问题或错误 运行Documentation文件中的示例 R包必须通过R CMD Check中的所有测试而没有任何警告或任何错误才能放在CRAN上
・可以在终端使用命令行运行R CMD build或 R CMD check程序
・也可以在R中使用system()函数运行
system("R CMD build newpackage")
system("R CMD check newpackage")
例:构建R包框架
library(utils)
package.skeleton("Newpackage")
Creating directories ...
Creating DESCRIPTION ...
Creating NAMESPACE ...
Creating Read-and-delete-me ...
Saving functions and data ...
Making help files ...
Done.
Further steps are described in './Newpackage/Read-and-delete-me'.
传递R包的名称给utils包中的package.skeleton()函数将创建该R包名称命名的目录框架,包括目录结构(子目录R和子目录man)、DESCRIPTION文件、NAMESPACE文件、Documentation文件。
图4.构建R包框架package.skeleton()函数将创建一个可供填写的DESCRIPTION文件;创建一个可供编写的NAMESPACE文件。默认情况下,package.skeleton()函数将当前工作空间中的存在的所有函数生成R代码文件和Documentation文件,并将它们写到子目录R和子目录man中;当前工作空间中的所有数据集或变量都将存放到子目录data中。
7.5 类和方法classes and methods
R是一个基于使用泛型函数的面向对象的编程语言。
・R编写类和方法的基本思想是开发一个新结构来支持新的数据类型,并开发一套适用于该数据类型的新函数。
・R的类和方法是面向对象编程(object oriented programming,OOP)的系统,允许用户(利用系统功能)成为程序员(扩展系统功能)。
・R是交互式的,并且支持OOP,而许多其他常见的OOP语言(C ++,Java,Python,Perl)通常不是交互式的,R中的OOB结构与大多数其他语言的结构不同。
R中的两种类和方法:
・S3(S语言的第三版) S3模型相对更老、更简单、结构更少,非正式的。R中使用的许多基本功能都是基于S3类和方法构建的。
・S4(S语言的第四版) S4模型更新且更复杂,标准严格,更加正式。
两个系统独立运行,但鼓励开发人员用S4编写新项目。
7.5.1 面向对象的编程
-
类class
・类class是对某些事物、对象的描述(新数据类型、想法) ・可以在methods包中使用setClass()函数定义 ・R中的所有对象都有一个类,可以通过class()函数确定
1)数值类型numeric:数值数据或数字序列组成的向量
2)逻辑类型logical:TRUE、FALSE、NA 注意:默认情况下,NA是逻辑类型,但是可以将NA作为数字/字符运算结果
3)字符类型character:字符串
4)线性模型类型lm
class(1)
[1] "numeric"
class(TRUE)
[1] "logical"
class(rnorm(100))
[1] "numeric"
class(NA)
[1] "logical"
class("foo")
[1] "character"
x <- rnorm(100)
y <- x + rnorm(100)
fit <- lm(y ~ x)
class(fit)
[1] "lm"
-
对象object
・对象object是类的实例 ・可以使用new()创建
-
方法method
・方法method是在某些特定的类的对象上运行的函数,也可以认为是针对特定类的对象的泛型函数的实现・可以为现有的泛型函数编写新方法,或创建新的泛型函数及相关方法 ・
getS3method(<genericFunction>, <class>)
可以返回给定类的S3方法的代码
某些S3方法(如mean.default)可以直接被调用,但最好永远不要调用他们,通常始终使用泛型函数。
・getMethod(<genericFunction>, <signature/class>)
可以返回给定类的S4方法的代码
1)签名signature表示该方法接受的对象的类的字符向量 2)S4方法无法被调用
-
泛型函数generic function
・泛型函数generic function是R中用于调用实现特定功能的方法的函数(如plot,mean,predict) ・泛型函数本身不执行任何计算,单独输入函数名称(即绘图)将返回函数的内容 ・S3和S4函数看起来不同,但概念上相似 ・
methods("mean")
返回与S3泛型函数关联的方法 ・showMethods("show")
返回与S4泛型函数关联的方法 ・函数的第一个参数为特定类的对象,调用针对特定类的对象相应的方法
S3泛型函数
mean
function (x, ...)
UseMethod("mean")
<bytecode: 0x7fe4fca91470>
<environment: namespace:base>
print
function (x, ...)
UseMethod("print")
<bytecode: 0x7fe4fcadbc88>
<environment: namespace:base>
S3方法
methods("mean")
[1] mean.Date mean.default mean.difftime mean.IDate*
[5] mean.ITime* mean.POSIXct mean.POSIXlt mean.quosure*
[9] mean.vctrs_vctr*
see '?methods' for accessing help and source code
S4泛型函数
show
standardGeneric for "show" defined from package "methods"
function (object)
standardGeneric("show")
<bytecode: 0x7fe4f9e3d088>
<environment: 0x7fe4f9b6aa10>
Methods may be defined for arguments: object
Use showMethods("show") for currently available ones.
(This generic function excludes non-simple inheritance; see ?setIs)
show相当于print,但是通常不会在R中直接调用这两个函数,因为大多数对象会在命令行自动打印。
S4方法
showMethods("show")
Function: show (package methods)
object="ANY"
object="C++Class"
object="C++Function"
object="C++Object"
object="classGeneratorFunction"
object="classRepresentation"
object="color"
object="Enum"
object="EnumDef"
object="envRefClass"
object="externalRefMethod"
object="function"
(inherited from: object="ANY")
object="genericFunction"
object="genericFunctionWithTrace"
object="MethodDefinition"
object="MethodDefinitionWithTrace"
object="MethodSelectionReport"
object="MethodWithNext"
object="MethodWithNextWithTrace"
object="Module"
object="namedList"
object="ObjectsWithPackage"
object="oldClass"
object="refClassRepresentation"
object="refMethodDef"
object="refObjectGenerator"
object="signature"
object="sourceEnvironment"
object="standardGeneric"
(inherited from: object="genericFunction")
object="SymbolicConstant"
object="traceable"
・工作过程
1)泛型函数检查对象的类>
2)如果存在用于该类的方法,对该对象调用该方法(完成处理)>
3)如果没有用于该类的方法,则搜索默认方法>
4)如果存在默认方法,则对该对象调用默认方法(完成处理)>
5)如果不存在默认方法,则报错(完成处理)
・对于data.frame这样的类,其中每个列可以属于不同的类,函数使用相应的方法
例:S3泛型函数工作过程
set.seed(2)
x <- rnorm(100)
mean(x)
[1] -0.03069816
x为数值类型,泛型函数mean没有对应数值类型的对象调用的方法,因此泛型函数mean将调用默认方法。
head(getS3method("mean", "default"), 10)
1 function (x, trim = 0, na.rm = FALSE, ...)
2 {
3 if (!is.numeric(x) && !is.complex(x) && !is.logical(x)) {
4 warning("argument is not numeric or logical: returning NA")
5 return(NA_real_)
6 }
7 if (na.rm)
8 x <- x[!is.na(x)]
9 if (!is.numeric(trim) || length(trim) != 1L)
10 stop("'trim' must be numeric of length one")
tail(getS3method("mean", "default"), 10)
15 if (anyNA(x))
16 return(NA_real_)
17 if (trim >= 0.5)
18 return(stats::median(x, na.rm = FALSE))
19 lo <- floor(n * trim) + 1
20 hi <- n + 1 - lo
21 x <- sort.int(x, partial = unique(c(lo, hi)))[lo:hi]
22 }
23 .Internal(mean(x))
24 }
getS3method()函数可以查看S3泛型函数的方法的具体运行代码。
7.5.2 创建新的类和方法
创建新类的原因
・是扩展R功能的强大方法
・可以代表新的数据类型(例如基因表达,时空,分层,稀疏矩阵)
・可以表示尚未想到的新概念、想法(例如,拟合点过程模型,混合效果模型,稀疏矩阵)
・可以从用户中抽象或隐藏实现细节
创建新类
・setClass()函数可以创建新类
・至少需要指定类型的名称name
・也可以指定数据属性attributes或插槽slots(一个类实际上是一个列表,所以插槽是该列表的元素)
定义类的方法
・setMethod() 函数可以定义类的方法
・@用于访问类的插槽或属性
・showClass()函数显示有关类的定义和信息。
例:定义一个新的多边形类ploygon class,并定义新的绘图方法
library(methods)
setClass("polygon", representation(x = "numeric", y = "numeric"))
使用setClass()函数创建具有(x,y)坐标集的ploygon类,以x和y坐标为插槽,包含多边形所有顶点的x坐标和y坐标。
setMethod("plot", "polygon",
function(x, y, ...) {
plot(x@x, x@y, type = "n", ...)
xp <- c(x@x, x@x[1])
yp <- c(x@y, x@y[1])
lines(xp, yp)
})
使用setMethod() 函数为多边形类创建绘图方法,在这种情况下,polygon为签名signature。在全局环境中为graphics包中的plot创建泛型函数。
showMethods("plot")
Function: plot (package graphics)
x="ANY"
x="color"
x="polygon"
查看新定义的绘图方法,扩展现有的plot函数。