收藏 分享(赏)

用R编写统计程序之三 可重用和面向对象编程.pdf

上传人:HR专家 文档编号:6266411 上传时间:2019-04-03 格式:PDF 页数:9 大小:269.28KB
下载 相关 举报
用R编写统计程序之三 可重用和面向对象编程.pdf_第1页
第1页 / 共9页
用R编写统计程序之三 可重用和面向对象编程.pdf_第2页
第2页 / 共9页
用R编写统计程序之三 可重用和面向对象编程.pdf_第3页
第3页 / 共9页
用R编写统计程序之三 可重用和面向对象编程.pdf_第4页
第4页 / 共9页
用R编写统计程序之三 可重用和面向对象编程.pdf_第5页
第5页 / 共9页
点击查看更多>>
资源描述

1、使用 R 编写统计程序,第 3 部分: 可重用和面向对象编程 了解 R 的底层特性 R 是一种作为自由软件发布的富统计环境,其中包括一种编程语言、一个交互式 shell 以及丰富的图形功能。本文延续 David 的 前两期文章(与 Brad Huntting 一起编写),研究 R 中的面向对象以及 R 中的其他一些一般编程概念。 本系列的 前两期 研究了 R 在 真实环境 中的使用方法。我们使用这几期的合作者收集的大量温度数据研究了各种统计分析和制图功能。正如前几篇文章中提到的,我们实际上只接触到了 R 中丰富的统计库的皮毛。 在本文中,我希望把对进一步统计分析本身 的讨论放在一边(这在很大程

2、度上是因为我自己还没有掌握决定最相关技术所需的统计知识;我以前的合作者 Brad Huntting 和许多读者在这些方面比我的知识丰富)。为了补充前两篇文章中提供的富统计概念,我希望带领读者探索一下 R 的底层语言设施。前两篇文章主要针对 R 的功能性编程方面;通过这些文章,读者会更熟悉面向对象语言。 另外,以前我们只以相当特定的 方式讨论了 R。在本期中,我将讨论如何为 R 开发创建可重用的和模块化的组件。 回到基础 在讨论 R 的面向对象概念之前,我们先回顾和澄清一下 R 的数据和函数概念。关于数据要记住的主要概念是 所有东西都是向量。尽管对象从表面上看与向量(矩阵、数组、data.fra

3、mes)不同,但是对象实际上只是向量加上额外的(可变的)属性,让 R 能够以特殊方式处理它们。 维(拼写为 dim)是(某些)R 向量最重要的属性之一。函数 matrix()、array() 和 dim() 是用于设置向量的维的简单函数。R 的 OOP 系统类似于把某些东西压缩在对象的 class 属性中。 我们先通过清单 1 中的代码回顾一下维的概念: 清单 1. 创建向量并分配维 v = 1:1000 typeof(v) 1 “integer“ attributes(v) NULL dim(v) = c(10,10,10) # (Re)dimension attributes(v) $di

4、m 1 10 10 10 v2 = matrix(1:1000, nrow=100, ncol=10) typeof(v2) 1 “integer“ attributes(v2) $dim 1 100 10 attr(v2,dim) = c(10,10,10) # Redimension attributes(v2) $dim 1 10 10 10 简单地说,将 dim 属性附着到向量上有好几种语法,但是这些语法在本质上做的事是一样的。 关于 R 的 所有东西都是向量 方式,容易引起混乱的一点是行操作和列操作可能不符合直觉。例如,以下代码创建一个 2D 数组(矩阵)并操作单列或单行: 清单 2

5、. 在矩阵向量上按行操作 m = matrix(1:12, nrow=3, ncol=4) m ,1 ,2 ,3 ,4 1, 1 4 7 10 2, 2 5 8 11 3, 3 6 9 12 sum(m) # sum of all elements of m 1 78 sum(m1,) # sum of first row 1 22 但是,如果希望创建一个向量来求每行的总和,您可能会编写下面这样的代码: 清单 3. 执行多个行操作的错误方式 sum(mc(1,2,3),) # NOT sum of each row 1 78 可能会 在这里构造一个循环,但是这与 R 的功能和面向向量的操作不一

6、致。实际上,应该使用函数apply(): 清单 4. 用 apply() 函数进行行操作 apply(m, 1, sum) # by row 1 22 26 30 apply(m, 2, sum) # by column 1 6 15 24 33 apply(m, c(1,2), sum) # by column AND row (sum of each single cell) ,1 ,2 ,3 ,4 1, 1 4 7 10 2, 2 5 8 11 3, 3 6 9 12 # Kinda worthless to sum each single cell, but what about th

