条形图
条形图通过垂直的或水平的条形展示了类别型变量的分布(频数)。函数 barplot() 的最简单用法是:
barplot(height)
其中的 height
是一个向量或一个矩阵。
在接下来的示例中,我们将绘制一项探索类风湿性关节炎新疗法研究的结果。数据已包含在随 vcd 包分发的 Arthritis 数据框中。由于 vcd 包并没用包括在R的默认安装中,请确保在第一次使用之前先安装它:
install. packages("vcd")
注意,我们并不需要使用 vcd 包来创建条形图。我们读入它的原因是为了使用 Arthritis 数据集。但我们需要使用 vcd 包创建6.1.5节中描述的棘状图(spinogram)。
简单的条形图
library(vcd)
pdf("test1.pdf")
counts <- table(Arthritis$Improved)
par(mfrow=c(1,2))
barplot(counts,
main="Simple Bar Plot",
xlab="Improvement", ylab="Frequency")
barplot(counts,
main="Horizontal Bar Plot",
xlab="Frequency", ylab="Improvement",
horiz=TRUE)
dev.off()
horiz=TRUE
则会生成一幅水平条形图
如果标签很长怎么办?在6.1.4节中,你将看到微调标签的方法,这样它们就不会重叠了。
堆砌条形图和分组条形图
如果 height 是一个矩阵而不是一个向量,则绘图结果将是一幅堆砌条形图或分组条形图。若 beside=FALSE (默认值),则矩阵中的每一列都将生成图中的一个条形,各列中的值将给出堆砌的“子条”的高度。若 beside=TRUE ,则矩阵中的每一列都表示一个分组,各列中的值将并列而不是堆砌。
考虑治疗类型和改善情况的列联表:
> counts <- table(Arthritis$Improved, Arthritis$Treatment)
> counts
Placebo Treated
None 29 13
Some 7 7
Marked 7 21
你可以将此结果绘制为一幅堆砌条形图或一幅分组条形图(见代码清单6-2)。结果如图6-2
所示。
# 堆砌条形图
barplot(counts,
main="Stacked Bar Plot",
xlab="Treatment", ylab="Frequency",
col=c("red", "yellow","green"),
legend=rownames(counts))
# 分组条形图
barplot(counts,
main="Grouped Bar Plot",
xlab="Treatment", ylab="Frequency",
col=c("red", "yellow", "green"),
legend=rownames(counts), beside=TRUE)
在第3章中,我们讲解过格式化和放置图例的方法,以确保最好的效果。请试着重新排布图例的位置以避免它们和条形产生叠加。
# 修改一下Y轴的范围即可
pdf("test1.pdf")
counts <- table(Arthritis$Improved, Arthritis$Treatment)
counts
par(mfrow=c(1,2))
x = c(0,10,20,30,40,50,60)
# 堆砌条形图
barplot(counts,
main="Stacked Bar Plot",
xlab="Treatment", ylab="Frequency",
col=c("red", "yellow","green"),
legend=rownames(counts),
ylim = c(0, 60))
# 分组条形图
barplot(counts,
main="Grouped Bar Plot",
xlab="Treatment", ylab="Frequency",
col=c("red", "yellow", "green"),
legend=rownames(counts), beside=TRUE,
ylim = c(0, 40))
dev.off()
均值条形图
条形图并不一定要基于计数数据或频率数据。你可以使用数据整合函数并将结果传递给barplot() 函数,来创建表示均值、中位数、标准差等的条形图。代码清单6-3展示了一个示例,结果如图6-3所示。
> states <- data.frame(state.region, state.x77)
> states
state.region Population Income Illiteracy Life.Exp Murder HS.Grad Frost Area
Alabama South 3615 3624 2.1 69.05 15.1 41.3 20 50708
Alaska West 365 6315 1.5 69.31 11.3 66.7 152 566432
Arizona West 2212 4530 1.8 70.55 7.8 58.1 15 113417
Arkansas South 2110 3378 1.9 70.66 10.1 39.9 65 51945
California West 21198 5114 1.1 71.71 10.3 62.6 20 156361
Colorado West 2541 4884 0.7 72.06 6.8 63.9 166 103766
Connecticut Northeast 3100 5348 1.1 72.48 3.1 56.0 139 4862
Delaware South 579 4809 0.9 70.06 6.2 54.6 103 1982
Florida South 8277 4815 1.3 70.66 10.7 52.6 11 54090
Georgia South 4931 4091 2.0 68.54 13.9 40.6 60 58073
Hawaii West 868 4963 1.9 73.60 6.2 61.9 0 6425
Idaho West 813 4119 0.6 71.87 5.3 59.5 126 82677
Illinois North Central 11197 5107 0.9 70.14 10.3 52.6 127 55748
Indiana North Central 5313 4458 0.7 70.88 7.1 52.9 122 36097
Iowa North Central 2861 4628 0.5 72.56 2.3 59.0 140 55941
Kansas North Central 2280 4669 0.6 72.58 4.5 59.9 114 81787
Kentucky South 3387 3712 1.6 70.10 10.6 38.5 95 39650
Louisiana South 3806 3545 2.8 68.76 13.2 42.2 12 44930
Maine Northeast 1058 3694 0.7 70.39 2.7 54.7 161 30920
Maryland South 4122 5299 0.9 70.22 8.5 52.3 101 9891
Massachusetts Northeast 5814 4755 1.1 71.83 3.3 58.5 103 7826
Michigan North Central 9111 4751 0.9 70.63 11.1 52.8 125 56817
Minnesota North Central 3921 4675 0.6 72.96 2.3 57.6 160 79289
Mississippi South 2341 3098 2.4 68.09 12.5 41.0 50 47296
Missouri North Central 4767 4254 0.8 70.69 9.3 48.8 108 68995
Montana West 746 4347 0.6 70.56 5.0 59.2 155 145587
Nebraska North Central 1544 4508 0.6 72.60 2.9 59.3 139 76483
Nevada West 590 5149 0.5 69.03 11.5 65.2 188 109889
New Hampshire Northeast 812 4281 0.7 71.23 3.3 57.6 174 9027
New Jersey Northeast 7333 5237 1.1 70.93 5.2 52.5 115 7521
New Mexico West 1144 3601 2.2 70.32 9.7 55.2 120 121412
New York Northeast 18076 4903 1.4 70.55 10.9 52.7 82 47831
North Carolina South 5441 3875 1.8 69.21 11.1 38.5 80 48798
North Dakota North Central 637 5087 0.8 72.78 1.4 50.3 186 69273
Ohio North Central 10735 4561 0.8 70.82 7.4 53.2 124 40975
Oklahoma South 2715 3983 1.1 71.42 6.4 51.6 82 68782
Oregon West 2284 4660 0.6 72.13 4.2 60.0 44 96184
Pennsylvania Northeast 11860 4449 1.0 70.43 6.1 50.2 126 44966
Rhode Island Northeast 931 4558 1.3 71.90 2.4 46.4 127 1049
South Carolina South 2816 3635 2.3 67.96 11.6 37.8 65 30225
South Dakota North Central 681 4167 0.5 72.08 1.7 53.3 172 75955
Tennessee South 4173 3821 1.7 70.11 11.0 41.8 70 41328
Texas South 12237 4188 2.2 70.90 12.2 47.4 35 262134
Utah West 1203 4022 0.6 72.90 4.5 67.3 137 82096
Vermont Northeast 472 3907 0.6 71.64 5.5 57.1 168 9267
Virginia South 4981 4701 1.4 70.08 9.5 47.8 85 39780
Washington West 3559 4864 0.6 71.72 4.3 63.5 32 66570
West Virginia South 1799 3617 1.4 69.48 6.7 41.6 100 24070
Wisconsin North Central 4589 4468 0.7 72.48 3.0 54.5 149 54464
Wyoming West 376 4566 0.6 70.29 6.9 62.9 173 97203
# aggregate()函数的使用方法见第五章——数据的整合与重构
> means <- aggregate(states$Illiteracy, by=list(state.region), FUN=mean)
> means
Group.1 x
1 Northeast 1.000000
2 South 1.737500
3 North Central 0.700000
4 West 1.023077
barplot(means$x, names.arg=means$Group.1,
col = "Blue")
title("Mean Illiteracy Rate")
means$x
是包含各条形高度的向量,而添加选项 names.arg=means$Group.1
是为了展示标签。
条形图的微调
有若干种方式可以微调条形图的外观。例如,随着条数的增多,条形的标签可能会开始重叠。你可以使用参数 cex.names 来减小字号。将其指定为小于1的值可以缩小标签的大小。可选的参数 names.arg 允许你指定一个字符向量作为条形的标签名。你同样可以使用图形参数辅助调整文本间隔。
代码清单6-4给出了一个示例,输出如图6-4所示。
par(mar=c(5,8,4,2))
par(las=2)
counts <- table(Arthritis$Improved)
barplot(counts,
main="Treatment Outcome",
horiz=TRUE,
cex.names=0.8,
names.arg=c("No Improvement", "Some Improvement",
"Marked Improvement"))
par() 函数能够让你对R的默认图形做出大量修改。详情参阅第3章。
棘状图
在结束关于条形图的讨论之前,让我们再来看一种特殊的条形图,它称为棘状图(spinogram)。棘状图对堆砌条形图进行了重缩放,这样每个条形的高度均为1,每一段的高度即表示比例。棘状图可由 vcd 包中的函数 spine() 绘制。以下代码可以生成一幅简单的棘状图:
library(vcd)
attach(Arthritis)
counts <- table(Treatment, Improved)
spine(counts, main="Spinogram Example")
detach(Arthritis)
输出如图6-5所示。治疗组同安慰剂组相比,获得显著改善的患者比例明显更高。
饼图
饼图可由以下函数创建:
pie(x, labels)
其中 x 是一个非负数值向量,表示每个扇形的面积,而 labels 则是表示各扇形标签的字符型向量。
代码清单6-5给出了四个示例,结果如图6-6所示。
par(mfrow=c(2, 2))
slices <- c(10, 12,4, 16, 8)
lbls <- c("US", "UK", "Australia", "Germany", "France")
pie(slices, labels = lbls,
main="Simple Pie Chart")
pct <- round(slices/sum(slices)*100)
lbls2 <- paste(lbls, " ", pct, "%", sep="")
pie(slices, labels=lbls2, col=rainbow(length(lbls2)),
main="Pie Chart with Percentages")
library(plotrix)
pie3D(slices, labels=lbls,explode=0.1,
main="3D Pie Chart ")
mytable <- table(state.region)
lbls3 <- paste(names(mytable), "\n", mytable, sep="")
pie(mytable, labels = lbls3,
main="Pie Chart from a Table\n (with sample sizes)")
其中 x 是一个非负数值向量,表示每个扇形的面积,而 labels 则是表示各扇形标签的字符型向量。
代码清单6-5给出了四个示例,结果如图6-6所示。
饼图让比较各扇形的值变得困难(除非这些值被附加在标签上)。例如,观察(第一幅)最简单的饼图,你能分辨出美国(US)和德国(Germany)的大小吗?(如果你可以,说明你的洞察力比我好。)为改善这种状况,我们创造了一种称为扇形图(fan plot)的饼图变种。扇形图(Lemon& Tyagi,2009)提供了一种同时展示相对数量和相互差异的方法。在R中,扇形图是通过 plotrix包中的 fan.plot() 函数实现的。
考虑以下代码和结果图(图6-7):
library(plotrix)
slices <- c(10, 12,4, 16, 8)
lbls <- c("US", "UK", "Australia", "Germany", "France")
fan.plot(slices, labels = lbls, main="Fan Plot")
如你所见,确定扇形图中扇形的相对大小比饼图要简单得多。扇形图虽然尚未普及,但它仍然是新生力量。
既然已经讲完了饼图和扇形图,就让我们转到直方图上吧。与条形图和饼图不同,直方图描述的是连续型变量的分布。
直方图
直方图通过在x轴上将值域分割为一定数量的组,在y轴上显示相应值的频数,展示了连续型变量的分布。可以使用如下函数创建直方图:
hist(x)
其中的 x 是一个由数据值组成的数值向量。参数 freq=FALSE 表示根据概率密度而不是频数绘制图形。参数 breaks 用于控制组的数量。在定义直方图中的单元时,默认将生成等距切分。代码
清单6-6提供了绘制四种直方图的代码,绘制结果见图6-8。
par(mfrow=c(2,2))
hist(mtcars$mpg)
hist(mtcars$mpg,
breaks=12,
col="red",
xlab="Miles Per Gallon",
main="Colored histogram with 12 bins")
hist(mtcars$mpg,
freq=FALSE,
breaks=12,
col="red",
xlab="Miles Per Gallon",
main="Histogram, rug plot, density curve")
rug(jitter(mtcars$mpg))
lines(density(mtcars$mpg), col="blue", lwd=2)
x <- mtcars$mpg
h<-hist(x,
breaks=12,
col="red",
xlab="Miles Per Gallon",
main="Histogram with normal curve and box")
xfit<-seq(min(x), max(x), length=40)
yfit<-dnorm(xfit, mean=mean(x), sd=sd(x))
yfit <- yfit*diff(h$mids[1:2])*length(x)
lines(xfit, yfit, col="blue", lwd=2)
box()
核密度图
在上节中,你看到了直方图上叠加的核密度图。用术语来说,核密度估计是用于估计随机变量概率密度函数的一种非参数方法。虽然其数学细节已经超出了本书的范畴,但从总体上讲,核密度图不失为一种用来观察连续型变量分布的有效方法。绘制密度图的方法(不叠加到另一幅图上方)为:
plot(density(x))
其中的 x 是一个数值型向量。由于 plot() 函数会创建一幅新的图形,所以要向一幅已经存在的图形上叠加一条密度曲线,可以使用 lines() 函数(如代码清单6-6所示)
代码清单6-7给出了两幅核密度图示例,结果如图6-9所示。
par(mfrow=c(2,1))
d <- density(mtcars$mpg)
# 完全使用默认参数
plot(d)
d <- density(mtcars$mpg)
# 添加标题
plot(d, main="Kernel Density of Miles Per Gallon")
# 将曲线改为蓝色,并使用实心红色填充曲线下方的区域
polygon(d, col="red", border="blue")
# 添加棕色的轴须图
rug(mtcars$mpg, col="brown")
箱线图
箱线图(又称盒须图)通过绘制连续型变量的五数总括,即最小值、下四分位数(第25百分位数)、中位数(第50百分位数)、上四分位数(第75百分位数)以及最大值,描述了连续型变量的分布。箱线图能够显示出可能为离群点(范围±1.5*IQR以外的值,IQR表示四分位距,即上四分位数与下四分位数的差值)的观测。例如:
boxplot(mtcars$mpg, main="Box plot", ylab="Miles per Gallon")
生成了如图6-11所示的图形。为了图解各个组成部分,我手工添加了标注。
默认情况下,两条须的延伸极限不会超过盒型各端加1.5倍四分位距的范围。此范围以外的值将以点来表示(在这里没有画出)。
举例来说,在我们的车型样本中,每加仑汽油行驶英里数的中位数是19.2,50%的值都落在了15.3和22.8之间,最小值为10.4,最大值为33.9。我是如何从图中如此精确地读出了这些值呢?执行 boxplot.stats(mtcars$mpg) 即可输出用于构建图形的统计量(换句话说,我作弊了)。图中似乎不存在离群点,而且略微正偏(上侧的须较下侧的须更长)。
使用并列箱线图进行跨组比较
箱线图可以展示单个变量或分组变量。使用格式为:
boxplot(formula, data=dataframe)
其中的 formula 是一个公式, dataframe 代表提供数据的数据框(或列表)。一个示例公式为 y ~A ,这将为类别型变量 A 的每个值并列地生成数值型变量 y 的箱线图。公式 y ~ A*B 则将为类别型变量 A 和 B 所有水平的两两组合生成数值型变量 y 的箱线图。添加参数 varwidth=TRUE 将使箱线图的宽度与其样本大小的平方根成正比。参数horizontal=TRUE 可以反转坐标轴的方向。
在以下代码中,我们使用并列箱线图重新研究了四缸、六缸、八缸发动机对每加仑汽油行驶的英里数的影响。结果如图6-12所示。
boxplot(mpg ~ cyl, data=mtcars,
main="Car Mileage Data",
xlab="Number of Cylinders",
ylab="Miles Per Gallon")
在图6-12中可以看到不同组间油耗的区别非常明显。同时也可以发现,六缸车型的每加仑汽油行驶的英里数分布较其他两类车型更为均匀。与六缸和八缸车型相比,四缸车型的每加仑汽油行驶的英里数散布最广(且正偏)。在八缸组还有一个离群点。箱线图灵活多变,通过添加 notch=TRUE ,可以得到含凹槽的箱线图。若两个箱的凹槽互不重叠,则表明它们的中位数有显著差异(Chambers et al.,1983,p. 62)。以下代码将为我们的车型油耗示例创建一幅含凹槽的箱线图:
boxplot(mpg ~ cyl, data=mtcars,
notch=TRUE,
varwidth=TRUE,
col="red",
main="Car Mileage Data",
xlab="Number of Cylinders",
ylab="Miles Per Gallon")
参数 col 以红色填充了箱线图,而 varwidth=TRUE 则使箱线图的宽度与它们各自的样本大小成正比。
在图6-13中可以看到,四缸、六缸、八缸车型的油耗中位数是不同的。随着汽缸数的减少,油耗明显降低。
最后,你可以为多个分组因子绘制箱线图。代码清单6-9为不同缸数和不同变速箱类型的车型绘制了每加仑汽油行驶英里数的箱线图(图形如图6-14所示)。同样地,这里使用参数 col 为箱线图进行了着色。请注意颜色的循环使用。在本例中,共有六幅箱线图和两种指定的颜色,所以颜色将重复使用三次。
mtcars$cyl.f <- factor(mtcars$cyl,
levels=c(4,6,8),
labels=c("4","6","8"))
mtcars$am.f <- factor(mtcars$am,
levels=c(0,1),
labels=c("auto", "standard"))
boxplot(mpg ~ am.f *cyl.f,
data=mtcars,
varwidth=TRUE,
col=c("gold","darkgreen"),
main="MPG Distribution by Auto Type",
xlab="Auto Type", ylab="Miles Per Gallon")
图6-14再一次清晰地显示出油耗随着缸数的下降而减少。对于四缸和六缸车型,标准变速箱(standard)的油耗更高。但是对于八缸车型,油耗似乎没有差别。你也可以从箱线图的宽度看出,四缸标准变速箱的车型和八缸自动变速箱的车型在数据集中最常见。
小提琴图
在结束箱线图的讨论之前,有必要研究一种称为小提琴图(violin plot)的箱线图变种。小提琴图是箱线图与核密度图的结合。你可以使用 vioplot 包中的 vioplot() 函数绘制它。请在第一次使用之前安装 vioplot 包。
vioplot() 函数的使用格式为:
vioplot(x1, x2, ... , names=, col=)
其中 x1, x2, ... 表示要绘制的一个或多个数值向量(将为每个向量绘制一幅小提琴图)。参数names 是小提琴图中标签的字符向量,而 col 是一个为每幅小提琴图指定颜色的向量。代码清单6-10中给出了一个示例。
library(vioplot)
x1 <- mtcars$mpg[mtcars$cyl==4]
x2 <- mtcars$mpg[mtcars$cyl==6]
x3 <- mtcars$mpg[mtcars$cyl==8]
vioplot(x1, x2, x3,
names=c("4 cyl", "6 cyl", "8 cyl"),
col="gold")
title("Violin Plots of Miles Per Gallon", ylab="Miles Per Gallon",
xlab="Number of Cylinders")
小提琴图基本上是核密度图以镜像方式在箱线图上的叠加。在图中,白点是中位数,黑色盒型的范围是下四分位点到上四分位点,细黑线表示须。外部形状即为核密度估计。小提琴图还没有真正地流行起来。同样,这可能也是由于普遍缺乏方便好用的软件导致的。时间会证明一切。我们将以点图结束本章。与之前看到的图形不同,点图绘制变量中的所有值。
点图
点图提供了一种在简单水平刻度上绘制大量有标签值的方法。你可以使用 dotchart() 函数
创建点图,格式为:
dotchart(x, labels=)
其中的 x 是一个数值向量,而 labels 则是由每个点的标签组成的向量。你可以通过添加参数groups 来选定一个因子,用以指定 x 中元素的分组方式。如果这样做,则参数 gcolor 可以控制不同组标签的颜色, cex 可以控制标签的大小。这里是 mtcars 数据集的一个示例:
dotchart(mtcars$mpg, labels=row.names(mtcars), cex=.7,
main="Gas Mileage for Car Models",
xlab="Miles Per Gallon")
绘图结果已在图6-16中给出。
图6-16可以让你在同一个水平轴上观察每种车型的每加仑汽油行驶英里数。通常来说,点图在经过排序并且分组变量被不同的符号和颜色区分开的时候最有用。代码清单6-11给出了一个示例,绘图的结果如图6-17所示。
x <- mtcars[order(mtcars$mpg),]
x$cyl <- factor(x$cyl)
x$color[x$cyl==4] <- "red"
x$color[x$cyl==6] <- "blue"
x$color[x$cyl==8] <- "darkgreen"
dotchart(x$mpg,
labels = row.names(x),
cex=.7,
groups = x$cyl,
gcolor = "black",
color = x$color,
pch=19,
main = "Gas Mileage for Car Models\ngrouped by cylinder",
xlab = "Miles Per Gallon")
在图6-17中,许多特征第一次明显起来。你再次看到,随着汽缸数的减少,每加仑汽油行驶的英里数有了增加。但你同时也看到了例外。例如,Pontiac Firebird有8个汽缸,但较六缸的Mercury280C和Valiant的行驶英里数更多。六缸的Hornet 4 Drive与四缸的Volvo 142E的每加仑汽油行驶英里数相同。同样明显的是,Toyota Corolla的油耗最低,而Lincoln Continental和Cadillac Fleetwood是英里数较低一端的离群点。
在本例中,你可以从点图中获得显著的洞察力,因为每个点都有标签,每个点的值都有其内在含义,并且这些点是以一种能够促进比较的方式排布的。但是随着数据点的增多,点图的实用性随之下降。
注意 点图有许多变种。Jacoby(2006)对点图进行了非常有意义的讨论,并且提供了创新型应用的R代码。此外, Hmisc 包也提供了一个带有许多附加功能的点图函数(恰如其分地叫作 dotchart2 )。
END !