5.5 用户自编函数
R的最大优点之一就是用户可以自行添加函数。事实上,R中的许多函数都是由已有函数构成的。一个函数的结构看起来大致如此:
- myfunction <- function(arg1, arg2, ... ){
- statements
- return(object)
- }
函数中的对象只在函数内部使用。返回对象的数据类型是任意的,从标量到列表皆可。让我们看一个示例。
假设你想编写一个函数,用来计算数据对象的集中趋势和散布情况。此函数应当可以选择性地给出参数统计量(均值和标准差)和非参数统计量(中位数和绝对中位差)。结果应当以一个含名称列表的形式给出。另外,用户应当可以选择是否自动输出结果。除非另外指定,否则此函数的默认行为应当是计算参数统计量并且不输出结果。代码清单5-8给出了一种解答。
代码清单5-8
mystats()
:一个由用户编写的描述性统计量计算函数
mystats <- function(x, parametric=TRUE, print=FALSE) {
if (parametric) {
center <- mean(x); spread <- sd(x)
} else {
center <- median(x); spread <- mad(x)
}
if (print & parametric) {
cat("Mean=", center, "\n", "SD=", spread, "\n")
} else if (print & !parametric) {
cat("Median=", center, "\n", "MAD=", spread, "\n")
}
result <- list(center=center, spread=spread)
return(result)
}
要看此函数的实战情况,首先需要生成一些数据(服从正态分布的,大小为500的随机样本):
set.seed(1234)
x <- rnorm(500)
在执行语句:
y <- mystats(x)
之后,y$center
将包含均值(0.00184),y$spread
将包含标准差(1.03),并且没有输出结果。如果执行语句:
y <- mystats(x, parametric=FALSE, print=TRUE)
y$center
将包含中位数(–0.0207),y$spread
将包含绝对中位差(1.001)。另外,还会输出以下结果:
Median= -0.0207
MAD= 1
下面让我们看一个使用了switch
结构的用户自编函数,此函数可让用户选择输出当天日期的格式。在函数声明中为参数指定的值将作为其默认值。在函数mydate()
中,如果未指定type
,则long
将为默认的日期格式:
mydate <- function(type="long") {
switch(type,
long = format(Sys.time(), "%A %B %d %Y"),
short = format(Sys.time(), "%m-%d-%y"),
cat(type, "is not a recognized type\n")
)
}
实战中的函数如下:
> mydate("long")
[1] "Thursday December 02 2010"
> mydate("short")
[1] "12-02-10"
> mydate()
[1] "Thursday December 02 2010"
> mydate("medium")
medium is not a recognized type
请注意,函数cat()
仅会在输入的日期格式类型不匹配"long"
或"short"
时执行。使用一个表达式来捕获错误输入的参数值通常来说是一个好主意。
有若干函数可以用来为函数添加错误捕获和纠正功能。你可以使用函数warning()
来生成一条错误提示信息,用message()
来生成一条诊断信息,或用stop()
停止当前表达式的执行并提示错误。请参阅每个函数的在线帮助文档以了解详细用法。
小提示 一旦开始编写无论任何长度和复杂度的函数,优秀调试工具的重要性都会凸显出来。R中有许多实用的内建调试函数,也有许多用户贡献包提供了额外的功能。关于这个话题,一份优秀的参考资料是Duncan Murdoch整理的“Debugging in R”(http://www.stats.uwo.ca/faculty/murdoch/software/debuggingR)。
在创建好自己的函数以后,你可能希望在每个会话中都能直接使用它们。附录B描述了如何定制R环境,以使R启动时自动读取用户编写的函数。我们将在第6章和第8章中看到更多的用户自编函数示例。
你可以使用本节中提供的基本技术完成很多工作。如果你想要探索编写函数的微妙之处,或编写可以分发给他人使用的专业级代码,个人推荐两本优秀的书籍,你可在本书末尾的参考文献部分找到:Venables & Ripley(2000)以及Chambers(2008)。这两本书共同提供了大量细节和众多示例。
函数的编写就讲到这里,我们将以对数据整合和重塑的讨论来结束本章。