2.1 R包
你并不是一个人在战斗,很多人都在编写自己的R函数。许多大学教授、程序员和统计学家都利用R设计了可以帮助人们分析数据的工具。他们还将这些工具提供给大家无偿使用。要想使用这些工具,只需下载即可。它们是打包的一些函数和对象。附录B详细介绍了如何下载和更新R包,这里我们介绍一些基本操作。
我们将要用到的qplot函数可以快速可视化数据。qplot来自R中的ggplot2包,后者是一个广受欢迎的可视化包。在使用qplot或者ggplot2包中的任何一个函数之前,你需要先下载并安装ggplot2包。
2.1.1 install.packages
所有R包都可以从R的官方网站https://cran.r-project.org下载。但是,下载R包并不需要直接访问这个网站,我们可以在R的命令行界面内下载R包。其步骤如下。
(1) 打开RStudio。
(2) 确保计算机已经连接到互联网。
(3) 在命令行窗口内运行命令install.packages("ggplot2")。
仅此三步而已。R会自动命令计算机连接到该网站,下载并安装ggplot2包。现在可以使用ggplot2这个包了。如果还想安装其他包,只需将上面的命令代码中的ggplot2改为相应的R包名称即可。
2.1.2 library
安装完包之后,该包内的所有函数并不是立即可用的:刚才的步骤只是将R包安装在计算机的硬盘上。当你需要使用该R包时,还需要在R会话中利用命令library("ggplot2")加载该包。如果想加载其他包,只需要将该命令中的ggplot2替换为相应的R包名称即可。
让我们了解一下这个加载命令的含义。在运行该命令之前,首先命令R显示qplot函数的源代码。R找不到qplot,因为它是ggplot2包中的函数,而我们还没有加载ggplot2包。
qplot ## Error: object 'qplot' not found
现在通过library命令加载ggplot2包。
library("ggplot2")
如果事先已经通过install.packages命令安装好这个包,那么加载之后就可以顺利使用这个包中的函数了。你可能发现加载该R包之后并没有任何信息显示。不要紧,加载包时,没有信息显示就代表一切正常。相反,如果在加载时有信息显示出来,也不用着急,因为有时候ggplot2会在加载时显示一些有用的启动信息。只要没有看见类似“Error”这样的信息,就说明操作没有问题。
现在再次尝试在命令行中键入qplot, R便会展示一长段代码(因为qplot函数的源代码很长)。
qplot ##(很长一段代码,这里不展示)
附录B中有关于获取和使用R包的更为详细的介绍。如果你还不熟悉R的包系统,那么建议你先去读一读这个附录。关键的一点是,每个包都只需要安装一次,但是在每个新的R会话中,使用这个包之前都应该先用library命令加载一次,因为每次关闭RStudio时R都会把所有已经加载的包从当前的R会话中卸载。现在已加载qplot,让我们试着用用它。qplot的意思是“快速绘图”(quick plot)。如果把两个长度相同的数值向量交给qplot函数,它就会绘制出一幅散点图。qplot将第一个向量作为一组x值,而将第二个向量作为一组y值。RStudio窗口右下角面板的Plots选项卡中会显示这个散点图。
下面这段绘图代码的结果如图2-1所示。到现在为止,我们一直都在用:运算符生成数值序列,但使用c函数也可以创建数值向量。将所有你想放进这个向量的数值放在c函数中,每个数值用逗号隔开即可。c代表连接(concatenate),也可以理解为“收集”(collect)或者“合并”(combine)。
图2-1:将两个数值向量交给qplot后将得到它们之间关系的散点图
x <- c(-1, -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1) x ## -1.0-0.8-0.6-0.4-0.2 0.0 0.2 0.4 0.6 0.8 1.0 y <- x^3 y ## -1.000-0.512-0.216-0.064-0.008 0.000 0.008 ## 0.064 0.216 0.512 1.000 qplot(x, y)
其实,你并不一定要用x和y命名这两个向量,我在这里只是为了让这个例子更加直观。从图2-1可以看出,散点图就是一些散落在平面上的点,它们在坐标轴上的位置是根据它们各自的x值和y值所确定的。凑在一起,这一对变量其实就是描述了11个点。那么R是如何将x和y中的这些值一一对应起来的呢?这要得益于R的元素方式执行,我们在第1章的图1-3中讲过这种执行。在描述两个变量之间的关系时,散点图是非常有用的工具。但我们接下来要介绍另外一种图,即直方图(histogram)。直方图用来可视化单一变量的分布情况;对于这个变量中的每一个值x,直方图会展示有多少数据点落在这个值内。
现在我们不妨看一个直方图找找感觉,看它到底有没有用。只要仅给qplot一个要绘制的向量,qplot就会画出一个直方图。接下来的一段代码将绘出图2-2左边的图(右边的图稍后再绘出)。为了保证图的一致性,我们在代码中设置了一个额外的参数binwidth = 1。
x <- c(1, 2, 2, 2, 3, 3) qplot(x, binwidth = 1)
图2-2:给qplot一个向量,qplot就会画出一个直方图
这个直方图告诉我们,当x取值区间为[1, 2)时,变量只有一个数据点落在这个区间内,因此这个区间对应的柱高为1。同理,该图也显示当取值区间为[2, 3)时,变量有三个数据点落在这个区间内,因此这个区间对应的柱高为3。区间[3, 4)对应的柱高为2,因为变量有两个数据点落在这个区间内。中括号[表示左边的数包含在区间之内,小括号)表示右边的数排除在区间之外。
让我们换个数据再画一个直方图。下面的代码所绘制的就是图2-2右边的图。对于这样一个新的变量x2来说,有五个数据点取值为1。因此,在区间x2 = [1, 2)上的柱高为5。
x2 <- c(1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4) qplot(x2, binwidth = 1)
练习
让x3成为以下向量。
x3 <- c(0, 1, 1, 2, 2, 2, 3, 3, 4)
试想一下x3的直方图会是什么样子。假设直方图中每根柱子的宽度为1,这个直方图一共会有多少根柱子?它们会分别出现在图中的哪个位置?每根柱子分别有多高呢?
思考完之后,可以用刚才的函数把x3的直方图画出来,并设置binwidth = 1,看看自己刚才想的对不对。
你可以用qplot(x3, binwidth = 1)得到x3的直方图。该直方图看起来像是一个对称金字塔。最中间的柱子高度为3,其所在的位置是在区间[2, 3)上。光看代码用处不大,你一定要自己动手把这些图画出来,看图说话。
通过直方图可以直观地展示x的哪些值出现的频率较高。较高的柱子所对应的数值要比较低柱子对应的数值出现的频率高。
那么到底如何利用直方图来检验虚拟骰子的均匀程度呢?其实,如果将骰子抛掷足够多次并跟踪记录每一次的点数,会发现某些点数出现的次数高于另外一些点数。这是因为,我们最后的点数由两个骰子的点数相加(点数和)而成,而两个骰子的某些点数和的组合方法个数要多于其他一些点数和的组合方法个数。在图2-3中可以清楚地看到这一点。
如果将一对骰子抛掷很多次,再用qplot画出点数和的直方图,那么该图会显示出每一个点数和出现的次数。点数和出现次数最多的,在直方图中对应的柱子最高。如果没有对骰子做过手脚,那么得到的直方图应该与图2-3类似。
图2-3:一共36种组合数,每个单一组合出现的频率应该是相同的。因此,考虑组合的点数和之后,某些点数和出现的频率就会高于另外一些点数和。对于没有被做过手脚的骰子来说,两个骰子的点数和应该与构成它们的点数组合数成正比
这里replicate函数就变得非常有用了。它提供了快速重复运行一段R命令的快捷方法。若要使用它,需要提供你想要重复运行的次数,还需要提供你想要重复运行的R代码。replicate会按照你设置的运行次数重复运行此段代码,并将结果存储为一个向量。
replicate(3, 1 + 1)
## 2 2 2
replicate(10, roll())
## 3 7 5 3 6 2 3 8 11 7
如果仅仅投掷10次骰子,那么所画出的直方图可能与图2-3所展示的情况有很大差别。为什么呢?因为投掷的过程中有很强的随机性,规律很难呈现出来。但是我们应该记得,在现实生活中,骰子是一种有效的随机数生成工具。长期频率这样的特征必须运行足够多的次数才会呈现出来。因此我们不妨模拟投掷10000次骰子再作图。虽说是10000次模拟,但你不用担心,qplot和replicate可以轻松搞定。图2-4便是10000次投掷之后两个骰子点数和的直方图。
rolls <- replicate(10000, roll()) qplot(rolls, binwidth = 1)
图2-4:这副骰子的投掷结果显示它们应该是均匀骰子。点数和为7的频率要高于其他点数和,并且点数和出现的频率与构成它们的点数组合个数成正比,从中间向两侧递减
直方图的结果显示这对骰子是均匀的。从长期来看,每个点数和都与构成它们的点数组合数成正比。
现在思考一下:如何在骰子上做些手脚,让模拟的结果变得有偏差呢?之前的结果之所以是无偏差的,就是因为每一种点数组合(比如(3, 4)组合)出现的频率都是等同的。如果可以设法将两个骰子出现点数为6的概率提高,那么任何包含点数6的组合出现的概率都要大于其他组合。点数组合(6, 6)的出现概率是最高的。虽然这样的做法不会直接导致点数和为12的概率要高于点数和为7的概率,但是从长期来看,它会将点数和的整体分布拉向较大值的那一边,也就是右边。
换句话说,一个均匀骰子每一个点数出现的概率应该是1/6。现在的任务是将所有非6点数的概率减小到1/8,而提高点数6的概率为3/8。
其实,只需要在sample函数中添加一个新参数就可以实现上面加权概率的目的。我不会直接告诉你这个新参数是什么,但我会为你指明方向:去sample函数的帮助页面上寻找答案。什么是帮助页面?R函数还有自己的帮助页面吗?当然有,现在我们就来学一学如何找到一个函数的帮助页面。