学习R语言:面向对象编程
本文内容来自《R 语言编程艺术》(The Art of R Programming),有部分修改
R 语言虽然与传统的面向对象语言 (如 C++,JAVA 和 Python) 不同,但也是一个明显的面向对象语言。
- R 中所有要素都是对象,从数字到字符串到矩阵
- R 支持封装 (encapsulation)
- R 类支持多态 (polymorphic)
- R 允许继承 (inheritance)
下面介绍 R 语言中的 S3 和 S4 类。
S3 类
R 中原始的类结构,包含一个列表,附加类名属性和调度 (dispatch) 功能
S3 泛型函数
同一个函数可以针对不同的类调用不同的操作。
实例:线性模型函数 lm()
中的 OOP
x <- c(1, 2, 3)
y <- c(1, 3, 8)
lm_out <- lm(y ~ x)
class(lm_out)
[1] "lm"
lm_out
Call:
lm(formula = y ~ x)
Coefficients:
(Intercept) x
-3.0 3.5
这里使用的是 print.lm()
方法。
去掉 lm_out
的类属性,再打印对象
unclass(lm_out)
$coefficients
(Intercept) x
-3.0 3.5
$residuals
1 2 3
0.5 -1.0 0.5
$effects
(Intercept) x
-6.928203 -4.949747 1.224745
$rank
[1] 2
$fitted.values
1 2 3
0.5 4.0 7.5
$assign
[1] 0 1
$qr
$qr
(Intercept) x
1 -1.7320508 -3.4641016
2 0.5773503 -1.4142136
3 0.5773503 0.9659258
attr(,"assign")
[1] 0 1
$qraux
[1] 1.577350 1.258819
$pivot
[1] 1 2
$tol
[1] 1e-07
$rank
[1] 2
attr(,"class")
[1] "qr"
$df.residual
[1] 1
$xlevels
named list()
$call
lm(formula = y ~ x)
$terms
y ~ x
attr(,"variables")
list(y, x)
attr(,"factors")
x
y 0
x 1
attr(,"term.labels")
[1] "x"
attr(,"order")
[1] 1
attr(,"intercept")
[1] 1
attr(,"response")
[1] 1
attr(,".Environment")
<environment: R_GlobalEnv>
attr(,"predvars")
list(y, x)
attr(,"dataClasses")
y x
"numeric" "numeric"
$model
寻找泛型函数的实现方法
print
function (x, ...)
UseMethod("print")
<bytecode: 0x0000029cb2efe180>
<environment: namespace:base>
methods()
函数列出某个函数对应的所有泛型函数。
methods(print)
[1] print.acf*
[2] print.anova*
[3] print.aov*
[4] print.aovlist*
[5] print.ar*
[6] print.Arima*
[7] print.arima0*
[8] print.AsIs
[9] print.aspell*
...skip...
星号标注的是不可见函数 (nonvisible function)。
可以使用 getAnywhere()
函数找到函数的命名空间。
getAnywhere(print.lm)
A single object matching ‘print.lm’ was found
It was found in the following places
registered S3 method for print from namespace stats
namespace:stats
with value
function (x, digits = max(3L, getOption("digits") - 3L),
...)
{
cat("\nCall:\n", paste(deparse(x$call), sep = "\n",
collapse = "\n"), "\n\n", sep = "")
if (length(coef(x))) {
cat("Coefficients:\n")
print.default(format(coef(x), digits = digits), print.gap = 2L,
quote = FALSE)
}
else cat("No coefficients\n")
cat("\n")
invisible(x)
}
<bytecode: 0x0000029cba7e8b90>
<environment: namespace:stats>
调用命名空间 stats
中的 print.lm()
函数
stats:::print.lm(lm_out)
Call:
lm(formula = y ~ x)
Coefficients:
(Intercept) x
-3.0 3.5
编写 S3 类
S3 类实例通过构建列表的方式来创建:
- 创建列表
- 设置类属性
j <- list(
name="Joe",
salary=55000,
union=TRUE
)
class(j) <- "employee"
attributes(j)
$names
[1] "name" "salary" "union"
$class
[1] "employee"
使用默认的 print()
输出
j
$name
[1] "Joe"
$salary
[1] 55000
$union
[1] TRUE
attr(,"class")
[1] "employee"
为 employee
类型定义 print
函数
print.employee <- function(wrkr) {
cat(wrkr$name, "\n")
cat("salary", wrkr$salary, "\n")
cat("union member", wrkr$union, "\n")
}
检查 employee
类的方法
methods(, "employee")
[1] print
实际测试
j
Joe
salary 55000
union member TRUE
使用继承
k <- list(
name="Kate",
salary=68000,
union=FALSE,
hrsthismonth=2
)
class(k) <- c("hrlyemployee", "employee")
hrlyemployee
类继承 employee
类的 print
方法
k
Kate
salary 68000
union member FALSE
S4 类
S4 类与 S3 类对比
操作 | S3 类 | S4 类 |
---|---|---|
定义类 | 在构造函数的代码中隐式定义 | setClass() |
创建对象 | 创建列表,设置类属性 | new() |
引用成员变量 | $ | @ |
实现泛型函数 f() | 定义 f.classname() | setMethod() |
声明泛型函数 | UseMethod() | setGeneric() |
编写 S4 类
定义 employee
类
setClass(
"employee",
representation(
name="character",
salary="numeric",
union="logical"
)
)
创建 employee
类对象
joe <- new(
"employee",
name="Joe",
salary=55000,
union=TRUE
)
joe
An object of class "employee"
Slot "name":
[1] "Joe"
Slot "salary":
[1] 55000
Slot "union":
[1] TRUE
成员变量称为 slot,通过 @
引用
joe@salary
[1] 55000
可以使用 slot()
函数通过组件名访问组件
slot(joe, "salary")
[1] 55000
可以给组件赋值
joe@salary <- 65000
joe
An object of class "employee"
Slot "name":
[1] "Joe"
Slot "salary":
[1] 65000
Slot "union":
[1] TRUE
slot(joe, "salary") <- 88000
joe
An object of class "employee"
Slot "name":
[1] "Joe"
Slot "salary":
[1] 88000
Slot "union":
[1] TRUE
访问未定义的组件会报错,但在 S3 中不会报错
joe@salry <- 48000
Error in (function (cl, name, valueClass) : ‘salry’ is not a slot in class “employee”
在 S4 类上实现泛型函数
使用 setMethod()
定义泛型函数。
S4 类中 show()
函数类似 S3 类中的 print()
函数
show(joe)
An object of class "employee"
Slot "name":
[1] "Joe"
Slot "salary":
[1] 88000
Slot "union":
[1] TRUE
为 employee
类定义泛型函数 show
setMethod(
"show",
"employee",
function(object) {
inorout <- ifelse(object@union, "is", "is not")
cat(object@name, "has a salary of", object@salary,
"and", inorout, "in the union", "\n")
}
)
show(joe)
Joe has a salary of 88000 and is in the union
S3 类和 S4 类对比
注:作为一名初学者,笔者尚不了解 R 语言常用的类型。 不过,类型安全对于维护大型项目来说至关重要。 S4 类有明确的类型定义,可以在 IDE 中使用代码提示功能。
对象管理
R 语言提供多种工具管理环境中的对象
ls()
rm()
save()
- 查看对象结构,如
class()
和mode()
exists()
用 ls()
函数列出所有对象
ls()
[1] "ct" "cttab" "ctu"
[4] "hz" "j" "joe"
[7] "k" "lm_out" "print.employee"
[10] "x" "y" "z"
pattern
参数用于筛选
ls(pattern="e")
[1] "joe" "print.employee"
用 rm()
函数删除特定对象
rm("j", "joe")
list
参数指定删除对象的名称。
下面代码删除所有对象
rm(list = ls())
用 save()
函数保存对象集合
save()
保存成文件,load()
从文件中加载
z <- rnorm(100000)
hz <- hist(z)
save(hz, file="hzfile")
ls()
[1] "hz" "z"
rm(hz)
ls()
[1] "z"
load("hzfile")
ls()
[1] "hz" "z"
plot(hz)
查看对象内部结构
class()
,mode()
names()
,attributes()
unclass()
,str()
edit()
ct <- read.table(
"../data/ct.dat",
header=TRUE,
)
ct
生成列联表
cttab <- table(ct)
cttab
Voted.For.X.Last.Time
Vote.for.X No Yes
No 2 0
Not Sure 0 1
Yes 1 1
使用 unclass()
查看对象结构
ctu <- unclass(cttab)
ctu
Voted.For.X.Last.Time
Vote.for.X No Yes
No 2 0
Not Sure 0 1
Yes 1 1
class(ctu)
[1] "matrix" "array"
str()
以更紧凑的方式显示
str(cttab)
'table' int [1:3, 1:2] 2 0 1 0 1 1
- attr(*, "dimnames")=List of 2
..$ Vote.for.X : chr [1:3] "No" "Not Sure" "Yes"
..$ Voted.For.X.Last.Time: chr [1:2] "No" "Yes"
page()
和 edit()
可以用于查看对象内容,比如函数的具体实现。
names()
函数显示对象有哪些组件
names(hz)
[1] "breaks" "counts" "density" "mids" "xname" "equidist"
attributes()
会给出更多信息,包括类名称
attributes(hz)
$names
[1] "breaks" "counts" "density" "mids" "xname" "equidist"
$class
[1] "histogram"
exists()
函数
exists()
判断对象是否存在
exists("acc")
[1] FALSE
参考
学习 R 语言系列文章:
《快速入门》
《向量》
《矩阵和数组》
《列表》
《数据框》
《因子和表》
《编程结构》
《数学运算与模拟》
本文代码请访问如下项目: