申峰 博士生导师 副教授 西南财经大学金融学院

邮箱:shenfeng@swufe.edu.cn



本章主要对图形的绘制进行介绍和深入探索。



5.1 初级图形



5.1.1 使用图形



加载Insurance数据集(该数据集在MASS包中)

library(MASS)
data(mtcars)


展示数据集

Insurance
##    District  Group   Age Holders Claims
## 1         1    <1l   <25     197     38
## 2         1    <1l 25-29     264     35
## 3         1    <1l 30-35     246     20
## 4         1    <1l   >35    1680    156
## 5         1 1-1.5l   <25     284     63
## 6         1 1-1.5l 25-29     536     84
## 7         1 1-1.5l 30-35     696     89
## 8         1 1-1.5l   >35    3582    400
## 9         1 1.5-2l   <25     133     19
## 10        1 1.5-2l 25-29     286     52
## 11        1 1.5-2l 30-35     355     74
## 12        1 1.5-2l   >35    1640    233
## 13        1    >2l   <25      24      4
## 14        1    >2l 25-29      71     18
## 15        1    >2l 30-35      99     19
## 16        1    >2l   >35     452     77
## 17        2    <1l   <25      85     22
## 18        2    <1l 25-29     139     19
## 19        2    <1l 30-35     151     22
## 20        2    <1l   >35     931     87
## 21        2 1-1.5l   <25     149     25
## 22        2 1-1.5l 25-29     313     51
## 23        2 1-1.5l 30-35     419     49
## 24        2 1-1.5l   >35    2443    290
## 25        2 1.5-2l   <25      66     14
## 26        2 1.5-2l 25-29     175     46
## 27        2 1.5-2l 30-35     221     39
## 28        2 1.5-2l   >35    1110    143
## 29        2    >2l   <25       9      4
## 30        2    >2l 25-29      48     15
## 31        2    >2l 30-35      72     12
## 32        2    >2l   >35     322     53
## 33        3    <1l   <25      35      5
## 34        3    <1l 25-29      73     11
## 35        3    <1l 30-35      89     10
## 36        3    <1l   >35     648     67
## 37        3 1-1.5l   <25      53     10
## 38        3 1-1.5l 25-29     155     24
## 39        3 1-1.5l 30-35     240     37
## 40        3 1-1.5l   >35    1635    187
## 41        3 1.5-2l   <25      24      8
## 42        3 1.5-2l 25-29      78     19
## 43        3 1.5-2l 30-35     121     24
## 44        3 1.5-2l   >35     692    101
## 45        3    >2l   <25       7      3
## 46        3    >2l 25-29      29      2
## 47        3    >2l 30-35      43      8
## 48        3    >2l   >35     245     37
## 49        4    <1l   <25      20      2
## 50        4    <1l 25-29      33      5
## 51        4    <1l 30-35      40      4
## 52        4    <1l   >35     316     36
## 53        4 1-1.5l   <25      31      7
## 54        4 1-1.5l 25-29      81     10
## 55        4 1-1.5l 30-35     122     22
## 56        4 1-1.5l   >35     724    102
## 57        4 1.5-2l   <25      18      5
## 58        4 1.5-2l 25-29      39      7
## 59        4 1.5-2l 30-35      68     16
## 60        4 1.5-2l   >35     344     63
## 61        4    >2l   <25       3      0
## 62        4    >2l 25-29      16      6
## 63        4    >2l 30-35      25      8
## 64        4    >2l   >35     114     33

其中,District(离散型)投保人所在街区;group(离散型)投保人汽车排量;Age(离散型)投保人年龄;Holders(连续型)投保人数量;Claims(连续型)索赔人数量;



画散点图:横轴x=Holders,纵轴y=Claims。

attach(Insurance)
plot(Holders,Claims)

detach(Insurance)

函数attach()将数据加载入内存中,detach()将数据从内存中删除。



我们还可以使用with()函数,来创建该数据的环境。

with(Insurance,
plot(Holders,Claims)
    )



思考:如果不用attach(),with()函数可以加载数据,绘制散点图吗?



在之前散点图的基础上,添加一条拟合曲线。

with(Insurance,{
plot(Holders,Claims)
abline(lm(Claims ~ Holders))}
    )

注意:在with函数中,有多个函数时,必须使用{}。函数abline()表示添加一条直线。lm(y ~ x)表示拟合的一条直线。



在之前绘图的基础上,添加标题

attach(Insurance)
plot(Holders,Claims)
abline(lm(Claims ~ Holders))
title("Regression of Claims on Holders")

注意:因为后面还要用到Insurance数据集,所以就不使用detach()函数了。



在之前基础上,将图形保存到当前工作目录中名为mygraph.pdf的PDF文件中。

pdf("mygraph.pdf")
plot(Holders,Claims)
abline(lm(Claims ~ Holders))
title("Regression of Claims on Holders")

注意:除了pdf(),还可以使用函数win.metafile()、png()、jpeg()、bmp()、tiff()、xfig()和postscript()将图形保存为其他格式。



保存为jpeg格式

jpeg("mygraph.jpeg")
plot(Holders,Claims)
abline(lm(Claims ~ Holders))
title("Regression of Claims on Holders")


5.1.2 简单例子



下表描述了病人对两种药物五个剂量水平上的响应情况。

5.1.jpg



使用以下代码输入数据:

dose <- c(20, 30, 40, 45, 60)
drugA <- c(16, 20, 27, 40, 60)
drugB <- c(15, 18, 25, 31, 40)

dose剂量,drugA药物A的响应时间,drugB药物B的响应时间



创建一幅描述药物A的剂量dose和响应时间drugA关系的图形

plot(dose, drugA, type = "b")

注意:plot(x, y, type=“b”) 表示将x置于横轴,将y置于纵轴,绘制点集(x,y)。选项type=“b”表示将点连成线。



5.1.3 图形参数



我们可以通过修改图形参数来自定义一幅图形的多个特征(字体、颜色、坐标轴、标题)。修改图形参数的一种方法是通过函数par()来指定这些选项。

假设你想修改上图,使用实心三角而不是空心圆圈作为点的符号,并且想用虚线代替实线连接这些点。

可以使用以下代码完成修改:

plot(dose, drugA, type = "b", lty = 2, pch = 17)



5.1.3 符号和线条



可以使用图形参数来指定绘图时使用的符号和线条类型。相关参数如下表所示。

5.2.jpg



选项pch=用于指定绘制点时使用的符号。pch取值如下图所示。

5.3.jpg



选项lty=用于指定想要的线条类型。lty取值如下图所示。

5.4.jpg



绘制一幅图形,其线条类型为点线,宽度为默认宽度的3倍,点的符号为实心正方形,大小为默认符号大小的2倍。可以通过代码设置:

plot(dose, drugA, type = "b", lty = 3, lwd = 3, pch = 15, cex = 2)



5.2 中级图形



该部分主要进行数据分析的可视化探索。

针对-连续型数据-介绍:直方图、累积分布图、箱形图。

针对-离散型数据-介绍:条形图、点阵图、饼图。



5.2.1 直方图



直方图将连续型数据分为几个等间距的组,并以矩形的高低来显示相应组中所含数据的频数或频率大小。



绘制Isurance数据集中Claims变量的直方图

hist(Claims,main="Histogram of Freq of Insurance$Claims")



hist()函数中,参数freg=TRUE(默认)表示频数绘制;freq=FALSE表示根据概率密度绘制图形。

hist(Claims,freq=FALSE,main="Histogram of Density of Insurance$Claims")

注意:此时矩阵高度代表各组的概率密度,同时各组矩阵面积之和为1。



参数density可为各组矩阵添加阴影,取值越大,阴影部分越深。

当density=10时

hist(Claims,freq=FALSE,density=10,main="Histogram of Density of Insurance$Claims")


当density=20时

hist(Claims,freq=FALSE,density=20,main="Histogram of Density of Insurance$Claims")



在之前直方图基础上,同时绘制概率密度曲线

hist(Claims,freq=FALSE,density=20,main="Histogram of Density of Insurance$Claims")
lines(density(Insurance$Claims))

注意:通过密度曲线,可以精确看到每一取值所对应的概率密度值,同时密度曲线与x轴围成的面积为1。



参数breaks用于控制组的数量。当breaks=20时

hist(Insurance$Claims,breaks=20, main="Histogram of Insurance$Claims with 20 bars")



参数labels = TRUE可显示每组频数。参数col可更换矩形内颜色。参数border可更换边框颜色。

hist(Insurance$Claims,breaks=20,labels = TRUE, col="blue",border="red", main="Histogram of Insurance$Claims with 20 bars" )



在绘图的同时,还可使用str()函数获取该直方图的相应输出值。

str(hist(Insurance$Claims,breaks=20,labels = TRUE, col="black",border="white",main="Histogram of Insurance$Claims with 20 bars"))

## List of 6
##  $ breaks  : num [1:21] 0 20 40 60 80 100 120 140 160 180 ...
##  $ counts  : int [1:20] 30 13 5 5 3 2 0 2 0 1 ...
##  $ density : num [1:20] 0.02344 0.01016 0.00391 0.00391 0.00234 ...
##  $ mids    : num [1:20] 10 30 50 70 90 110 130 150 170 190 ...
##  $ xname   : chr "Insurance$Claims"
##  $ equidist: logi TRUE
##  - attr(*, "class")= chr "histogram"

注意:以上6项输出结果,分别列示出各组边界值(breaks)、频数(counts)、概率密度(density), 中间值(mids)、绘图对象名(Name),以及是否为等距分组(equidist)。在绘制直方图时,可以将如上结果一并输出,来辅助理解图示信息。



5.2.2 累计分布图



累积分布图中每个点(x,y)的含义为:共有y(百分数)的数据小于或等于该x值。数据中x最大值所对应的y值为1。



使用Himsc软件包中的Ecd()函数来绘制累积分布图, 首次使用需要首先下载此包。

library(lattice)
library(survival)
library(Formula)
library(ggplot2)
library(Hmisc)
## 
## Attaching package: 'Hmisc'
## The following objects are masked from 'package:base':
## 
##     format.pval, units


以Claims变量为例,用Ecdf()函数来绘制累积分布图

Ecdf(Insurance$Claims,xlab="Claims",main="Cumulative Distribution of Claims")



在一张图中,绘制各个年龄段Age的索赔人数claims的累积分布图。

观察一下这两个变量

Insurance[,c("Age","Claims")]
##      Age Claims
## 1    <25     38
## 2  25-29     35
## 3  30-35     20
## 4    >35    156
## 5    <25     63
## 6  25-29     84
## 7  30-35     89
## 8    >35    400
## 9    <25     19
## 10 25-29     52
## 11 30-35     74
## 12   >35    233
## 13   <25      4
## 14 25-29     18
## 15 30-35     19
## 16   >35     77
## 17   <25     22
## 18 25-29     19
## 19 30-35     22
## 20   >35     87
## 21   <25     25
## 22 25-29     51
## 23 30-35     49
## 24   >35    290
## 25   <25     14
## 26 25-29     46
## 27 30-35     39
## 28   >35    143
## 29   <25      4
## 30 25-29     15
## 31 30-35     12
## 32   >35     53
## 33   <25      5
## 34 25-29     11
## 35 30-35     10
## 36   >35     67
## 37   <25     10
## 38 25-29     24
## 39 30-35     37
## 40   >35    187
## 41   <25      8
## 42 25-29     19
## 43 30-35     24
## 44   >35    101
## 45   <25      3
## 46 25-29      2
## 47 30-35      8
## 48   >35     37
## 49   <25      2
## 50 25-29      5
## 51 30-35      4
## 52   >35     36
## 53   <25      7
## 54 25-29     10
## 55 30-35     22
## 56   >35    102
## 57   <25      5
## 58 25-29      7
## 59 30-35     16
## 60   >35     63
## 61   <25      0
## 62 25-29      6
## 63 30-35      8
## 64   >35     33


先按照书上的程序实现,创建一个数据集data_plot

data_plot=with(Insurance,
      rbind(data.frame(var1=Claims[Age=="<25"],var2="<25"),
            data.frame(var1=Claims[Age=="25-29"],var2="25-29"),
            data.frame(var1=Claims[Age=="30-35"],var2="30-35"),
            data.frame(var1=Claims[Age==">35"],var2=">35") )
       )

函数with()用于形成Insurance数据集的环境,这样在使用该数据集中的各变量时,就不需在每次都采用Insurance$Claims方式指定数据集,只用输入Claims,简化了代码编写。

函数data.frame()则用于构造新的数据集。

函数rbind()函数可将各部分数据按行连接起来。



data_plot
##    var1  var2
## 1    38   <25
## 2    63   <25
## 3    19   <25
## 4     4   <25
## 5    22   <25
## 6    25   <25
## 7    14   <25
## 8     4   <25
## 9     5   <25
## 10   10   <25
## 11    8   <25
## 12    3   <25
## 13    2   <25
## 14    7   <25
## 15    5   <25
## 16    0   <25
## 17   35 25-29
## 18   84 25-29
## 19   52 25-29
## 20   18 25-29
## 21   19 25-29
## 22   51 25-29
## 23   46 25-29
## 24   15 25-29
## 25   11 25-29
## 26   24 25-29
## 27   19 25-29
## 28    2 25-29
## 29    5 25-29
## 30   10 25-29
## 31    7 25-29
## 32    6 25-29
## 33   20 30-35
## 34   89 30-35
## 35   74 30-35
## 36   19 30-35
## 37   22 30-35
## 38   49 30-35
## 39   39 30-35
## 40   12 30-35
## 41   10 30-35
## 42   37 30-35
## 43   24 30-35
## 44    8 30-35
## 45    4 30-35
## 46   22 30-35
## 47   16 30-35
## 48    8 30-35
## 49  156   >35
## 50  400   >35
## 51  233   >35
## 52   77   >35
## 53   87   >35
## 54  290   >35
## 55  143   >35
## 56   53   >35
## 57   67   >35
## 58  187   >35
## 59  101   >35
## 60   37   >35
## 61   36   >35
## 62  102   >35
## 63   63   >35
## 64   33   >35


用Ecdf()函数绘制按Age分组的累积分布图

Ecdf(data_plot$var1,lty=2,group=data_plot$var2,label.curves=TRUE,
      xlab="Claims", main="Cumulative Distribution of Claims by Age")

其中用到lty、group、label.curves这3个参数。lty用于设定线条的类型。group用于设置分组变量,例如这里我们将Age作为分组变量;

label. curves=TRUE(默认)用于标出按分组变量划分的各曲线组名。



书上的程序,经过精简,可修改为

Ecdf(Insurance$Claims,lty=2,group=Insurance$Age,label.curves=TRUE,
      xlab="Claims", main="Cumulative Distribution of Claims by Age")



在之前图形的基础上,加入1条实线绘制的Claims的总体累积分布图。

Ecdf(data_plot$var1,lty=2,group=data_plot$var2,label.curves=1:4,
      xlab="Claims", main="Cumulative Distribution of Claims by Age")
Ecdf(Insurance$Claims,add=TRUE)

其中要用到add参数,add参数表示是否在上一输出图形中添加图像。



5.2.3 箱形图


箱线图(又称盒须图)通过绘制连续型变量的五个特征数,即最小值、下四分位数(第25百分位数)、中位数(第50百分位数)、上四分位数(第75百分位数)以及最大值,描述了连续型变量的分布。

在默认情况下,两条须的延伸极限不会超过盒型各端加1.5倍四分位距的范围。此范围以外的值将以点来表示。箱线图能够显示出可能为离群点(范围±1.5*IQR以外的值, IQR表示四分位距,即上 四分位数与下四分位数的差值)的观测。


使用boxplot()函数,绘制数据集中Claims的线形图

Claims_bp=boxplot(Insurance$Claims,main="Distribution of Claims")


观察箱型图中的各种详细信息