7、is: a = array(1:24,c(3,4,2) apply(a, c(1,2), sum) # sum by depth in 3-D array ,1 ,2 ,3 ,4 1, 14 20 26 32 2, 16 22 28 34 3, 18 24 30 36 apply(a, c(3), sum) # sum of each depth slice 1 78 222 无限序列 在实践中,一种非常有用的构造是无限数字序列。例如,前几期的合作者对蒙特卡罗积分技术做了一些分析,为此他需要一个无限长的随机数序列。要知道,所需的无限序列类型并不 仅仅是能够在需要时生成一个新数字;还需要能够引用

8、以前引用的特定数字,而且是引用与以前相同的值。 显然,没有任何计算机语言或计算机能够存储无限序列 它们能够存储的是惰性的 和无限制的 序列。只能在需要访问时在现有列表中添加更多元素。例如,在 Python 中,可以这样实现:创建一个类似列表的对象和一个定制的 ._getitem_() 方法,这个方法会根据需要扩展内部列表。在 Haskell 中,惰性是内置在语言中的 实际上,所有东西都是惰性的。在我的 Haskell 教程(参考资料)中,我使用了一个创建所有 素数列表的示例: 清单 5. 用爱拉托逊斯筛法创建所有素数的 Haskell 列表 primes : Int primes = siev

9、e 2 sieve (x:xs) = x : sieve y | y getRand(3) # Single index 1 0.5557101 getRand(1:5) # Range 1 -0.05472011 -0.30419695 0.55571013 0.91667175 -0.40644081 getRand(sqrt(c(4,16) # Computed index collection 1 -0.3041970 0.9166717 getRand(100) # Force background vector extension 1 0.6577079 如果愿意,可以在使用元素前

10、用 assure() 确保向量足够长: 清单 8. 在访问前扩展向量(如果需要的话) assure(2000) 1 2000 inf_vector1500 1 1.267652 面向对象的 R R 能够进行完全面向对象的编程,但是要理解这种方式,需要重新回顾一下您对 OOP 的认识。Java? 和 C+ 或者 Python、Ruby 或 Smalltalk 等语言的用户可能已经对面向对象形成了一种相当明确的认识。这是对的,但是面向对象并不只限于一种模型。 R 的 OOP 方式基于泛型函数(generic function),而不是基于类层次结构。对于使用过 Lisp 的CLOS 或者读过我对使

11、用 Python 进行多种分派的讨论(参考资料)的读者,这个概念应该是熟悉的。不幸的是,R 的方式仍然是单一分派方式;在这方面,它与 C+、Java 等 传统语言相同。 我应该提醒您(尽管在本文中并不详细讨论这个问题):R 最近的版本附带有一个称为 methods 的包,这个包定义和操作所谓的 形式方法(formal method)。在许多方面,使用这些形式方法会采用传统 OOP 语言中的许多原理(和限制)。在任何情况下,R 中的形式 OOP 都基于本文中介绍的 非形式 OOP。methods 包仍然处于试验性阶段,但是经过某些调整后的版本肯定会出现在以后的 R 版本中。参考资料 提供了更多的

12、背景知识。 要完全理解 OOP,有一点必须记住:OOP 其实并不一定意味着继承,而是更一般的分派决策(dispatch decision)。也就是说,在传统 OOP 语言中的 obj.method() 调用会通过对象的方法解析次序(method resolution order,MRO) 寻找 第一个 具有 .method() 方法的 obj 祖先类。 第一个 的意思比较微妙(参见 参考资料 中对 Python 中 MRO 设计的讨论)。R 采用相同的决策方式,但是它将继承的概念从内部转到了外部。R 并不用一系列类 来定义和覆盖各种方法,而是创建一系列泛型函数,这些函数带有一个标记,指出它们应

13、该在什么类型的对象上进行操作。 泛型函数 作为一个简单的例子,我们来创建一个称为 whoami() 的泛型函数以及一些要被分派标记的方法: 清单 9. 创建泛型函数和一些标记方法 #- Create a generic method whoami whoami.foo whoami.bar whoami.default a = 1:10 b = 2:20 whoami(a) # No class assigned 1 “I dont know who I am“ attr(a,class) attr(b,class) whoami(a) 1 “I am a foo“ whoami(b) # S

14、earch MRO for defined method 1 “I am a bar“ attr(a,class) whoami(a) 1 “I am a bar“ 与传统的继承式语言一样,对象不必将同一个类用于它调用的每个方法。按照传统方式,如果 Child 继承自 Mom 和 Dad,那么 Child 类型的对象可能利用来自 Mom 的 .meth1(),以及来自 Dad的 .meth2()。在 R 中可以很自然地实现这种结构,但是 Mom 和 Dad 没有实质性内容,只是名称: 清单 11. 每个方法的分派解析 meth1 meth1.Mom meth1.Dad meth2 meth2.

15、Dad attr(a,class) meth1(a) # Even though meth1.Dad exists, Mom comes first for a 1 “Moms meth1“ meth2(a) 1 “Dads meth2“ 包含祖先 需要显式地指定对象的 MRO,而不是依赖于通过继承语法建立的隐式解析,这似乎不太方便。但是实际上,可以用很简单的包装器函数轻松地实现基于继承的 MRO。清单 11 中使用的 MRO 可能不是最好的(参见 参考资料 中 Simionato 的文章),但是它展示了这种思想: 清单 12. 用很简单的包装器函数实现基于继承的 MRO char0 = ch

16、aracter(0) makeMRO print(Me) 1 “Hello World“ attr(,“class“) 1 “Me“ “Mom“ “MaternalGrandma“ “Dad“ 5 “Uncle“ “PaternalGrandma“ 如果希望按照传统方式建立类/继承关系,那么需要包含创建的类的名称(比如在它的 classes 参数中包含 Mom)。实际上,如果每个类本身都是一个对象,那么以上系统更接近基于原型的 OOP 系统而不是基于类的系统。 而且,整个系统足够灵活,能够包含所有变体。如果愿意,可以自由地从实例对象分离出类对象 可以通过附加另一个属性(比如 type 可以是

17、class 或 instance;并用实用函数进行检查)建立一种命名约定来区分类,或者通过其他方式。 再论无限向量 既然已经有了 OOP 设施,我们实际上可以更好地处理前面提到的无限向量。第一个解决方案是有效的,但是最好能够有更无缝的和不可见的无限向量。 R 中的操作符只是进行函数调用的简写方式;可以自由地专门化操作符在类上的行为,产生任何其他函数调用的效果。采用这种方式,可以修复第一个系统中的一些缺点: 希望能够根据需要生成任意数量的无限向量。 希望能够配置使用的随机分布。 希望能够用另一个向量中的值对一个无限随机向量进行初始化。 现在就实现这些功能: 清单 14. 定义一个可索引的无限随机

18、向量 “.infinite_random“ v1000 1 -1.341881 print(v) * Infinite Random Vector * 1 -0.6247392 1.308057 1.654919 1.691754 -2.251065 . 996 1.027440 0.8376 -0.7066545 -0.7778386 -1.341881 . 结束语 要用 R 编写通用的函数、对象和类,需要重新思考已经习惯的传统过程式编程和面向对象编程。前两期文章展示了一些特定的 统计研究示例,这实际上不要求重新思考原来的模型,但是如果希望重用代码,就需要理解泛型函数和可以用它们编写的 外翻

19、式 OOP(外翻形式实际上更一般化)。 关键是 OOP 只涉及两个问题:调用什么代码 以及 如何做出这一决策。并非只能使用传统语言(比如 C+、Objective C、Java、Ruby 或 Python)的特定语法来表达这些;要将注意力放在分派概念本身上。 参考资料 学习 您可以参阅本文在 developerWorks 全球站点上的 英文原文。 使用 R 编写统计程序:第 1 部分. 初涉大量统计工具(developerWorks,2004 年 9 月)介绍 R 以及基本的统计分析。 使用 R 编写统计程序: 第 2 部分. 功能编程和数据采集(developerWorks,2004 年 1

20、0 月)探索了这种语言的功能。 Wikipedia 定义了 蒙特卡罗积分 算法,使用随机数解决各种计算问题。 Michele Simionato 的文章 The Python 2.3 Method Resolution Order 讨论了方法解析次序(MRO)以及不同 MRO 算法的优点。 R 形式方法包 提供了形式类和方法的实现。 R 核心开发团队提供了 R: A Language and Environment for Statistical Computing(12MB 的 PDF 文件),这是 R 的完全参考手册;其中的第 4 章讨论了形式方法和 methods 包。 Beginnin

21、g Haskell(developerWorks,2001 年 9 月)介绍了 Haskell 98 语言的函数编程范型。 Phillip J. Eby 在 PYCON 05 上给出了关于泛型函数的演示文稿,并且制作了 幻灯片。 可爱的 Python: 多分派(developerWorks,2003 年 3 月)为 OOP 到泛型函数的泛化提供了一个稳固的框架(这个泛化框架也是 R 功能的一个超集)。 在 developerWorks Linux 专区 中可以找到为 Linux 开发人员准备的更多参考资料。 获得产品和技术 订购免费的 SEK for Linux,这套 DVD(两张)包含来自

22、DB2? 、Lotus? 、Rational? 、Tivoli? 和 WebSphere? 的最新 IBM 试用版 Linux 软件。 使用 IBM 试用软件 构建您的下一个 Linux 开发项目,这些软件可以从 developerWorks 直接下载。 讨论 通过参与 developerWorks blog 加入 developerWorks 社区。 关于作者 对于 David Mertz 来说,整个世界就是一个舞台;他的职业就是在舞台边上为别人提供指导。关于他的生活经历的更多情况,请访问他的 个人 Web 页面。他从 2000 年开始编写 developerWorks 专栏 Charming Python 和 XML Matters。他还撰写了 Text Processing in Python 一书。您可以通过 mertzgnosis.cx 与 David 联系。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报