第 2 章 工具
“好了,好了,我的好伙计,就这么办吧。我们在这房子里共同生活了好几年,如果再蹲在同一座牢房里就更有意思了。华生,我跟你说实话。我一直有个想法:我要是当罪犯,一定是超一流的。这是我在这方面难得的一次机会。看这儿!”他从抽屉里拿出一个整洁的皮制小袋,打开来亮出里面几件闪亮的工具。“这是最新最好的盗窃工具,镀镍的撬棒,镶着金刚石的玻璃刀,万能钥匙,以及对付现代文明所需要的各种新玩意儿。我这儿还有在黑暗中使用的灯。一切都准备好了。你有走路不出声的鞋吗?”
— 柯南·道尔《查尔斯·密尔沃顿》
那么我们面对这么多工具应该如何选择呢?我们认为主要的准则有三点:一是统计计算功能齐全,二是统计元素易于控制,三是图形类型丰富多样。
2.1 选择作图工具
在人们通常的观念中,图形往往代表着简单,然而直观与简单是两个不同的概念,图形的首要作用的确是直观展示信息,然而这里的信息未必是简单的。一幅优秀的统计图形背后也许隐藏着重要的统计量,而统计量是统计图形的最关键构成因素。
我们通常见到的所谓统计图形也许只是 Microsoft Office Excel 的产物,事实上,Excel 的图形总体看来只有三种,第一种是表现绝对数值大小,如条形图、柱形图、折线图等,第二种是表现比例,如饼图,第三种则是表示二维平面上的变量关系,如 X-Y 散点图;从更广泛的意义上来说,Excel 展示的几乎都是原始数据,基于数据的统计推断的意味比较淡薄。而统计学的核心研究对象是什么?答案应该是分布(Distribution),注意分布不仅包含一元变量的分布,而且更重要的是多元变量的分布,诸如”均值 “、”方差”、“相关”和”概率”等概念都可以归为分布的范畴。无论 Excel 的图形如何搭配色彩、怎样变得立体化,都跳不出上面三种类型的限制,而且不能全面妥善表达统计学的要义。可能正是因为这样的原因,统计图形界内的一位大家 Leland Wilkinson 才说给统计刊物投稿时永远不要用 Excel 作图。本书所要介绍的 R 语言能表达的统计量种类极其丰富,甚至可以毫不夸张地说,任何理论上可以计算出来的统计量都能在 R 中很方便地以图形的方式表达出来。
除了统计量之外,我们也应对图形本身的组成元素给予足够的重视,这些元素包括点、线(直线、曲线、线段和箭头等)、多边形、文本、图例、颜色等,往往这部分工作都由常见的统计软件替我们做了—我们不必自己设计点、线等基本元素,但同时也就意味着我们几乎无法灵活运用这些元素(比如在图中添加点、线、文本标注等)。表面上看似计算机软件给我们省了不少麻烦,实际上,这种限制的弊端要大于那一点微不足道的好处。我们在实际工作中遇到不少这样的例子,比如往散点图中添加若干条不同的回归直线(根据某自变量不同分类的回归、或者是不同分位数的分位回归 (Koenker 2023) 直线等)、在图中添加箭头或者文本标注甚至添加包含希腊字母、微积分符号、上下标的数学公式,等等,不一而足,对于这些简单问题,用传统的(商业)统计软件恐怕太难解决,原因就在于,它们让计算机替代了太多本可以由用户完成的工作。当然,总体来说,自动化是好事,但如果为了自动化而大幅度牺牲可定制性,则可能得不偿失了。
相比之下,R 语言汇集统计计算与统计图示两种功能于一身,灵活的面向对象(Object-Oriented,OO)编程方式让我们可以很方便地控制图形输出,从而制作出既精美又专业的统计图形。我们说图形并不意味着简单,指的是统计图形的构造可以很细致入微(包括统计量的选定和图形元素的设计),而不是指 R 作图的过程或程序很复杂。同样,如果我们总是需要在图形细节上耗费大量的精力,那么 R 也将不是一个好工具。在后面第 3 、4 章和附录 B 中,我们会介绍若干作图的细节问题,读者需要斟酌美观与效率之间的平衡,避免陷入细节泥潭;实际上 ggplot2 是一个可以大幅减少细节设置的图形系统,我们将会在第 5.1 小节中详细介绍。
要想真正精通统计图形,则应首先要练好基本功(例如对图形基础元素或构造作深入的了解),然后在此基础上通过各种抽象、提炼和认识最终上升到理性认识的境界(自如地表达统计量)。
2.2 R 语言简介
R(R Core Team 2024) 是一款优秀的统计软件,同时也是一门统计计算与作图的语言,它最初由奥克兰大学统计学系的 Ross Ihaka 和 Robert Gentleman 编写 (Ihaka and Gentleman 1996);自 1997 年起 R 开始由一个核心团队(R Core Team)开发,这个团队的成员大部分来自大学机构(统计及相关院系), 包括牛津大学、华盛顿大学、威斯康星大学、爱荷华大学、奥克兰大学等,除了这些作者之外,R 还拥有一大批贡献者(来自哈佛大学、加州大学洛杉矶分校、麻省理工大学等),他们为 R 编写代码、修正程序缺陷和撰写文档。迄今为止,R 中的程序包(Package)已经是数以千计,各种统计前沿理论方法的相应计算机程序都会在短时间内以软件包的形式得以实现,这种速度是其它统计软件无法比拟的。除此之外,R 还有一个重要的特点,那就是它是自由、开源的!由于 R 背后的强大技术支持力量和它在统计理论及应用上的优势,加上目前国内对 R 的了解相对较少,我们期望能够通过本书引进并推动它在国内的广泛使用。
R 的功能概括起来可以分为两方面,一是统计计算(Statistical Computation),二是统计图示(Graphics);一般而言,图形往往比较直观而且相对简易,因此本书从 R 图形入手,以期给初学者提供一份好的 R 作图入门学习资料。
安装好 R 之后,Windows 用户可以从程序快捷方式直接启动 R,Linux 用户则可从终端敲入命令 R
启动;在启动 R 时,屏幕上会显示类似如下信息,告诉我们 R 是由很多贡献者一起协作编写的自由软件,用户可以在一定许可和条件(GPL)下重新发布它:
## R version 4.4.0 (2024-04-24) -- "Puppy Cup"
## Copyright (C) 2024 The R Foundation for Statistical Computing
## Platform: aarch64-apple-darwin20
##
## R is free software and comes with ABSOLUTELY NO WARRANTY.
## You are welcome to redistribute it under certain conditions.
## Type 'license()' or 'licence()' for distribution details.
##
## Natural language support but running in an English locale
##
## R is a collaborative project with many contributors.
## Type 'contributors()' for more information and
## 'citation()' on how to cite R or R packages in publications.
##
## Type 'demo()' for some demos, 'help()' for on-line help, or
## 'help.start()' for an HTML browser interface to help.
从技术上来讲,R 是一套用于统计计算和图示的综合系统,它由一个语言系统(R 语言)和运行环境构成,后者包括图形、调试器(Debugger)、对某些系统函数的调用和运行脚本文件的能力。R 的设计原型是基于两种已有的语言:S 语言 (Becker, Chambers, and Wilks 1988) 以及 Sussman 的 Scheme,因此它在外观上很像 S,而背后的执行方式和语义是来自 Scheme。
R 的核心是一种解释性计算机语言,大部分用户可见的函数都是用 R 语言编写的,而用户也可以调用 C、C++ 或者 FORTRAN 程序以提高运算效率。正式发行的 R 版本中默认包括了 base(R 基础包)、stats(统计函数包)、graphics(图形包)、grDevices(图形设备包)、datasets(数据集包)等基础程序包,其中包含了大量的统计模型函数,如:线性模型/广义线性模型、非线性回归模型、时间序列分析、经典的参数/非参数检验、聚类和光滑方法等,还有大批灵活的作图程序。此外,附加程序包(add-on packages)中也提供了各式各样的程序用于特殊的统计学方法,但这些附加包都必须先安装到 R 的系统中才能够使用 (Hornik 2018)。
本书不会过多涉及到附加包,所介绍图形主要基于 R 自身的 graphics 包,当然也不可避免会使用 base 和 grDevices 等基础包中的函数,这些基础包一般不用特别加载,R 在启动的时候会自动加载进来;在第 4 章中会使用一些附加包介绍特殊的统计数据和统计方法、模型涉及到的图形,例如分类数据(Categorical Data)会提到 vcd 包,生存分析会用到 survival 包。当我们需要调用附加包时,可以使用 library()
函数,例如加载 MSG 包:
## [1] ".GlobalEnv" "package:formatR" "package:stats"
## [4] "package:graphics" "package:grDevices" "package:utils"
## [7] "package:datasets" "package:methods" "Autoloads"
## [10] "package:base"
R 的官方网站 https://www.R-project.org 中对 R 有详细介绍,我们也可以从它在世界各地的镜像(CRAN:https://CRAN.R-project.org,全称 Comprehensive R Archive Network)下载 R 的安装程序和附加包,通常我们可以在 R 中用函数 install.packages()
安装附加包,Windows 用户也可以从菜单中点击安装:Packages \(\Rightarrow\) Install Package(s)),注意,附加包都是从镜像上下载的,因此安装时要保证网络连接正常;当然我们也可以先将程序包下载到本地计算机上然后安装。
可能令读者感到不便的一点是,R 不像别的统计软件那样有图形用户界面(GUI,即 Graphical User Interface,后面附录 C 会详细介绍),因此它不会显得很傻瓜,它的界面常常是命令行界面(CLI,即 Command Line Interface),即:输入一些代码,R 就会输出相应的运算结果或其它输出结果。因此,在正式使用 R 之前,我们有必要大致了解一下 R 的运作方式。
计算机科学家 Nikiklaus Wirth 曾经提出,程序语言的经典构成是“数据结构+算法”:数据结构是程序要处理的对象,算法则是程序的灵魂。R 也不例外,它有自己独特的数据结构,这些数据结构尤其适应统计分析的需要,它们包括:向量(vector)、矩阵(matrix)、数据框(data frame)、列表(list)、数组(array)、因子(factor)和时间序列(ts)等。当然,数值、文本和逻辑数据都可以在 R 中灵活使用,附录 A.1 中给出了一些简单的数据操作例子,请读者通过阅读该章熟悉 R 的数据对象;至于算法,我们暂时可以不去过多了解,因为 R 中已经包含了大量设计好的函数,对于一般的用户来讲都不必自行设计算法,除非有特殊或者自定义的算法,那么也可以根据 R 的语法规则编写程序代码。
对 R 的运作方式粗略了解之后,我们再回头看看 GUI。一个软件的菜单、按钮、对话框等 GUI 组件不可能无限增多,否则软件会变得无比庞大臃肿,而繁杂的操作顺序的难度将很可能会超过使用程序命令行的难度,而且非开源的 GUI 隐藏了计算原理,除了程序的原始编写者,无人知道输出结果究竟是以怎样的方式计算出来。随着统计学的发展,各种新方法、模型必将不断涌现,我们现在不妨试想未来的统计软件用户界面将会变成什么样子。看看 R 的发展,可以体会到这种思路:菜单不可能无限增加,但是程序、函数都是可以无限增加的—道理很简单,因为它们不受计算机屏幕的限制。迄今为止,R 的仓库(repository)中附加包的数量已经超过 14800 个,这还只是 CRAN 上的仓库,不算另外两个站点 Bioconductor、 R-Forge 和 Omegahat,以及 Github 上处于开发状态的。在这样的大仓库中,我们可以找到最前沿的统计理论方法的实现,所需做的仅仅就是下载一个通常在几十 K 到几百 K 的一个附加包。值得注意的是,R 的主安装程序大小约为 70 M。相比之下,SPSS 的安装程序已达六七百 M,而 SAS 的基本安装程序也有数百 M,从程序大小角度便可知 R 语言的精炼。
关于 R 更深入的介绍,请参考官方发行的若干手册和网站上的大量学习材料,如官方的 7 本手册(均可从 R 的安装目录或者网站 https://cran.r-project.org/manuals.html 上找到):
- An Introduction to R (R-intro)
- R Data Import/Export (R-data)
- R Installation and Administration (R-admin)
- Writing R Extensions (R-exts)
- R Internals (R-ints)
- The R Language Definition (R-lang)
其中 R-intro 手册附录 A 中给了一个很好的代码入门演示,推荐读者通过这个演示初步熟悉 R 语言;其它手册都比较偏重 R 的底层介绍,不适合对计算机程序没有深入了解的初学者阅读;另外还有 Emmanuel Paradis 编写的《R for Beginners》也是较好的入门教材(已由本书作者及其他几位合作者翻译为中文,可从统计之都 https://cosx.org 获得);丁国徽也已将几本官方手册翻译为中文,R-intro 的中文翻译文档可以在官方网站上下载。鉴于目前 R 的中文资料并不多,我们也推荐 R 的初学者和爱好者通过访问统计之都网站及其 R 语言版块 https://d.cosx.org/t/r 共同学习、讨论和研究。
最后需要特别指出的是,R 拥有一套完善而便利的帮助系统,这对于初学者也是很好的资源。若已知函数名称,我们可以简单用问号 ?
来获取该函数的帮助,比如查询计算均值的函数帮助,只需要在命令行中敲入 ?mean
,则会弹出一个窗口显示该函数的详细说明。大多数情况下 ?
等价于 help()
函数;但有少数特殊的函数在查询其帮助时需要在函数名上加引号,比如 ?+
就查不到加法的帮助信息,而 ?'+'
或者 help('+')
则可顺利查到加法帮助信息,类似的还有 if
、for
等。
一般来说,帮助窗口会显示一个函数所属的包、用途、用法、参数说明、返回值、参考文献、相关函数以及示例,这些信息是相当丰富的。当然,更多情况下是我们并不知道函数名称是什么,此时也可以使用搜索功能,即函数 help.search()
(几乎等价于双问号 ??
)。例如我们想知道方差分析的函数名称,则可输入命令 help.search('analysis of variance')
,弹出的信息窗口会显示与搜索关键词相关的所有函数,如 aov()
等。
知道名称以后,接下来我们就可以通过前面讲到的 “?
” 来查询具体函数的帮助信息。函数 help.start()
可以打开一个网页浏览器用来浏览帮助。如果这些帮助功能还不够用,例如有时候需要的函数在已经安装的包中找不到,那么可以到官方网站上搜索:https://www.r-project.org/search.html,网站上的邮件列表导航(Mailing List Archives)也是很有用的资源,其中有大批统计相关学科的著名教授以及世界各地的统计研究者和 R 爱好者在那里用邮件的方式公开回答各种关于 R 的问题。
我们在此如此强调帮助系统,目的在于告诉读者,要想学好 R 语言,除了阅读相关书籍资料,也应该自己多多动手利用信息资源解决自己的问题。
2.3 安装 R 语言
读者在进入下一章学习之前,可以先将 R 安装在自己的计算机上,以便阅读时可以随时打开 R 进行实际操作。R 软件可以在多种操作系统上运行,包括 Windows、Linux 以及 MacOS 等,进入官方网站主页会发现中间有 Download 一项,点击进入便可以看到世界各地的 R 镜像,任意选择一个进入,比如选择清华大学 https://mirrors.tuna.tsinghua.edu.cn/CRAN/,我们就会看到 R 安装程序和源代码的下载页面,此时只需要根据自己的操作系统选择相应的链接进入下载即可。例如 Windows 用户应该选择链接 Windows 进入,然后下载基础安装包(base),其中 R-*.*.*-win.exe 字样(*.*.* 表示版本号,例如 2.12.2)的链接便是 Windows 安装程序;Linux 用户则可根据具体的系统如 RedHat、Ubuntu 等选择对应的链接,既可以用现成的二进制包,也可以自行下载源代码编译安装,但要注意如果从源代码编译,则需要自己解决依赖问题,这些依赖包如果不存在,那么运行 ./configure
的时候会给出提示。注意 R 语言一直在开发更新中,通常每隔三个月会发布一次新版本,请读者务必注意检查自己的 R 是否为最新版本,这一点尤其 Windows 用户容易忽略,据作者本人的经验,保持 R 软件跟上最新版本通常利大于弊。
由于 R 是完全开放源代码的,所以我们可以自由修改代码以构建符合自己需要的程序,但是一方面这需要一定的其它程序语言技能(典型的如 C 语言),因为 R 的很多基础函数都是用 C 写的;另一方面,对于绝大多数用户,其实没有必要对基础包进行修改——既然 R 是开源软件,其代码必然受到很多用户监视,这样就会最大程度减少程序错误、优化程序代码,而且我们也可以在 R 里面自定义函数,这些函数可以保存起来,以后同样还能继续使用,或者自行编写 R 包。对比起来,现今的商业统计软件都将源代码和计算过程封闭在用户完全不知道的黑匣子中,而在用户界面上花大量的功夫,这对统计来说,毫无疑问并非长久之计。我们相信,随着读者对 R 的深入了解,一定能体会到这个软件的真正强大之处。
下面我们以一个例子开始 R 图形之旅,见图 2.1 ,它是基于 ggplot2 包的拿破仑远征图,可以说是 R 画图灵活性的一个典型代表。
troops <- read.table(system.file("extdata", "troops.txt", package = "MSG"), header = TRUE)
cities <- read.table(system.file("extdata", "cities.txt", package = "MSG"), header = TRUE)
library(ggplot2)
p <- ggplot(cities, aes(x = long, y = lat)) # 框架
p <- p + geom_path(aes(linewidth = survivors, colour = direction, group = group),
data = troops, lineend = "round") # 军队路线
p <- p + geom_point() # 城市点
p <- p + geom_text(aes(label = city), hjust = 0, vjust = 1, size = 2.5) # 城市名称
p <- p + scale_colour_manual(values = c("grey50", "red")) +
scale_size(range = c(1, 10)) +
theme(legend.position = "none") +
xlim(24, 39) # 细节调整工作
print(p) # 打印全图