Claims_bp
## $stats
##      [,1]
## [1,]    0
## [2,]    9
## [3,]   22
## [4,]   58
## [5,]  102
## attr(,"class")
##           
## "integer" 
## 
## $n
## [1] 64
## 
## $conf
##         [,1]
## [1,] 12.3225
## [2,] 31.6775
## 
## $out
## [1] 156 400 233 290 143 187
## 
## $group
## [1] 1 1 1 1 1 1
## 
## $names
## [1] ""

其中Claims_bp$stats表示claims变量的各分位点情况

Claims_bp$stats
##      [,1]
## [1,]    0
## [2,]    9
## [3,]   22
## [4,]   58
## [5,]  102
## attr(,"class")
##           
## "integer"

其中,第1个数值0代表下须位。中间的三个值,即第2、3、4对应的数值9、22、58分别表示该变量的下四分位数、中位数、上四分位数,这三个点确定了箱型图中“箱子”的大小,而箱子中的黑线位置即为中位数,且上四分位数与下四分位数之差58-9=49,被称为四分位距IQR;超出上须线和下须线的点作为异常值被单独标出。58+1.5*49=131.5。我们查看一下claims变量的数据发现,低于131.5的数据是102,高于131.5的数据是143。

我们从上图可以看到该数据显现右偏趋势,同时将6个极大异常值点被标注。


在之前的基础上,用星号标记出均值的位置

Claims_bp=boxplot(Claims,main="Distribution of Claims")
points(x=1,y=mean(Claims),pch=8)

函数mean()表示求平均值


在之前的基础上,标记出各重要点的具体取值来进一步完善箱线图。

将离群点以矩阵方式保存:

Claims_points=as.matrix(Claims[which(Insurance$Claims>102)],6,1)

6表示6行,1表示1列。

将需要标注的12点给汇聚在Claims_text新数据集中

Claims_text=rbind(Claims_bp$stats,mean(Claims),Claims_points)
Claims_bp=boxplot(Insurance$Claims,main="Distribution of Claims")
points(x=1,y=mean(Insurance$Claims),pch=8)
for(i in 1:length(Claims_text))
text(x=1.3,y=Claims_text[i,],labels=Claims_text[i,])


当要绘制多个箱型图时,我们需要输出横向的箱线图。

