34.s3classes初步
这无疑是篇进阶贴,从2个月前完全看不懂,到现在能领悟一些感觉,其中苦乐,真的是满脸都是泪。。。
好吧,为了了解S3classes我们先来了解R中vector,我们都该知道R中一个vector中数据类型必须一致,但如果不一致怎么办呢,用list().
R>x=c("r",1) #尝试不同类型放入一个向量x<br />
R>x<br />
[1] "r" "1"<br />
R>class(x) #看看x是什么类型<br />
[1] "character"<br />
R>#发现数字1被强制转换成字符型了<br />
R>x=list("r",1)<br />
R>x<br />
[[1]]<br />
[1] "r"</p>
<p>[[2]]<br />
[1] 1</p>
<p>R>class(x)<br />
[1] "list"<br />
R>#输出结果不一样,可以放入不同类型到list里去,有C基础的同学会容易联想到struct,其实意思差不多<br />
了解了list后我们再来看看R中的一些分析结果是怎么看的:
<br />
R>x=1:9<br />
R>y=2:10<br />
R>z=lm(y~x)<br />
R>z #这句与print(z)等价</p>
<p>Call:<br />
lm(formula = y ~ x)</p>
<p>Coefficients:<br />
(Intercept) x<br />
1 1 </p>
<p>R>class(z)<br />
[1] "lm"<br />
R><br />
这里z的class是"lm",先不急着分析,我们再来看看什么是print(),学过C++重载,多态概念的同学可能好理解(这也是为什么我看过一边C++基础后才能写出这部分内容),我们通常print一个数字也可以,print一个字符也可以,但只需要用print就可以了,类似的还有summary().这是为什么呢?
其实当你print(z)的时候,R就找z的class,然后根据class(z)="numeric"还是"character"决定调用print.numeric(当然不一定有这个,我只是形象举例),还是print.character,这里R知道(说了z,和print(z)一样)哦,原来z是一个叫"lm"的class,然后它就调用print.lm,下面我们来看看这个的具体代码是什么:
R>print.lm<br />
function (x, digits = max(3L, getOption("digits") - 3L), ...)<br />
{<br />
cat("\nCall:\n", paste(deparse(x$call), sep = "\n", collapse = "\n"),<br />
"\n\n", sep = "")<br />
if (length(coef(x))) {<br />
cat("Coefficients:\n")<br />
print.default(format(coef(x), digits = digits), print.gap = 2L,<br />
quote = FALSE)<br />
}<br />
else cat("No coefficients\n")<br />
cat("\n")<br />
invisible(x)<br />
}<br />
<bytecode: 0x05ad5350><br />
<envi
这段代码聪明的人会恍然大悟,原来输出的什么call,coefficients格式全都通过print.lm这个函数设置好了!
那么我们unclass(z)让z没有"lm"这个类名称看看会有什么:
R>unclass(z)<br />
$coefficients<br />
(Intercept) x<br />
1 1 </p>
<p>$residuals<br />
1 2 3 4 5<br />
6.421462e-16 -1.463870e-15 2.575398e-16 2.160477e-16 7.762798e-17<br />
6 7 8 9<br />
4.247140e-16 5.495385e-16 -1.992870e-16 -5.044570e-16 </p>
<p>$effects<br />
(Intercept) x<br />
-1.800000e+01 7.745967e+00 2.636780e-16 5.551115e-17 -2.495833e-16 </p>
<p>-6.917210e-17 -1.110223e-16 -1.026523e-15 -1.498367e-15 </p>
<p>$rank<br />
[1] 2</p>
<p>$fitted.values<br />
1 2 3 4 5 6 7 8 9<br />
2 3 4 5 6 7 8 9 10 </p>
<p>$assign<br />
[1] 0 1</p>
<p>$qr<br />
$qr<br />
(Intercept) x<br />
1 -3.0000000 -1.500000e+01<br />
2 0.3333333 7.745967e+00<br />
3 0.3333333 1.290994e-01<br />
4 0.3333333 -2.866584e-17<br />
5 0.3333333 -1.290994e-01<br />
6 0.3333333 -2.581989e-01<br />
7 0.3333333 -3.872983e-01<br />
8 0.3333333 -5.163978e-01<br />
9 0.3333333 -6.454972e-01<br />
attr(,"assign")<br />
[1] 0 1</p>
<p>$qraux<br />
[1] 1.333333 1.258199</p>
<p>$pivot<br />
[1] 1 2</p>
<p>$tol<br />
[1] 1e-07</p>
<p>$rank<br />
[1] 2</p>
<p>attr(,"class")<br />
[1] "qr"</p>
<p>$df.residual<br />
[1] 7</p>
<p>$xlevels<br />
named list()</p>
<p>$call<br />
lm(formula = y ~ x)</p>
<p>$terms<br />
y ~ x<br />
attr(,"variables")<br />
list(y, x)<br />
attr(,"factors")<br />
x<br />
y 0<br />
x 1<br />
attr(,"term.labels")<br />
[1] "x"<br />
attr(,"order")<br />
[1] 1<br />
attr(,"intercept")<br />
[1] 1<br />
attr(,"response")<br />
[1] 1<br />
attr(,".Environment")<br />
<environment: R_GlobalEnv><br />
attr(,"predvars")<br />
list(y, x)<br />
attr(,"dataClasses")<br />
y x<br />
"numeric" "numeric" </p>
<p>$model<br />
y x<br />
1 2 1<br />
2 3 2<br />
3 4 3<br />
4 5 4<br />
5 6 5<br />
6 7 6<br />
7 8 7<br />
8 9 8<br />
9 10 9</p>
<p>R>
这个时候很多人会哇,原来这样,是的,lm()返回了很多东西,但是R的作者力求简洁,所以我们z或者print(z)出来的东西不多,原因上面也贴出了是print.lm这个函数的问题!而不了解真相的初学者会想"切!就这种结果么?SAS好多了。。"
好吧,我们继续,上面说了lm()返回了很多结果,我们就来看看lm():
R>lm<br />
function (formula, data, subset, weights, na.action, method = "qr",<br />
model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE,<br />
contrasts = NULL, offset, ...)<br />
{<br />
ret.x <- x<br />
ret.y <- y<br />
cl <- match.ca<br />
.....<br />
z <- list(coefficients = if (is.matrix(y)) matrix(, 0,<br />
3) else numeric(), residuals = y, fitted.values = 0 *<br />
y, weights = w, rank = 0L, df.residual = if (!is.null(w)) sum(w !=<br />
0) else if (is.matrix(y)) nrow(y) else length(y))<br />
....<br />
class(z) <- c(if (is.matrix(y)) "mlm", "lm")<br />
..<br />
大家可以看到,lm返回了一个list,把各种结果都装到z这个list里去了,然后给它命名了"lm"(这里还涉及到继承的概念,后面再说)
简单的总结下就是lm返回了一大堆结果,然后放到一个list里,然后取class"lm",当print的时候R调用print.lm只输出了其中部分结果。
到这里我再引用一个例子:
R>j <- list(name="Joe", salary=55000, union=T)<br />
R>class(j) <- "employee"<br />
R>attributes(j)<br />
$names<br />
[1] "name" "salary" "union" </p>
<p>$class<br />
[1] "employee"</p>
<p>R>str(j)<br />
List of 3<br />
$ name : chr "Joe"<br />
$ salary: num 55000<br />
$ union : logi TRUE<br />
- attr(*, "class")= chr "employee"<br />
R>j<br />
$name<br />
[1] "Joe"</p>
<p>$salary<br />
[1] 55000</p>
<p>$union<br />
[1] TRUE</p>
<p>attr(,"class")<br />
[1] "employee"<br />
R>print.employee <- function(wrkr) {<br />
+ cat(wrkr$name,"\n")<br />
+ cat("salary",wrkr$salary,"\n")<br />
+ cat("union member",wrkr$union,"\n")<br />
+ }<br />
R>j<br />
Joe<br />
salary 55000<br />
union member TRUE<br />
R>methods(,"employee")<br />
[1] print.employee<br />
R>j<br />
Joe<br />
salary 55000<br />
union member TRUE<br />
R>
大家应该知道了吧,最后讨论下继承:
R>k <- list(name="Kate", salary= 68000, union=F, hrsthismonth= 2)<br />
R>class(k) <- c("hrlyemployee","employee")<br />
R>k<br />
Kate<br />
salary 68000<br />
union member FALSE
其实很简单,就是k有个子class,父class,R先找有没有符合第一个class的print.hrlyemployee,没有就调用print.employee
</p>