R中的“ S3方法”是什么意思?


124

由于我对R相当陌生,所以我不知道S3方法和对象是什么。我发现有S3和S4对象系统,有些建议尽可能使用S3而不是S4(请参阅http://google-styleguide.googlecode.com/svn/trunk/google-r-style上的 Google R样式指南。 html)*。但是,我不知道S3方法/对象的确切定义。

更新:从2019年开始,Google的R样式指南超链接现在位于此处

Answers:


85

通过查看?S3或可以找到大多数相关信息?UseMethod,但总而言之:

S3是指方法分派的方案。如果你使用的R用了一段时间,你会发现有printpredictsummary进行了大量不同类型的对象的方法。

在S3中,此方法的工作原理是:

  • 设置感兴趣的对象的类别(例如:调用方法的返回值glm具有class glm
  • 提供一种方法与一般的名称(例如print),则一个点,然后将类名(例如: print.glm
  • 必须对此通用名称(print)进行一些准备才能使其正常工作,但是如果您只是想使自己符合现有的方法名称,则不需要这样做(请参阅我之前提到的帮助)。

对于旁观者来说,尤其是对于您新创建的时髦模型拟合包的用户而言,键入predict(myfit, type="class")比方便得多predict.mykindoffit(myfit, type="class")

还有很多其他功能,但这应该可以帮助您入门。这种基于对象的属性(类)的分派方法的方式有很多缺点(C纯粹主义者可能会在晚上惊恐地醒着),但是在很多情况下,它的工作方式相当不错。在当前版本的R中,已经实现了新的方式(S4和引用类),但是大多数人仍然(仅)使用S3。


54

要开始使用S3,请查看该median功能的代码。median在命令提示符下键入将显示其主体中有一行,即

UseMethod("median")

这意味着它是一种S3方法。换句话说,median对于不同的S3类,您可以具有不同的功能。要列出所有可能的中位数方法,请键入

methods(median) #actually not that interesting.  

在这种情况下,只有一种方法(默认方法)可以调用任何方法。您可以通过键入以下内容查看代码

median.default

一个更有趣的例子是print函数,它具有许多不同的方法。

methods(print)  #very exciting

注意,某些方法*的名称旁边有s。这意味着它们被隐藏在某些包的命名空间中。使用find找出他们是在哪个包。例如

find("acf")  #it's in the stats package
stats:::print.acf

39

来自http://adv-r.had.co.nz/OO-essentials.html

R的三个OO系统在定义类和方法的方式上有所不同:

  • S3实现了一种称为通用功能OO的OO编程风格。这与大多数实现消息传递OO的编程语言(例如Java,C ++和C#)不同。通过消息传递,消息(方法)被发送到对象,并且对象确定要调用的函数。通常,此对象在方法调用中具有特殊外观,通常出现在方法/消息名称之前:例如canvas.drawRect(“ blue”)。S3不同。尽管仍通过方法进行计算,但是一种称为通用函数的特殊类型的函数决定要调用的方法,例如drawRect(canvas,“ blue”)。S3是一个非常随意的系统。它没有类的正式定义。

  • S4与S3相似,但更为正式。S3有两个主要区别。S4具有正式的类定义,用于描述每个类的表示形式和继承,并且具有用于定义泛型和方法的特殊辅助功能。S4还具有多个分派,这意味着泛型函数可以基于任意数量的参数(而不仅仅是一个)的类来选择方法。

  • 参考类简称为RC,与S3和S4完全不同。RC实现了消息传递的OO,因此方法属于类而不是函数。$用于分隔对象和方法,因此方法调用类似于canvas $ drawRect(“ blue”)。RC对象也是可变的:它们不使用R常用的“修改时复制”语义,而是在适当位置进行修改。这使他们难以推理,但允许他们解决使用S3或S4难以解决的问题。

还有另外一个系统不是很OO,但是在这里提到很重要:

  • 基本类型,其他OO系统基础的内部C级类型。基本类型主要使用C代码进行操作,但要了解它们很重要,因为它们为其他OO系统提供了构建基块。


10

尝试

methods(residuals)

其中列出了“ residuals.lm”和“ residuals.glm”。这意味着当您拟合线性模型,m和类型residuals(m),residentials.lm将被调用。拟合了广义线性模型后,将调用residentials.glm。有点像C ++对象模型倒挂了。在C ++中,您定义了具有虚函数的基类,这些虚函数被派生类覆盖。在R中,您定义一个虚拟(又称通用)函数,然后决定哪些类将覆盖此函数(即定义一个方法)。请注意,执行此操作的类不必从一个公共的超类派生。我不会同意通常更喜欢S3而不是S4。S4具有更多的形式主义(=更多类型),这对于某些应用程序可能太多了。但是,可以像C ++中的类或结构一样定义S4类。您可以指定某个类的对象由一个字符串和两个数字组成,例如:

setClass("myClass", representation(label = "character", x = "numeric", y = "numeric"))

用该类的对象调用的方法可以依赖于具有那些成员的对象。这与S3类非常不同,后者只是一堆元素的列表。

使用S3和S4,您可以通过fun(object, args)而不是通过调用成员函数object$fun(args)。如果您正在寻找类似后者的东西,请查看proto软件包。


我很确定,在R中,属于对象的成员函数和方法的概念在意义上没有太大意义。方法不属于对象(功能也是对象),但属于功能。
petermeissner's

3

这是根据Hadley Wickham(RStudio的首席科学家)的“ Advanced R,2nd edition”(CRC Press,2019年)更新的众多R对象系统的快速精简版本此处基于有关对象的章节在此处具有Web表示形式面向程序设计

高级R书的封面

从2015年第一版网页表示这里,对OO相应的章节这里

面向对象系统的方法

Hadley定义以下内容以区分两种不同的面向对象编程方法:

功能性OOP:方法(可调用的代码段)属于泛型函数(请勿与Java / C#泛型方法混淆)。可以将这些方法视为位于全局查找表中。运行时系统根据函数的名称以及传递给该函数的一个或多个参数的类型(或对象类)来找到要执行的方法(称为“方法分派”)。在语法方面,方法调用可能看起来像普通的函数调用:myfunc(object, arg1, arg2)。此调用将导致运行时查找与该对(“ myfunc”,typeof(object))或可能(“ myfunc”,typeof(object),typeof(arg1),typeof(arg2))关联的方法。如果语言支持。在R的S3中,泛型函数的全名给出了(函数名,类)对。例如:mean.Date是计算日期均值的方法。尝试methods("mean")使用函数名列出通用方法mean。功能性OOP方法可在OO先驱SmalltalkCommon Lisp对象系统Julia中找到。Hadley指出:“与R相比,Julia的实现是完全开发的并且性能极佳。”

封装的OOP:方法属于对象或类,方法调用通常看起来像object.method(arg1, arg2)。之所以称为封装,是因为对象封装了数据(字段)和行为(方法)。可以将该方法视为位于附加到对象或对象的类说明的查找表中。运行时将根据方法名称以及一个或多个参数的类型来查找方法。这是在“流行的” OO语言(例如C ++,Java,C#)中发现的方法。

在两种情况下,如果都支持继承(可能是继承),则运行时可以向上遍历类层次结构,直到找到与调用查找键匹配的对象为止。

如何找出R对象所属的系统

library(sloop) # formerly, "pryr"
otype(mtcars)
#> [1] "S3"

R对象系统

S3

  • 功能性OOP方法。
  • 根据Hadley所说,最重要的系统。
  • 最简单,最常见。R使用的第一个OO系统。
  • 带有基数R,在整个基数R中使用。
  • 依靠惯例而不是强制保证。
  • 参见钱伯斯,约翰·M和特雷弗·哈斯蒂。1992年。“ S中的统计模型”。Wadsworth和Brooks / Cole高级书籍和软件。
  • 详细信息“高级R,第二版” 在这里

S4

  • 功能性OOP方法。
  • 根据哈德利(Hadley)的看法,第三重要的系统。
  • S3的重写,因此与S3类似,但更正式,更严格:它迫使您仔细考虑程序设计。适用于构建大型系统(例如Bioconductor项目)。
  • 在基本的“方法”包中实现。
  • 参见:Chambers,John M.,1998年。“使用数据编程:S语言指南”。施普林格。
  • 详细信息“高级R,第二版” 在这里

RC又名“参考课程”

  • 封装的OOP方法。
  • 带有基本R。
  • 基于S4。
  • RC对象是S4对象的特殊类型,它们也是“可变的”。即,可以使用就地修改它们,而不用使用R的通常的修改时复制语义。请注意,可变状态很难推理,并且是丑陋错误的来源,但可以导致某些应用程序中的代码效率更高。

R6

  • 封装的OOP方法。
  • 根据哈德利的看法,第二重要的系统。
  • 可以在R6软件包中找到(安装为library(R6)
  • 与RC类似,但更轻巧,更快:它不依赖于S4或方法包。建立在R环境之上。也有:
    • 公共和私人方法
    • 活动绑定(访问时实际上调用方法的字段)
    • 跨包工作的类继承
    • 两个类的方法(代码属于类,并且可以通过访问一个实例selfprivatesuper)和成员函数(转让给字段的功能,但其不是方法,只是功能)
  • 提供了一种标准化的方法来逃避R的“修改时复制”语义
  • 请参见软件包站点:“ R6:R的封装的面向对象编程”
  • 详细信息“高级R,第二版” 在这里

其他

还有其他一些,例如R.oo(类似于RC),proto(基于原型,例如JavaScript)和Mutatr。但是,“高级R”表示:

除了被广泛使用的R6之外,这些系统还主要具有理论意义。它们确实具有优势,但是很少有R用户了解和理解它们,因此其他人很难阅读并为您的代码做出贡献。

请务必阅读的章节的取舍“高级R,第二版”了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.