( 我们先来看默认的纵向的箱线图。

data_plot=with(Insurance,
      rbind(data.frame(var1=Claims[Age=="<25"],var2="<25"),
            data.frame(var1=Claims[Age=="25-29"],var2="25-29"),
            data.frame(var1=Claims[Age=="30-35"],var2="30-35"),
            data.frame(var1=Claims[Age==">35"],var2=">35") )
       )

boxplot(var1~var2,data=data_plot,
        main="Distribution of Claims by Age",xlab="Age",ylab="Claims")


在之前的基础上,我们输出横向的箱线图。

boxplot(var1~var2,data=data_plot,horizontal=TRUE,
        main="Distribution of Claims by Age",xlab="Claims",ylab="Age")

其中参数horizontal=TRUE时,表示输出横向箱线图。


5.2.4 条形图


条形图与柱状图类似。柱状图适用于连续型数据,条形图适用于离散型变量。


下面我们研究Isurance数据集中离散变量Age的分布情况,并以Claims作为各年龄组的取值频率。(x轴为Age,y轴为Claims)


Claims_Age = with(Insurance,
                c( sum(Claims[Age=="<25"]), sum(Claims[Age=="25-29"]),
                   sum(Claims[Age=="30-35"]), sum(Claims[Age==">35"]) ) )
attach(Insurance)
## The following objects are masked from Insurance (pos = 8):
## 
##     Age, Claims, District, Group, Holders
Claims_Age1 = c( sum(Claims[which(Age=="<25")]), sum(Claims[which(Age=="25-29")]),
                   sum(Claims[which(Age=="30-35")]), sum(Claims[which(Age==">35")]) 
                 )
barplot(Claims_Age,names.arg=c("<25","25-29","30-35",">35"),density=rep(20,4),
        main="Distribution of Age by Claims", xlab="Age", ylab="Claims")

barplot()函数中的参数names.arg表示标注x轴每个组别的名称,density表示条形图框内阴影斜线的密度,main表示图形名称。


小练习:研究Isurance数据集中离散变量Group的分布情况,并以Holders作为各年龄组的取值频率。



进一步,我们将Holders变量加入考虑,使得各年龄Age组下的Claims和Holders值同时存在于一张图中,便于比较分析。

常用的合并条形图主要有:分组条形图,堆叠条形图。分组条形图是将同一Age组别下的的Claims和Holders值并行摆放在一起,堆叠条形图是将其堆叠在一个矩形中表示。


绘制分组条形图。首先先得到向量Holders_Age来储存四个年龄组下的Holders值,并将Claims_Age和Holders_Age合并为data_bar矩阵用于绘图。

Holders_Age = with(Insurance,
                c( sum(Holders[which(Age=="<25")]), sum(Holders[which(Age=="25-29")]),
                   sum(Holders[which(Age=="30-35")]), sum(Holders[which(Age==">35")]) ) )
Holders_Age
## [1]  1138  2336  3007 16878

将Claims_Age和Holders_Age合并为data_bar矩阵。

data_bar = rbind(Claims_Age,Holders_Age)
data_bar
##             [,1] [,2] [,3]  [,4]
## Claims_Age   229  404  453  2065
## Holders_Age 1138 2336 3007 16878

绘制分组条形图

barplot(data_bar, names.arg=c("<25","25-29","30-35",">35"),beside=TRUE,
        main="Age Distribution by Claims and Holders",
        xlab="Age", ylab="Claims&Holders", col=c("black","darkgrey"))

其中参数beside=TRUE时,表示为分组条形图。beside=FALSE时(默认),表示为堆叠条形图。


barplot(data_bar, names.arg=c("<25","25-29","30-35",">35"),beside=TRUE,
        main="Age Distribution by Claims and Holders",
        xlab="Age", ylab="Claims&Holders", col=c("black","darkgrey"))
legend(x="topleft",rownames(data_bar), fill = c("black","darkgrey"))

函数legend()表示给两个并列的组进行注释说明。参数x表示注释的位置,有时用x=,y=表示注释的位置。


绘制堆叠条形图

barplot(data_bar, names.arg=c("<25","25-29","30-35",">35"),beside=FALSE,
        main="Age Distribution by Claims and Holders",
       ylab="Claims&Holders", col=c("black","darkgrey"))
legend(x="topleft", rownames(data_bar), fill = c("black","darkgrey"))


小练习:分组条形图和堆叠条形图中每组的数值如何显示?



5.2.5 点阵图


点阵图的本质和条形图是一致的,也是用于呈现离散型变量各取值水平的分布情况。


我们使用在不同Age年龄组下的Claims和Holders值的矩阵data_bar来生成点阵图。

dotchart(data_bar,xlab="Claims&Holders", pch=1:2,
         main="Age Distribution by Claims and Holders")
legend(x=14000,y=16,"<25",bty="n")
legend(x=14000,y=12,"25-29",bty="n")
legend(x=14000,y=8,"30-35",bty="n")
legend(x=14000,y=4,">35",bty="n")

参数bty表示图例框是否画出,o为画出(默认),n不画出。



5.2.6 饼图



饼图是观察某个离散变量分布情况的有效图形。



以离散变量Age为例,绘制其Claims的取值情况。使用pie()函数绘制。

pie(Claims_Age,labels=c("<25","25-29","30-35",">35"),
    main="Pie Chart of Age by Claims",col=c("white","lightgray","darkgrey","black"))



有时需要展示各部分所占的比例值。那就需要先计算各部分的百分比,然后在绘图。我们以上面的为例展示。


计算各组的比例

percent = round(Claims_Age/sum(Claims_Age)*100)
percent
## [1]  7 13 14 66

round()函数表示四舍五入,sum()表示求和。


设置饼图各部分文本信息

label = paste(paste(c("<25","25-29","30-35",">35"),":"), percent,"%",sep="")

paste()函数表示连接函数,参数sep表示连接之间的间隔。


绘制带百分比信息的饼图

pie(Claims_Age,labels = label,  
    main="Pie Chart of Age by Claims",col=c("white","lightgray","darkgrey","black"))