初探R语言的OOP系统

编程入门 行业动态 更新时间:2024-10-05 07:26:10

初探R<a href=https://www.elefans.com/category/jswz/34/1770116.html style=语言的OOP系统"/>

初探R语言的OOP系统

初探R语言的OOP系统

  • 前言
  • S3方法
    • Methods()
    • UseMethod()
    • 建议
  • S4方法

前言

R语言中有两个最为基础的概念:对象函数。和之前学过的其余编程语言相比(如:c++,python),变量不会声明为某种数据类型。 变量分配有 R 对象,R 对象的数据类型便为变量的数据类型。 该文介绍R的OOP系统,R目前有四个各自独立的对象系统,分别为:S3, S4, R5/RC, R6. 类别class实际上是作为R对象的一个属性(attribute).目前大部分包是用 R3 OOP系统的,个人觉得 R4 更接近我们平时在 c, java 里的面向对象方法。

S3方法

为什么叫S3,其实很简单,S是因为R语言来源自S语言,3是版本信息。前面也提过,R内置函数中大部分函数是S3型的。举一个令人熟悉的例子,mtcars是数据包
datasets自带的一个数据集,假如我们想初步了解一下这个包中各变量的统计信息,我们很可能会使用上函数 summary(),得到结果如下:

>summary(mtcars)
>      mpg             cyl             disp             hp             drat             wt             qsec             vs               am        Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0   Min.   :2.760   Min.   :1.513   Min.   :14.50   Min.   :0.0000   Min.   :0.0000  1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5   1st Qu.:3.080   1st Qu.:2.581   1st Qu.:16.89   1st Qu.:0.0000   1st Qu.:0.0000  Median :19.20   Median :6.000   Median :196.3   Median :123.0   Median :3.695   Median :3.325   Median :17.71   Median :0.0000   Median :0.0000  Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7   Mean   :3.597   Mean   :3.217   Mean   :17.85   Mean   :0.4375   Mean   :0.4062  3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0   3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.90   3rd Qu.:1.0000   3rd Qu.:1.0000  Max.   :33.90   Max.   :8.000   Max.   :472.0   Max.   :335.0   Max.   :4.930   Max.   :5.424   Max.   :22.90   Max.   :1.0000   Max.   :1.0000  gear            carb      Min.   :3.000   Min.   :1.000  1st Qu.:3.000   1st Qu.:2.000  Median :4.000   Median :2.000  Mean   :3.688   Mean   :2.812  3rd Qu.:4.000   3rd Qu.:4.000  Max.   :5.000   Max.   :8.000  

随意拟合一个模型,再次使用函数 summary(),得到的却是不同的信息

> summary(lm(mpg ~ disp + hp, data = mtcars))Call:
lm(formula = mpg ~ disp + hp, data = mtcars)Residuals:Min      1Q  Median      3Q     Max 
-4.7945 -2.3036 -0.8246  1.8582  6.9363 Coefficients:Estimate Std. Error t value Pr(>|t|)    
(Intercept) 30.735904   1.331566  23.083  < 2e-16 ***
disp        -0.030346   0.007405  -4.098 0.000306 ***
hp          -0.024840   0.013385  -1.856 0.073679 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1Residual standard error: 3.127 on 29 degrees of freedom
Multiple R-squared:  0.7482,	Adjusted R-squared:  0.7309 
F-statistic: 43.09 on 2 and 29 DF,  p-value: 2.062e-09

也就是说,虽然这两个函数是同名函数,但实际上的工作却有所不同,像是C++里的 重载。实际上这两条语句的完整写法是

> summary.data.frame(mtcars)
> summary.lm(lm(mpg ~ disp + hp, data = mtcars))

字如其名,一个用于对数据框里的各列(各个变量)进行统计描述,一个对拟合的线性模型进行估计、检验等评价。
我们这时才发现,我们平时在使用 summary 功能时,压根就不需要考虑我们具体是对什么进行 summary,是 summary.data.frame 还是 summary.lm 还是其他的,不仅减轻了我们的负担,也使代码更美观(这里的美观是说将这一步的作用简明表示出来,就足矣了)
我们需要熟悉这样的形式:
g e n e r i c . m e t h o d \rm generic.method generic.method
泛型函数有很多,直接输入函数名(不带括号)可以得到相关信息, UseMethod(“as.Date”) 预示着这是个泛型函数,如下:

> as.Date
function (x, ...) 
UseMethod("as.Date")
<bytecode: 0x000001b63a781060>
<environment: namespace:base>

Methods()

The call to “UseMethod” means that this is a generic function, and it will call a specific method, a function, based on the class of the first argument. (All methods are functions; not all functions are methods.) You can list all the methods for a generic with methods(). (摘自 R for Data Science)
翻译一下,大概是说:对“UseMethod”的调用意味着这是一个泛型函数,它将基于第一个参数的类调用一个特定的方法,一个函数。(所有方法都是函数;并非所有函数都是方法。)可以使用methods()列出泛型的所有方法。
(为啥有时候查资料时,觉得很绕,是因为:它们把“泛型”和“方法”都和函数挂钩,叫成是“泛型函数”,且把方法叫做函数 😢)
为了不混淆概念,我们约定只用这两个名词: “泛型” & “方法”
methods() 的作用是查看该泛型下的所有方法

> methods("as.Date")
[1] as.Date.character   as.Date.default     as.Date.factor      as.Date.numeric     as.Date.POSIXct     as.Date.POSIXlt     as.Date.vctrs_sclr*
[8] as.Date.vctrs_vctr*
see '?methods' for accessing help and source code

对于输入的 x,泛型 as.Date() 会根据 x 的类型选择合适的方法,比如当 x 是个字符类型时,此时 as.Date 会使用其下的方法character(),也即实际上运行的是 as.Date.character(x)
使用 getS3method() 可以看到方法的具体实现:(实际上我发现大部分方法好像直接打名字也能出来源代码,不过确实有些不行,具体什么原因我也不太清楚)

> getS3method("as.Date", "default")
function (x, ...) 
{if (inherits(x, "Date")) xelse if (is.null(x)) .Date(numeric())else if (is.logical(x) && all(is.na(x))) .Date(as.numeric(x))else stop(gettextf("do not know how to convert '%s' to class %s", deparse1(substitute(x)), dQuote("Date")), domain = NA)
}
<bytecode: 0x000001b639562ba0>
<environment: namespace:base>

UseMethod()

接下来我们学习如何创建自己的泛型及其下面的方法,这个例子来自文章A Simple Guide to S3 Methods
UseMethod() 用于自定义泛型,我们自定义泛型时,只需要把下面的变量名及UseMethod里的参数改一下就行了,虽然说两处地方名字可以不同,但这么做是徒劳的。

rss <- function(x) UseMethod("rss")

不同对象类型的残差平方和是不大相同的,针对 rpart,randomForest 类型我们可以定制不同的方法,因为它们所起的作用是相同的——计算RSS,所以在同一泛型下是自然合理的写法。

rss.rpart <- function(x, ···){result <- sum((residuals(x)**2))return(result)
}
rss.randomForest <- function(x, ···){temp <- x$y - x$predicted  result <- sum(temp**2)  return(result)
}

(这里写的是很粗糙的方法,真正写的时候要考虑各种异常情况)

在同一泛型下的不同方法中,用一个方法尤为重要——default 方法,当输入的对象类型与其他方法都不匹配时,便会考虑到default方法,一般来说,里面会考虑到很多的异常情况,并且对于各种错误操作提出有效的警告。启示使用者足够及时更正。

建议

最后一点小建议,S3面向对象方法“标识符”是 “.” 插在泛型和函数之间,其实不太好,我认为是一种设计上的考虑欠缺,像S4,它的标识符是 $ 我们平时命名函数名时其实也经常会出现点,知道了S3系统后,我现在改用下划线了。

S4方法

未完待续

参考资料:

  • A Simple Guide to S3 Methods
  • R语言中S3类
  • R语言S3对象的简单学习
  • R语言的S4类
  • R 面向对象编程(三)—— RC
  • The S3 object system

更多推荐

初探R语言的OOP系统

本文发布于:2024-02-13 21:18:49,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1760512.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:语言   系统   OOP

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!