第 9 章 ggplot2
9.1 条形图与线图
9.1.1 问题
你想要创建基本的条形图与线图
9.1.2 方案
想要使用 ggplot2 绘制图形,数据必须是一个数据框,而且必须是长格式。
9.1.2.1 基本图形,离散 x-axis
使用条形图,条形的高度通常代表这种不同的东西:
- 每一组事件的计数,通过
stat_bin()
指定,ggplot2 默认使用该选项 - 数据集中某一列的值,通过
stat_identity()
指定
x 轴 | 高度含义 | 名称 |
---|---|---|
连续 | 计数 | 直方图 |
离散 | 计数 | 条形图 |
连续 | 数值 | 条形图 |
离散 | 数值 | 条形图 |
9.1.2.1.1 有值的条形图
这里有一些样例数据 (抽自 reshape2 包的 tips
数据集):
<- data.frame(time = factor(c("Lunch", "Dinner"), levels = c("Lunch",
dat "Dinner")), total_bill = c(14.89, 17.23))
dat#> time total_bill
#> 1 Lunch 14.89
#> 2 Dinner 17.23
# 导入 ggplot2 分析包
library(ggplot2)
在这些例子中,条形的高度代表数据框某一列的值,所以使用 stat="identity"
而不是默认的 stat="bin"
。
这里使用的映射变量为:
time
: x-axis 和填充颜色total_bill
: y-axis
# 非常基本的条形图
ggplot(data = dat, aes(x = time, y = total_bill)) + geom_bar(stat = "identity")
# 按时间填充颜色
ggplot(data = dat, aes(x = time, y = total_bill, fill = time)) +
geom_bar(stat = "identity")
## 这和上面是一样的结果 ggplot(data=dat, aes(x=time,
## y=total_bill)) + geom_bar(aes(fill=time),
## stat='identity') 添加黑色的边框线
ggplot(data = dat, aes(x = time, y = total_bill, fill = time)) +
geom_bar(colour = "black", stat = "identity")
# 没有图例,因为这个信息是多余的
ggplot(data = dat, aes(x = time, y = total_bill, fill = time)) +
geom_bar(colour = "black", stat = "identity") + guides(fill = FALSE)
#> Warning: `guides(<scale> = FALSE)` is deprecated.
#> Please use `guides(<scale> = "none")` instead.
一个理想的条形图可能是下面这样的:
# 添加题目,缩小箱宽,填充颜色,改变轴标签
ggplot(data = dat, aes(x = time, y = total_bill, fill = time)) +
geom_bar(colour = "black", fill = "#DD8888", width = 0.8,
stat = "identity") + guides(fill = FALSE) + xlab("Time of day") +
ylab("Total bill") + ggtitle("Average bill for 2 people")
#> Warning: `guides(<scale> = FALSE)` is deprecated.
#> Please use `guides(<scale> = "none")` instead.
查看颜色获取更多关于颜色的信息。
9.1.2.1.2 计数的条形图
在下面例子中,条形高度代表事件的计数。
我们直接使用 reshape2 的 tips
数据集。
library(reshape2)
# 查看头几行
head(tips)
#> total_bill tip sex smoker day time size
#> 1 16.99 1.01 Female No Sun Dinner 2
#> 2 10.34 1.66 Male No Sun Dinner 3
#> 3 21.01 3.50 Male No Sun Dinner 3
#> 4 23.68 3.31 Male No Sun Dinner 2
#> 5 24.59 3.61 Female No Sun Dinner 4
#> 6 25.29 4.71 Male No Sun Dinner 4
想要得到一个计数的条形图,不要映射变量到 y
,使用 stat="bin"
(默认就是这个) 而不是 stat="identity"
:
# 计数的条形图
ggplot(data = tips, aes(x = day)) + geom_bar(stat = "count")
## 和上面等同, 因为 stat='bin' 是默认
## ggplot(data=tips, aes(x=day)) + geom_bar()
9.1.2.2 线图
对于线图,数据点必须分组从而 R 知道怎么连接这些点。如果只有一组的话,非常简单,设定 group=1
即可,如果是多组,需要设定分组变量。
下面是使用的映射变量:
time
: x 轴total_bill
: y 轴
# 基本的线图
ggplot(data = dat, aes(x = time, y = total_bill, group = 1)) +
geom_line()
## 与上面结果一致 ggplot(data=dat, aes(x=time,
## y=total_bill)) + geom_line(aes(group=1)) 添加点
ggplot(data = dat, aes(x = time, y = total_bill, group = 1)) +
geom_line() + geom_point()
# 改变线和点的颜色
# 改变线的类型和点的类型,用更粗的线、更大的点
# 用红色填充点
ggplot(data = dat, aes(x = time, y = total_bill, group = 1)) +
geom_line(colour = "red", linetype = "dashed", size = 1.5) +
geom_point(colour = "red", size = 4, shape = 21, fill = "white")
理想的线图可能像下面这样:
# 设定 y 轴的范围 改变轴标签
ggplot(data = dat, aes(x = time, y = total_bill, group = 1)) +
geom_line() + geom_point() + expand_limits(y = 0) +
xlab("Time of day") + ylab("Total bill") + ggtitle("Average bill for 2 people")
9.1.3 有更多变量的图
下面这个数据将用于接下来的例子
<- data.frame(sex = factor(c("Female", "Female", "Male",
dat1 "Male")), time = factor(c("Lunch", "Dinner", "Lunch",
"Dinner"), levels = c("Lunch", "Dinner")), total_bill = c(13.53,
16.81, 16.24, 17.42))
dat1#> sex time total_bill
#> 1 Female Lunch 13.53
#> 2 Female Dinner 16.81
#> 3 Male Lunch 16.24
#> 4 Male Dinner 17.42
9.1.3.1 条形图
变量映射:
time
: x 轴sex
: 颜色填充total_bill
: y 轴
# 堆积条形图 -- 不常用
ggplot(data = dat1, aes(x = time, y = total_bill, fill = sex)) +
geom_bar(stat = "identity")
# 条形图,x 轴是 time, 颜色填充是 sex
ggplot(data = dat1, aes(x = time, y = total_bill, fill = sex)) +
geom_bar(stat = "identity", position = position_dodge())
ggplot(data = dat1, aes(x = time, y = total_bill, fill = sex)) +
geom_bar(stat = "identity", position = position_dodge(),
colour = "black")
# 改变颜色
ggplot(data = dat1, aes(x = time, y = total_bill, fill = sex)) +
geom_bar(stat = "identity", position = position_dodge(),
colour = "black") + scale_fill_manual(values = c("#999999",
"#E69F00"))
改变映射是非常容易的:
# 条形图,x 轴是性别,颜色是时间
ggplot(data = dat1, aes(x = sex, y = total_bill, fill = time)) +
geom_bar(stat = "identity", position = position_dodge(),
colour = "black")
查看颜色获取更多关于颜色的信息。
9.1.3.2 线图
变量映射:
time
: x 轴sex
: 线的颜色total_bill
: y 轴
想要绘制多条线,必须指定分组变量,否则所有点都将通过一条线进行连接。在这个例子中,我们希望通过性别来进行分组。
# 基本的带点线图
ggplot(data = dat1, aes(x = time, y = total_bill, group = sex)) +
geom_line() + geom_point()
# 将性别映射到颜色
ggplot(data = dat1, aes(x = time, y = total_bill, group = sex,
colour = sex)) + geom_line() + geom_point()
# 映射性别到不同的点类型
ggplot(data = dat1, aes(x = time, y = total_bill, group = sex,
shape = sex)) + geom_line() + geom_point()
# 使用更粗的线、更大的点
ggplot(data = dat1, aes(x = time, y = total_bill, group = sex,
shape = sex)) + geom_line(size = 1.5) + geom_point(size = 3,
fill = "white") + scale_shape_manual(values = c(22,
21))
更改颜色和线型变量的映射非常容易:
ggplot(data = dat1, aes(x = sex, y = total_bill, group = time,
shape = time, color = time)) + geom_line() + geom_point()
9.1.3.3 完成的例子
完成的例子可能像下面这样:
# 一个条形图
ggplot(data=dat1, aes(x=time, y=total_bill, fill=sex)) +
geom_bar(colour="black", stat="identity",
position=position_dodge(),
size=.3) + # 更粗的线
scale_fill_hue(name="Sex of payer") + # 设定图例标题
xlab("Time of day") + ylab("Total bill") + # 设定轴标签
ggtitle("Average bill for 2 people") + # 设定题目
theme_bw()
# 一个线图
ggplot(data=dat1, aes(x=time, y=total_bill, group=sex, shape=sex, colour=sex)) +
geom_line(aes(linetype=sex), size=1) + # 按性别设定线型
geom_point(size=3, fill="white") + # 使用更大的点,并用颜色填充
expand_limits(y=0) + # 将 0 包含仅 y 轴
scale_colour_hue(name="Sex of payer", # 设定图例标题
l=30) + # 使用更深的颜色 (lightness=30)
scale_shape_manual(name="Sex of payer",
values=c(22,21)) + #
scale_linetype_discrete(name="Sex of payer") +
xlab("Time of day") + ylab("Total bill") + # 设定轴标签
ggtitle("Average bill for 2 people") + # 设定标题
theme_bw() +
theme(legend.position=c(.7, .4)) # 图例的位置
为了保证上图的图例一致,必须指定 3 次。至于为何如此,查看图例。
9.1.4 使用数值 x-axis
当 x 轴上的变量是数字时,有时需要将其视为连续变量,有时需要将其视为分类变量。在该数据集中,剂量应该是值为 0.5
, 1.0
和 2.0
的数值变量。作图时,将这些值视为相同类别可能很有用。
<- read.table(header = TRUE, text = "
datn supp dose length
OJ 0.5 13.23
OJ 1.0 22.70
OJ 2.0 26.06
VC 0.5 7.98
VC 1.0 16.77
VC 2.0 26.14
")
来自 ToothGrowth
数据集。
9.1.4.1 x-axis 作为连续变量
一个简单的图形可能会将剂量放在 x 轴,这种方式可以绘制一个线图。
ggplot(data = datn, aes(x = dose, y = length, group = supp,
colour = supp)) + geom_line() + geom_point()
9.1.4.2 x-axis 作为分类变量
首先,我们要将该变量转换为因子。
# 拷贝数据框并将它转换为因子
<- datn
datn2 $dose <- factor(datn2$dose)
datn2ggplot(data = datn2, aes(x = dose, y = length, group = supp,
colour = supp)) + geom_line() + geom_point()
# 使用原始的数据框,但使用 factor 函数在绘图时转换
ggplot(data = datn, aes(x = factor(dose), y = length, group = supp,
colour = supp)) + geom_line() + geom_point()
当连续值作为分类变量使用时,也可以绘制条形图。
ggplot(data = datn2, aes(x = dose, y = length, fill = supp)) +
geom_bar(stat = "identity", position = position_dodge())
ggplot(data = datn, aes(x = factor(dose), y = length, fill = supp)) +
geom_bar(stat = "identity", position = position_dodge())
9.2 绘制均值和误差线
9.2.1 问题
你想要为一个数据集绘制均值的误差线。
9.2.2 方案
想要用 ggplot2 绘制图形,数据必须是数据框形式,而且是长格式(相对于宽格式)。如果你的数据需要重构,请参考长宽格式转换获取更多信息。
9.2.2.1 助手函数
如果你处理的仅仅是组间变量,那么 summarySE()
是你代码中唯一需要使用的函数。如果你的数据里有组内变量,并且你想要矫正误差线使得组间的变异被移除,就像 Loftus and Masson (1994) 里的那样,那么 normDataWithin()
和 summarySEwithin()
这两个函数必须加入你的代码中,然后调用 summarySEwithin()
函数进行计算。
## Gives count, mean, standard deviation, standard
## error of the mean, and confidence interval (default
## 95%). data: a data frame. measurevar: the name of
## a column that contains the variable to be
## summariezed groupvars: a vector containing names of
## columns that contain grouping variables na.rm: a
## boolean that indicates whether to ignore NA's
## conf.interval: the percent range of the confidence
## interval (default is 95%)
<- function(data = NULL, measurevar, groupvars = NULL,
summarySE na.rm = FALSE, conf.interval = 0.95, .drop = TRUE) {
library(plyr)
# New version of length which can handle NA's: if
# na.rm==T, don't count them
<- function(x, na.rm = FALSE) {
length2 if (na.rm)
sum(!is.na(x)) else length(x)
}
# This does the summary. For each group's data
# frame, return a vector with N, mean, and sd
<- ddply(data, groupvars, .drop = .drop, .fun = function(xx,
datac
col) {c(N = length2(xx[[col]], na.rm = na.rm), mean = mean(xx[[col]],
na.rm = na.rm), sd = sd(xx[[col]], na.rm = na.rm))
}, measurevar)
# Rename the 'mean' column
<- rename(datac, c(mean = measurevar))
datac
$se <- datac$sd/sqrt(datac$N) # Calculate standard error of the mean
datac
# Confidence interval multiplier for standard error
# Calculate t-statistic for confidence interval:
# e.g., if conf.interval is .95, use .975
# (above/below), and use df=N-1
<- qt(conf.interval/2 + 0.5, datac$N - 1)
ciMult $ci <- datac$se * ciMult
datac
return(datac)
}
## Norms the data within specified groups in a data
## frame; it normalizes each subject (identified by
## idvar) so that they have the same mean, within each
## group specified by betweenvars. data: a data
## frame. idvar: the name of a column that identifies
## each subject (or matched subjects) measurevar: the
## name of a column that contains the variable to be
## summariezed betweenvars: a vector containing names
## of columns that are between-subjects variables
## na.rm: a boolean that indicates whether to ignore
## NA's
<- function(data = NULL, idvar, measurevar,
normDataWithin betweenvars = NULL, na.rm = FALSE, .drop = TRUE) {
library(plyr)
# Measure var on left, idvar + between vars on right
# of formula.
<- ddply(data, c(idvar, betweenvars),
data.subjMean .drop = .drop, .fun = function(xx, col, na.rm) {
c(subjMean = mean(xx[, col], na.rm = na.rm))
}, measurevar, na.rm)
# Put the subject means with original data
<- merge(data, data.subjMean)
data
# Get the normalized data in a new column
<- paste(measurevar, "_norm", sep = "")
measureNormedVar <- data[, measurevar] - data[,
data[, measureNormedVar] "subjMean"] + mean(data[, measurevar], na.rm = na.rm)
# Remove this subject mean column
$subjMean <- NULL
data
return(data)
}
## Summarizes data, handling within-subjects variables
## by removing inter-subject variability. It will
## still work if there are no within-S variables.
## Gives count, un-normed mean, normed mean (with same
## between-group mean), standard deviation, standard
## error of the mean, and confidence interval. If
## there are within-subject variables, calculate
## adjusted values using method from Morey (2008).
## data: a data frame. measurevar: the name of a
## column that contains the variable to be summariezed
## betweenvars: a vector containing names of columns
## that are between-subjects variables withinvars: a
## vector containing names of columns that are
## within-subjects variables idvar: the name of a
## column that identifies each subject (or matched
## subjects) na.rm: a boolean that indicates whether
## to ignore NA's conf.interval: the percent range of
## the confidence interval (default is 95%)
<- function(data = NULL, measurevar, betweenvars = NULL,
summarySEwithin withinvars = NULL, idvar = NULL, na.rm = FALSE, conf.interval = 0.95,
.drop = TRUE) {
# Ensure that the betweenvars and withinvars are
# factors
<- vapply(data[, c(betweenvars, withinvars),
factorvars drop = FALSE], FUN = is.factor, FUN.VALUE = logical(1))
if (!all(factorvars)) {
<- names(factorvars)[!factorvars]
nonfactorvars message("Automatically converting the following non-factors to factors: ",
paste(nonfactorvars, collapse = ", "))
<- lapply(data[nonfactorvars],
data[nonfactorvars]
factor)
}
# Get the means from the un-normed data
<- summarySE(data, measurevar, groupvars = c(betweenvars,
datac na.rm = na.rm, conf.interval = conf.interval,
withinvars), .drop = .drop)
# Drop all the unused columns (these will be
# calculated with normed data)
$sd <- NULL
datac$se <- NULL
datac$ci <- NULL
datac
# Norm each subject's data
<- normDataWithin(data, idvar, measurevar, betweenvars,
ndata .drop = .drop)
na.rm,
# This is the name of the new column
<- paste(measurevar, "_norm", sep = "")
measurevar_n
# Collapse the normed data - now we can treat
# between and within vars the same
<- summarySE(ndata, measurevar_n, groupvars = c(betweenvars,
ndatac na.rm = na.rm, conf.interval = conf.interval,
withinvars), .drop = .drop)
# Apply correction from Morey (2008) to the standard
# error and confidence interval Get the product of
# the number of conditions of within-S variables
<- prod(vapply(ndatac[, withinvars, drop = FALSE],
nWithinGroups FUN = nlevels, FUN.VALUE = numeric(1)))
<- sqrt(nWithinGroups/(nWithinGroups -
correctionFactor 1))
# Apply the correction factor
$sd <- ndatac$sd * correctionFactor
ndatac$se <- ndatac$se * correctionFactor
ndatac$ci <- ndatac$ci * correctionFactor
ndatac
# Combine the un-normed means with the normed
# results
merge(datac, ndatac)
}
9.2.2.2 示例数据
下面的示例将使用 ToothGrowth
数据集。注意 dose
在这里是一个数值列,一些情况下我们将它转换为因子变量将会更加有用。
<- ToothGrowth
tg head(tg)
#> len supp dose
#> 1 4.2 VC 0.5
#> 2 11.5 VC 0.5
#> 3 7.3 VC 0.5
#> 4 5.8 VC 0.5
#> 5 6.4 VC 0.5
#> 6 10.0 VC 0.5
library(ggplot2)
首先,我们必须对数据进行统计汇总。 这可以通过多种方式实现,参阅汇总数据。在这个案例中,我们将使用 summarySE()
函数( 确保summarySE()
函数的代码在使用前已经键入)。
# install.packages('Rmisc')
library(Rmisc)
#> 载入需要的程辑包:lattice
#> 载入需要的程辑包:plyr
#>
#> 载入程辑包:'Rmisc'
#> The following objects are masked _by_ '.GlobalEnv':
#>
#> normDataWithin, summarySE, summarySEwithin
# summarySE 函数提供了标准差、标准误以及 95%
# 的置信区间
<- summarySE(tg, measurevar = "len", groupvars = c("supp",
tgc "dose"))
tgc#> supp dose N len sd se ci
#> 1 OJ 0.5 10 13.23 4.460 1.4103 3.190
#> 2 OJ 1.0 10 22.70 3.911 1.2368 2.798
#> 3 OJ 2.0 10 26.06 2.655 0.8396 1.899
#> 4 VC 0.5 10 7.98 2.747 0.8686 1.965
#> 5 VC 1.0 10 16.77 2.515 0.7954 1.799
#> 6 VC 2.0 10 26.14 4.798 1.5172 3.432
9.2.2.3 线图
数据统计总结后,我们就可以开始绘制图形了。这里是一些带误差线的线图和点图,误差线代表标准差、标准误或者是 95% 的置信区间。
# 均值的标准误
ggplot(tgc, aes(x = dose, y = len, colour = supp)) + geom_errorbar(aes(ymin = len -
ymax = len + se), width = 0.1) + geom_line() + geom_point() se,
# 发现误差线重叠(dose=2.0),我们使用 position_dodge
# 将它们进行水平移动
<- position_dodge(0.1) # move them .05 to the left and right
pd
ggplot(tgc, aes(x = dose, y = len, colour = supp)) + geom_errorbar(aes(ymin = len -
ymax = len + se), width = 0.1, position = pd) +
se, geom_line(position = pd) + geom_point(position = pd)
# 使用 95% 置信区间替换标准误
ggplot(tgc, aes(x = dose, y = len, colour = supp)) + geom_errorbar(aes(ymin = len -
ymax = len + ci), width = 0.1, position = pd) +
ci, geom_line(position = pd) + geom_point(position = pd)
# 黑色的误差线 - 注意 'group=supp' 的映射 --
# 没有它,误差线将不会避开(就是会重叠)。
ggplot(tgc, aes(x = dose, y = len, colour = supp, group = supp)) +
geom_errorbar(aes(ymin = len - ci, ymax = len + ci),
colour = "black", width = 0.1, position = pd) +
geom_line(position = pd) + geom_point(position = pd,
size = 3)
一张完成的带误差线(代表均值的标准误)的图形可能像下面显示的那样。最后画点,这样白色将会在线和误差线的上面(这个需要理解图层概念,顺序不同展示的效果是不一样的)。
ggplot(tgc, aes(x=dose, y=len, colour=supp, group=supp)) +
geom_errorbar(aes(ymin=len-se, ymax=len+se), colour="black", width=.1, position=pd) +
geom_line(position=pd) +
geom_point(position=pd, size=3, shape=21, fill="white") + # 21的填充的圆
xlab("Dose (mg)") +
ylab("Tooth length") +
scale_colour_hue(name="Supplement type", # 图例标签使用暗色
breaks=c("OJ", "VC"),
labels=c("Orange juice", "Ascorbic acid"),
l=40) + # 使用暗色,亮度为40
ggtitle("The Effect of Vitamin C on\nTooth Growth in Guinea Pigs") +
expand_limits(y=0) + # 扩展范围
scale_y_continuous(breaks=0:20*4) + # 每4个单位设置标记(y轴)
theme_bw() +
theme(legend.justification=c(1,0),
legend.position=c(1,0)) # 右下方放置图例
9.2.2.4 条形图
条形图绘制误差线也非常相似。 注意 tgc$dose
必须是一个因子。如果它是一个数值向量,将会不起作用。
# 将dose转换为因子变量
<- tgc
tgc2 $dose <- factor(tgc2$dose)
tgc2
# 误差线代表了均值的标准误
ggplot(tgc2, aes(x=dose, y=len, fill=supp)) +
geom_bar(position=position_dodge(), stat="identity") +
geom_errorbar(aes(ymin=len-se, ymax=len+se),
width=.2, # 误差线的宽度
position=position_dodge(.9))
# 使用95%的置信区间替换标准误
ggplot(tgc2, aes(x=dose, y=len, fill=supp)) +
geom_bar(position=position_dodge(), stat="identity") +
geom_errorbar(aes(ymin=len-ci, ymax=len+ci),
width=.2, # 误差线的宽度
position=position_dodge(.9))
一张绘制完成的图片像下面这样:
ggplot(tgc2, aes(x=dose, y=len, fill=supp)) +
geom_bar(position=position_dodge(), stat="identity",
colour="black", # 使用黑色边框,
size=.3) + # 将线变细
geom_errorbar(aes(ymin=len-se, ymax=len+se),
size=.3, # 将线变细
width=.2,
position=position_dodge(.9)) +
xlab("Dose (mg)") +
ylab("Tooth length") +
scale_fill_hue(name="Supplement type", # Legend label, use darker colors
breaks=c("OJ", "VC"),
labels=c("Orange juice", "Ascorbic acid")) +
ggtitle("The Effect of Vitamin C on\nTooth Growth in Guinea Pigs") +
scale_y_continuous(breaks=0:20*4) +
theme_bw()
9.2.2.5 为组内变量添加误差线
当所有的变量都属于不同组别时,我们画标准误或者置信区间会显得非常简单直观。然而,当我们描绘的是组内变量(重复测量),那么添加标准误或者通常的置信区间可能会对不同条件下差异的推断产生误导作用。
下面的方法来自 Morey (2008),它是对 Cousineau (2005)的矫正,而它所做的就是 提供比 Loftus and Masson (1994)更简单的方法。 你可以查看这些文章,以获得更多对组内变量误差线问题的详细探讨和方案。
这里有一个组内变量的数据集 (来自 Morey 2008),包含 pre/post-test
。
<- read.table(header = TRUE, text = "
dfw subject pretest posttest
1 59.4 64.5
2 46.4 52.4
3 46.0 49.7
4 49.0 48.7
5 32.5 37.4
6 45.2 49.5
7 60.3 59.9
8 54.3 54.1
9 45.4 49.6
10 38.9 48.5
")
# 将物体的 ID 作为因子变量对待
$subject <- factor(dfw$subject) dfw
第一步是将该数据集转换为长格式,参见长宽格式数据互换获取更多信息。
# 转换为长格式
library(reshape2)
<- melt(dfw, id.vars = "subject", measure.vars = c("pretest",
dfw_long "posttest"), variable.name = "condition")
dfw_long#> subject condition value
#> 1 1 pretest 59.4
#> 2 2 pretest 46.4
#> 3 3 pretest 46.0
#> 4 4 pretest 49.0
#> 5 5 pretest 32.5
#> 6 6 pretest 45.2
#> 7 7 pretest 60.3
#> 8 8 pretest 54.3
#> 9 9 pretest 45.4
#> 10 10 pretest 38.9
#> 11 1 posttest 64.5
#> 12 2 posttest 52.4
#> 13 3 posttest 49.7
#> 14 4 posttest 48.7
#> 15 5 posttest 37.4
#> 16 6 posttest 49.5
#> 17 7 posttest 59.9
#> 18 8 posttest 54.1
#> 19 9 posttest 49.6
#> 20 10 posttest 48.5
使用 summarySEwithin()
函数拆解数据。
<- summarySEwithin(dfw_long, measurevar = "value",
dfwc withinvars = "condition", idvar = "subject", na.rm = FALSE,
conf.interval = 0.95)
dfwc#> condition N value value_norm sd se ci
#> 1 posttest 10 51.43 51.43 2.262 0.7154 1.618
#> 2 pretest 10 47.74 47.74 2.262 0.7154 1.618
library(ggplot2)
# 创建带 95% 置信区间的图形
ggplot(dfwc, aes(x = condition, y = value, group = 1)) +
geom_line() + geom_errorbar(width = 0.1, aes(ymin = value -
ymax = value + ci)) + geom_point(shape = 21, size = 3,
ci, fill = "white") + ylim(40, 60)
value
和 value_norm
列代表了未标准化和标准化后的值。
9.2.2.6 理解组内变量的误差线
这部分解释组内的误差线值是如何计算出来的。这些步骤仅作解释目的;它们对于绘制误差线是非必需的。
下面独立数据的图形结果展示了组内变量 condition
存在连续一致的趋势,但使用常规的标准误(或者置信区间)则不能充分地展示这一点。Morey (2008) 和Cousineau (2005) 的方法本质是标准化数据去移除组间的变化,计算出这个标准化数据的变异程度。
# 使用一致的 y 轴范围
<- max(dfw_long$value)
ymax <- min(dfw_long$value)
ymin
# 绘制个体数据
ggplot(dfw_long, aes(x = condition, y = value, colour = subject,
group = subject)) + geom_line() + geom_point(shape = 21,
fill = "white") + ylim(ymin, ymax)
# 创造标准化的版本
<- normDataWithin(data = dfw_long, idvar = "subject",
dfwNorm.long measurevar = "value")
# 绘制标准化的个体数据
ggplot(dfwNorm.long, aes(x = condition, y = value_norm,
colour = subject, group = subject)) + geom_line() +
geom_point(shape = 21, fill = "white") + ylim(ymin,
ymax)
针对正常(组间)方法和组内方法的误差线差异在下面呈现。正常的方法计算出的误差线用红色表示,组内方法的误差线用黑色表示。
# Instead of summarySEwithin, use summarySE, which
# treats condition as though it were a
# between-subjects variable
<- summarySE(data = dfw_long, measurevar = "value",
dfwc_between groupvars = "condition", na.rm = FALSE, conf.interval = 0.95)
dfwc_between#> condition N value sd se ci
#> 1 pretest 10 47.74 8.599 2.719 6.151
#> 2 posttest 10 51.43 7.254 2.294 5.189
# 用红色显示组间的置信区间,用黑色展示组内的置信区间
ggplot(dfwc_between, aes(x = condition, y = value, group = 1)) +
geom_line() + geom_errorbar(width = 0.1, aes(ymin = value -
ymax = value + ci), colour = "red") + geom_errorbar(width = 0.1,
ci, aes(ymin = value - ci, ymax = value + ci), data = dfwc) +
geom_point(shape = 21, size = 3, fill = "white") + ylim(ymin,
ymax)
9.2.2.7 两个组内变量
如果存在超过一个的组内变量,我们可以使用相同的函数 summarySEwithin
。下面的数据集来自 Hays (1994),在 Rouder and Morey (2005) 中用来绘制这类的组内误差线。
<- read.table(header = TRUE, text = "
data Subject RoundMono SquareMono RoundColor SquareColor
1 41 40 41 37
2 57 56 56 53
3 52 53 53 50
4 49 47 47 47
5 47 48 48 47
6 37 34 35 36
7 47 50 47 46
8 41 40 38 40
9 48 47 49 45
10 37 35 36 35
11 32 31 31 33
12 47 42 42 42
")
数据集首先必须转换为长格式,列名显示了两个变量: 形状 (圆形/方形) 和配色方案 (黑白/有色)。
# 转换为长格式
library(reshape2)
<- melt(data = data, id.var = "Subject", measure.vars = c("RoundMono",
data_long "SquareMono", "RoundColor", "SquareColor"), variable.name = "Condition")
names(data_long)[names(data_long) == "value"] <- "Time"
# 拆分 Condition 列为 Shape 和 ColorScheme
$Shape <- NA
data_long$Shape[grepl("^Round", data_long$Condition)] <- "Round"
data_long$Shape[grepl("^Square", data_long$Condition)] <- "Square"
data_long$Shape <- factor(data_long$Shape)
data_long
$ColorScheme <- NA
data_long$ColorScheme[grepl("Mono$", data_long$Condition)] <- "Monochromatic"
data_long$ColorScheme[grepl("Color$", data_long$Condition)] <- "Colored"
data_long$ColorScheme <- factor(data_long$ColorScheme, levels = c("Monochromatic",
data_long"Colored"))
# 删除 Condition 列
$Condition <- NULL
data_long
# 检查数据
head(data_long)
#> Subject Time Shape ColorScheme
#> 1 1 41 Round Monochromatic
#> 2 2 57 Round Monochromatic
#> 3 3 52 Round Monochromatic
#> 4 4 49 Round Monochromatic
#> 5 5 47 Round Monochromatic
#> 6 6 37 Round Monochromatic
现在可以进行统计汇总和绘图了。
<- summarySEwithin(data_long, measurevar = "Time",
datac withinvars = c("Shape", "ColorScheme"), idvar = "Subject")
datac#> Shape ColorScheme N Time Time_norm sd se
#> 1 Round Colored 12 43.58 43.58 1.212 0.3500
#> 2 Round Monochromatic 12 44.58 44.58 1.331 0.3844
#> 3 Square Colored 12 42.58 42.58 1.462 0.4219
#> 4 Square Monochromatic 12 43.58 43.58 1.261 0.3641
#> ci
#> 1 0.7703
#> 2 0.8460
#> 3 0.9287
#> 4 0.8014
library(ggplot2)
ggplot(datac, aes(x = Shape, y = Time, fill = ColorScheme)) +
geom_bar(position = position_dodge(0.9), colour = "black",
stat = "identity") + geom_errorbar(position = position_dodge(0.9),
width = 0.25, aes(ymin = Time - ci, ymax = Time + ci)) +
coord_cartesian(ylim = c(40, 46)) + scale_fill_manual(values = c("#CCCCCC",
"#FFFFFF")) + scale_y_continuous(breaks = seq(1:100)) +
theme_bw() + geom_hline(yintercept = 38)
9.2.2.8 注意标准化的均值
函数 summarySEWithin()
返回标准化和未标准化的均值。未标准化的均值只是简单地表示每组的均值。标准化的均值计算出来保证组间的均值是一样的。
比如:
<- read.table(header = TRUE, text = "
dat id trial gender dv
A 0 male 2
A 1 male 4
B 0 male 6
B 1 male 8
C 0 female 22
C 1 female 24
D 0 female 26
D 1 female 28
")
# 标准化和未标准化的均值是不同的
summarySEwithin(dat, measurevar = "dv", withinvars = "trial",
betweenvars = "gender", idvar = "id")
#> Automatically converting the following non-factors to factors: gender, trial
#> gender trial N dv dv_norm sd se ci
#> 1 female 0 2 24 14 0 0 0
#> 2 female 1 2 26 16 0 0 0
#> 3 male 0 2 4 14 0 0 0
#> 4 male 1 2 6 16 0 0 0
9.2.3 其他
解决问题的方法不止作者提供的这一种,为了理解 ggplot2 是如何进行误差线的计算和添加,我在 stackoverflow 上提交了一个关于 ggplot2 使用 SE 还是 SD 作为默认误差线的问题。有人就给出了快速简易的解答。回答者的共同观点是必须先进行数据的统计计算。我之前在其他博客上看到使用 stat_boxplot(geom="errorbar", width=.3)
直接计算误差线可能就有问题(难以解释它算的是 SD 还是 SE)。
9.3 分布图
9.3.1 问题
你想要绘制一组数据的分布图。
9.3.2 方案
后面的例子中会使用以下这组简单的数据:
set.seed(1234)
<- data.frame(cond = factor(rep(c("A", "B"), each = 200)),
dat rating = c(rnorm(200), rnorm(200, mean = 0.8)))
# 查看数据
head(dat)
#> cond rating
#> 1 A -1.2071
#> 2 A 0.2774
#> 3 A 1.0844
#> 4 A -2.3457
#> 5 A 0.4291
#> 6 A 0.5061
library(ggplot2)
9.3.2.1 直方图和概率密度图
qplot()
函数能够用更简单的语法绘制出与 ggplot()
相同的图像。然而,在实践过程中你会发现 ggplot()
是更好的选择,因为 qplot()
中很多参数的选项都会让人感到困惑。
# 以 rating 为横轴绘制直方图,组距设为 0.5
# 两种函数都可以绘制出相同的图:
ggplot(dat, aes(x=rating)) + geom_histogram(binwidth=.5)
# qplot(dat$rating, binwidth=.5)
# 绘制黑色边线,白色填充的图
ggplot(dat, aes(x=rating)) +
geom_histogram(binwidth=.5, colour="black", fill="white")
# 密度曲线
ggplot(dat, aes(x=rating)) + geom_density()
# 直方图与核密度曲线重叠
ggplot(dat, aes(x=rating)) +
geom_histogram(aes(y=..density..), # 这里直方图以 density (密度)为y轴
binwidth=.5,
colour="black", fill="white") +
geom_density(alpha=.2, fill="#FF6666") # 重合部分透明填充
添加一条均值线:
ggplot(dat, aes(x=rating)) +
geom_histogram(binwidth=.5, colour="black", fill="white") +
geom_vline(aes(xintercept=mean(rating, na.rm=T)), # 忽略缺失值
color="red", linetype="dashed", size=1)
9.3.2.2 多组数据的直方图和概率密度图
# 重叠直方图
ggplot(dat, aes(x = rating, fill = cond)) + geom_histogram(binwidth = 0.5,
alpha = 0.5, position = "identity") # identity 表示将每个对象直接显示在图中,条形会彼此重叠。
# 间隔直方图
ggplot(dat, aes(x = rating, fill = cond)) + geom_histogram(binwidth = 0.5,
position = "dodge") # dodge 表示将每组的条形依次并列放置。
# 密度图
ggplot(dat, aes(x = rating, colour = cond)) + geom_density()
# 半透明填充的密度图
ggplot(dat, aes(x = rating, fill = cond)) + geom_density(alpha = 0.3)
在给每组数据添加均值线前,需要将每组数据的平均值赋值到一个新的数据框。
# 求均值
library(plyr)
<- ddply(dat, "cond", summarise, rating.mean = mean(rating))
cdat
cdat#> cond rating.mean
#> 1 A -0.05776
#> 2 B 0.87325
# 给重叠直方图添加均值线
ggplot(dat, aes(x = rating, fill = cond)) + geom_histogram(binwidth = 0.5,
alpha = 0.5, position = "identity") + geom_vline(data = cdat,
aes(xintercept = rating.mean, colour = cond), linetype = "dashed",
size = 1)
# 给密度图添加均值线
ggplot(dat, aes(x = rating, colour = cond)) + geom_density() +
geom_vline(data = cdat, aes(xintercept = rating.mean,
colour = cond), linetype = "dashed", size = 1)
使用分面:
ggplot(dat, aes(x = rating)) + geom_histogram(binwidth = 0.5,
colour = "black", fill = "white") + facet_grid(cond ~
.)
# 使用之前的 cdat 添加均值线
ggplot(dat, aes(x = rating)) + geom_histogram(binwidth = 0.5,
colour = "black", fill = "white") + facet_grid(cond ~
+ geom_vline(data = cdat, aes(xintercept = rating.mean),
.) linetype = "dashed", size = 1, colour = "red")
更多关于分面的细节可查看分面
9.3.2.3 箱型图
# 绘制箱型图
ggplot(dat, aes(x = cond, y = rating)) + geom_boxplot()
# 对分组填充颜色
ggplot(dat, aes(x = cond, y = rating, fill = cond)) + geom_boxplot()
# 将上图中冗余的图例删除掉:
ggplot(dat, aes(x = cond, y = rating, fill = cond)) + geom_boxplot() +
guides(fill = FALSE)
#> Warning: `guides(<scale> = FALSE)` is deprecated.
#> Please use `guides(<scale> = "none")` instead.
# 坐标轴翻转
ggplot(dat, aes(x = cond, y = rating, fill = cond)) + geom_boxplot() +
guides(fill = FALSE) + coord_flip()
#> Warning: `guides(<scale> = FALSE)` is deprecated.
#> Please use `guides(<scale> = "none")` instead.
同时可以通过 stat_summary()
来添加平均值。
# 用菱形图标指征平均值,并调整参数使该图标变更大。
ggplot(dat, aes(x = cond, y = rating)) + geom_boxplot() +
stat_summary(fun.y = mean, geom = "point", shape = 5,
size = 4)
#> Warning: `fun.y` is deprecated. Use `fun` instead.
9.4 散点图
9.4.1 问题
你想要绘制一幅散点图。
9.4.2 方案
假设这是你的数据:
set.seed(955)
# 创建一些噪声数据
<- data.frame(cond = rep(c("A", "B"), each = 10), xvar = 1:20 +
dat rnorm(20, sd = 3), yvar = 1:20 + rnorm(20, sd = 3))
head(dat)
#> cond xvar yvar
#> 1 A -4.252 3.47316
#> 2 A 1.702 0.00594
#> 3 A 4.323 -0.09425
#> 4 A 1.781 2.07281
#> 5 A 11.537 1.21544
#> 6 A 6.672 3.60811
library(ggplot2)
9.4.2.1 带回归线的基本散点图
ggplot(dat, aes(x=xvar, y=yvar)) +
geom_point(shape=1) # 使用空心圆
ggplot(dat, aes(x=xvar, y=yvar)) +
geom_point(shape=1) + # 使用空心圆
geom_smooth(method=lm) # 添加回归线
#> `geom_smooth()` using formula 'y ~ x'
# (默认包含 95% 置信区间)
ggplot(dat, aes(x=xvar, y=yvar)) +
geom_point(shape=1) + # 使用空心圆
geom_smooth(method=lm, # 添加回归线
se=FALSE) # 不加置信区域
#> `geom_smooth()` using formula 'y ~ x'
ggplot(dat, aes(x=xvar, y=yvar)) +
geom_point(shape=1) + # 使用空心圆
geom_smooth() # 添加带置信区间的平滑拟合曲线
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
9.4.2.2 通过其他变量设置颜色和形状
# 根据 cond 设置颜色
ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + geom_point(shape=1)
# 同上,但这里带了回归线
ggplot(dat, aes(x=xvar, y=yvar, color=cond)) +
geom_point(shape=1) +
scale_colour_hue(l=50) + # 使用稍暗的调色板
geom_smooth(method=lm,
se=FALSE)
#> `geom_smooth()` using formula 'y ~ x'
# 拓展回归线到数据区域之外(带预测效果)
ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + geom_point(shape=1) +
scale_colour_hue(l=50) +
geom_smooth(method=lm,
se=FALSE,
fullrange=TRUE)
#> `geom_smooth()` using formula 'y ~ x'
# 根据 cond 设置形状
ggplot(dat, aes(x=xvar, y=yvar, shape=cond)) + geom_point()
# 同上,但形状不同
ggplot(dat, aes(x=xvar, y=yvar, shape=cond)) + geom_point() +
scale_shape_manual(values=c(1,2)) # 使用圆和三角形
9.4.2.3 处理图像元素叠加
如果你有很多数据点,或者你的数据是离散的,那么数据可能会覆盖到一起,这样就看不清楚同一个位置有多少数据了。
# 取近似值
$xrnd <- round(dat$xvar/5) * 5
dat$yrnd <- round(dat$yvar/5) * 5
dat
# 让每个点都部分透明 如果情况严重,可以使用更小的值
ggplot(dat, aes(x = xrnd, y = yrnd)) + geom_point(shape = 19,
alpha = 1/4)
# 抖动点 抖动范围在 x 轴上是 1,y 轴上是 0.5
ggplot(dat, aes(x = xrnd, y = yrnd)) + geom_point(shape = 1,
position = position_jitter(width = 1, height = 0.5))
9.5 标题
9.5.1 问题
你想给图形设定一个标题。
9.5.2 方案
一个不带标题的图形例子:
library(ggplot2)
<- ggplot(PlantGrowth, aes(x = group, y = weight)) +
bp geom_boxplot()
bp
添加标题:
+ ggtitle("Plant growth") bp
## 等同于 bp + labs(title='Plant growth')
## 如果标题比较长,可以用 \n 将它分成多行来显示
+ ggtitle("Plant growth with\ndifferent treatments") bp
# 缩少行距并使用粗体
+ ggtitle("Plant growth with\ndifferent treatments") +
bp theme(plot.title = element_text(lineheight = 0.8, face = "bold"))
9.6 坐标轴
9.6.1 问题
你想要改变轴的顺序或方向。
9.6.2 方案
注意:下面的例子中提到的
scale_y_continuous()
、ylim()
等函数名中,y
都可以替换为x
。
下面使用内置的 PlantGrowth
数据集绘制一个基本的箱线图。
library(ggplot2)
<- ggplot(PlantGrowth, aes(x = group, y = weight)) +
bp geom_boxplot()
bp
9.6.2.1 交换 x 和 y 轴
交换 x 和 y 轴(让 x 垂直、y 水平)。
+ coord_flip() bp
9.6.2.2 离散轴
9.6.2.2.1 改变条目的顺序
# 手动设定离散轴条目的顺序
+ scale_x_discrete(limits = c("trt1", "trt2", "ctrl")) bp
## 逆转轴条目顺序 获取因子水平
<- levels(PlantGrowth$group)
flevels
flevels#> [1] "ctrl" "trt1" "trt2"
# 逆转顺序
<- rev(flevels)
flevels
flevels#> [1] "trt2" "trt1" "ctrl"
+ scale_x_discrete(limits = flevels) bp
# 或者一行搞定
+ scale_x_discrete(limits = rev(levels(PlantGrowth$group))) bp
9.6.2.2.2 设定标签
对于离散变量,标签来自于因子水平。然而,有时候短的因子水平名字并不适合展示。
+ scale_x_discrete(breaks = c("ctrl", "trt1", "trt2"),
bp labels = c("Control", "Treat 1", "Treat 2"))
# 隐藏 x 刻度、标签和网格线
+ scale_x_discrete(breaks = NULL) bp
# 隐藏所有的刻度和标签(X 轴),保留网格线
+ theme(axis.ticks = element_blank(), axis.text.x = element_blank()) bp
9.6.2.3 连续轴
9.6.2.3.1 设定范围和反转轴方向
如果你仅想简单地让轴包含某个值,可以使用 expand_limits()
,它会进行拓展而不是拉伸。
# 确保 y 轴包含 0
+ expand_limits(y = 0) bp
# 确保 y 轴包含 0 和 8
+ expand_limits(y = c(0, 8)) bp
当然你也可以通过 y 刻度显式地指定。注意如果使用任何 scale_y_continuous
命令,它会覆盖任何 ylim
命令,而且 ylim
会被忽略。
# 设定连续值轴的范围 下面是相同的操作
+ ylim(0, 8) bp
# bp + scale_y_continuous(limits=c(0, 8))
如果使用上述方法让 y 轴的范围变小,任何超出范围的数据都会被忽略。有时候这会产生一些问题,读者需要注意。
为了避免产生问题,你可以使用 coord_cartesian()
,相比于设定轴的范围,它设定数据可视化的区域。
# 这两个操作一致,超出范围的数据被删除了,导致产生一个误导的箱线图
+ ylim(5, 7.5)
bp #> Warning: Removed 13 rows containing non-finite values
#> (stat_boxplot).
# bp + scale_y_continuous(limits=c(5, 7.5))
# 使用 coord_cartesian 'zooms' 区域
+ coord_cartesian(ylim = c(5, 7.5)) bp
# 直接指定刻度
+ coord_cartesian(ylim = c(5, 7.5)) + scale_y_continuous(breaks = seq(0,
bp 10, 0.25)) # Ticks from 0-10, every .25
9.6.2.3.2 反转轴方向
# 反转一个连续值轴的方向
+ scale_y_reverse() bp
9.6.2.3.3 设置和隐藏刻度标记
# Setting the tick marks on an axis This will show
# tick marks on every 0.25 from 1 to 10 The scale will
# show only the ones that are within range (3.50-6.25
# in this case)
+ scale_y_continuous(breaks = seq(1, 10, 1/4)) bp
# 刻度不平等变化
+ scale_y_continuous(breaks = c(4, 4.25, 4.5, 5, 6, 8)) bp
# 抑制标签和网格线
+ scale_y_continuous(breaks = NULL) bp
# Hide tick marks and labels (on Y axis), but keep the
# gridlines
+ theme(axis.ticks = element_blank(), axis.text.y = element_blank()) bp
9.6.2.3.4 轴刻度 log、sqrt 等转换
默认轴是线性坐标,我们也可以将它转换为 log、幂、根等等。
有两种办法可以转换一个轴,一是使用 scale 进行转换,另外是使用 coordinate 进行转换。使用前者需要在先弄好刻度和轴的范围之前转换,而使用后者则相反,需要在弄好刻度和轴范围之后转换。这将产生不太一样的显示效果,如下所示。
# 创建指数分布数据
set.seed(201)
<- 100
n <- data.frame(xval = (1:n + rnorm(n, sd = 5))/20, yval = 2 *
dat 2^((1:n + rnorm(n, sd = 5))/20))
# 创建常规的散点图
<- ggplot(dat, aes(xval, yval)) + geom_point()
sp sp
# log2 比例化(间隔相等)
library(scales) # 需要 scales 包
+ scale_y_continuous(trans = log2_trans()) sp
# log2 坐标转换,空间间隔不同
+ coord_trans(y = "log2") sp
在标度转换中,我们还可以指定刻度值,让它们显示指数。
+ scale_y_continuous(trans = log2_trans(), breaks = trans_breaks("log2",
sp function(x) 2^x), labels = trans_format("log2", math_format(2^.x)))
可以使用非常多的转换,参见 ?trans_new
查看所有可用转换的列表。如果你所需要的转换不在该列表上,可以自己写一个转换函数。
有一些非常便捷的函数:scale_y_log10()
和 scale_y_sqrt()
(有对应的 x 版本)。
set.seed(205)
<- 100
n <- data.frame(xval = (1:n + rnorm(n, sd = 5))/20,
dat10 yval = 10 * 10^((1:n + rnorm(n, sd = 5))/20))
<- ggplot(dat10, aes(xval, yval)) + geom_point()
sp10
# log10
+ scale_y_log10() sp10
# log10 转换,并设定指数标签
+ scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x),
sp10 labels = trans_format("log10", math_format(10^.x)))
9.6.2.3.5 x 与 y 轴固定的比例
设置 x 与 y 轴比例宽度也是可以的。
# x 范围 0-10, y 范围 0-30
set.seed(202)
<- data.frame(xval = runif(40, 0, 10), yval = runif(40,
dat 0, 30))
<- ggplot(dat, aes(xval, yval)) + geom_point()
sp
# 强制比例相等
+ coord_fixed() sp
# 相等的标度变化,让 x 的 1 个单位等同 y 的 3 个单位
+ coord_fixed(ratio = 1/3) sp
9.6.2.4 轴标签和文字格式化
设置和隐藏轴标签:
+ theme(axis.title.x = element_blank()) + # 移除 x 轴标签
bp ylab("Weight (Kg)") # 设置 y 轴标签
# 也可以通过标度设置
# 注意这里 x 轴标签的空间仍然保留
+ scale_x_discrete(name="") +
bp scale_y_continuous(name="Weight (Kg)")
改变字体、颜色、旋转刻度标签:
# 改变字体选项: X-axis label: bold, red, and 20
# points X-axis tick marks: rotate 90 degrees CCW,
# move to the left a bit (using vjust, since the
# labels are rotated), and 16 points
+ theme(axis.title.x = element_text(face = "bold", colour = "#990000",
bp size = 20), axis.text.x = element_text(angle = 90, vjust = 0.5,
size = 16))
9.6.2.5 刻度标签
你可能想将值显示为百分比、或美元、或科学计数法。这里可以使用格式器,它是一个可以改变文本的函数。
# 标签格式器
library(scales) # 需要 scales 包
+ scale_y_continuous(labels = percent) + scale_x_discrete(labels = abbreviate) # 在这个例子中它没作用 bp
连续标度格式器有 comma
、percent
、dollar
以及 scientific
。离散标度格式器有 abbreviate
、date_format
等。
有时你需要自己创建格式化函数。下面的函数可以显示时间格式为 HH:MM:SS。
# 自定义时间格式化函数
<- function(x) {
timeHMS_formatter <- floor(x/60)
h <- floor(x%%60)
m <- round(60 * (x%%1)) # Round to nearest second
s <- sprintf("%02d:%02d:%02d", h, m, s) # Format the strings as HH:MM:SS
lab <- gsub("^00:", "", lab) # Remove leading 00: if present
lab <- gsub("^0", "", lab) # Remove leading 0 if present
lab
}
+ scale_y_continuous(label = timeHMS_formatter) bp
9.6.2.6 隐藏网格线
隐藏网格线:
# 隐藏所有网格线
+ theme(panel.grid.minor = element_blank(), panel.grid.major = element_blank()) bp
# 仅隐藏次级网格线
+ theme(panel.grid.minor = element_blank()) bp
也可以仅隐藏水平或垂直网格线:
# 隐藏所有垂直网格线
+ theme(panel.grid.minor.x = element_blank(), panel.grid.major.x = element_blank()) bp
# 隐藏所有水平网格线
+ theme(panel.grid.minor.y = element_blank(), panel.grid.major.y = element_blank()) bp
9.7 图例
9.7.1 问题
你想用 ggplot2 修改图表中的图例。
9.7.2 方案
从带有默认选项的示例图开始:
library(ggplot2)
<- ggplot(data = PlantGrowth, aes(x = group, y = weight,
bp fill = group)) + geom_boxplot()
bp
9.7.2.1 去除图例
使用 guides(fill=FALSE)
, 用想要的颜色替代填充色.
你也可以用 theme()
移除图表中所有的图例。
# 删除特定美学的图例(填充)
+ guides(fill = FALSE)
bp #> Warning: `guides(<scale> = FALSE)` is deprecated.
#> Please use `guides(<scale> = "none")` instead.
# 在指定比例时也可以这样做
+ scale_fill_discrete(guide = FALSE)
bp #> Warning: It is deprecated to specify `guide = FALSE` to
#> remove a guide. Please use `guide = "none"` instead.
# 这将移除所有的图例
+ theme(legend.position = "none") bp
9.7.2.2 在图例中改变变量的顺序
这会将变量的顺序更改为 trt1,ctrl,trt2:
+ scale_fill_discrete(breaks = c("trt1", "ctrl", "trt2")) bp
根据指定颜色的方式,你可能必须使用不同的比例,如 scale_fill_manual()
, scale_colour_hue()
, scale_colour_manual()
, scale_shape_discrete()
, scale_linetype_discrete()
等。
9.7.2.3 反转图例中的条目顺序
反转图例顺序:
# 这两种方式等同:
+ guides(fill = guide_legend(reverse = TRUE)) bp
+ scale_fill_discrete(guide = guide_legend(reverse = TRUE)) bp
# 你也可以直接修改比例尺:
+ scale_fill_discrete(breaks = rev(levels(PlantGrowth$group))) bp
你可以使用不同的比例尺,如 scale_fill_manual()
, scale_colour_hue()
, scale_colour_manual()
, scale_shape_discrete()
, scale_linetype_discrete()
等,而不是 scale_fill_discrete()
。
9.7.2.4 隐藏图例标题
这将隐藏图例标题:
# 为了填充的图例移除标题
+ guides(fill = guide_legend(title = NULL)) bp
# 为了所有的图例移除标题
+ theme(legend.title = element_blank()) bp
9.7.2.5 修改图例标题和标签的文字
有两种方法可以更改图例标题和标签。 第一种方法是告诉 scale 使用具有不同的标题和标签。 第二种方法是更改数据框,使因子具有所需的形式。
9.7.2.5.1 使用比例尺
图例可能由 fill
, colour
, linetype
, shape
或其他因素所介导.
9.7.2.5.2 使用填充和颜色
因为图例中的变量 group
被映射到颜色 fill
,所以必须使用 scale_fill_xxx
,其中 xxx
是将 group
的每个因子级别映射到不同颜色的方法。 默认设置是在每个因子级别的色轮上使用不同的色调,但也可以手动指定每个级别的颜色。
+ scale_fill_discrete(name = "Experimental\nCondition") bp
+ scale_fill_discrete(name = "Experimental\nCondition",
bp breaks = c("ctrl", "trt1", "trt2"), labels = c("Control",
"Treatment 1", "Treatment 2"))
# 使用手动刻度而不是色调
+ scale_fill_manual(values = c("#999999", "#E69F00",
bp "#56B4E9"), name = "Experimental\nCondition", breaks = c("ctrl",
"trt1", "trt2"), labels = c("Control", "Treatment 1",
"Treatment 2"))
请注意,这并未更改 x 轴标签。 有关如何修改轴标签的信息,请参见坐标轴。
如果使用折线图,则可能需要使用 scale_colour_xxx()
或 scale_shape_xxx()
而不是 scale_fill_xxx()
。 颜色映射到线条和点的颜色,而填充映射到区域填充的颜色。 形状映射到点的形状。
我们将在这里为线图使用不同的数据集,因为 PlantGrowth 数据集不适用于折线图。
# 一个不同的数据集
<- data.frame(sex = factor(c("Female", "Female", "Male",
df1 "Male")), time = factor(c("Lunch", "Dinner", "Lunch",
"Dinner"), levels = c("Lunch", "Dinner")), total_bill = c(13.53,
16.81, 16.24, 17.42))
# 基本的图表
<- ggplot(data = df1, aes(x = time, y = total_bill, group = sex,
lp shape = sex)) + geom_line() + geom_point()
lp
# 更改图例
+ scale_shape_discrete(name = "Payer", breaks = c("Female",
lp "Male"), labels = c("Woman", "Man"))
如果你同时使用 colour
和 shape
,它们都需要给出比例规格。 否则会有两个独立的图例。
# 指定颜色和形状
<- ggplot(data = df1, aes(x = time, y = total_bill,
lp1 group = sex, shape = sex, colour = sex)) + geom_line() +
geom_point()
lp1
# 如果你仅仅指定颜色,将会发生
+ scale_colour_discrete(name = "Payer", breaks = c("Female",
lp1 "Male"), labels = c("Woman", "Man"))
# 指定的颜色和形状
+ scale_colour_discrete(name = "Payer", breaks = c("Female",
lp1 "Male"), labels = c("Woman", "Man")) + scale_shape_discrete(name = "Payer",
breaks = c("Female", "Male"), labels = c("Woman", "Man"))
9.7.2.5.3 比例尺的种类
比例尺有很多种。 它们采用「scale_xxx_yyy」的形式。 以下是一些常用的 xxx
和 yyy
值:
xxx | 描述 |
---|---|
colour | 线和点的颜色 |
fill | 填充区域的颜色 (比如:柱状图) |
线条类型 | Solid/dashed/dotted lines |
形状 | 点的形状 |
大小 | Size of points |
alpha | 不透明度/透明度 |
yyy | 描述 |
---|---|
hue | 色轮的颜色相同 |
manual | 手动指定的值(例如,颜色,点形状,线型) |
gradient | 颜色渐变 |
grey | Shades of grey |
discrete | 不连续的值 (比如颜色,点的形状,线条类型,点的大小 |
continuous | 连续的值(透明度,颜色,点的大小) |
9.7.2.6 更改数据框中的因子
更改图例标题和标签的另一种方法是直接修改数据框。
<- PlantGrowth # 把数据复制到新的数据框
pg # 重命名列中的列和值
levels(pg$group)[levels(pg$group) == "ctrl"] <- "Control"
levels(pg$group)[levels(pg$group) == "trt1"] <- "Treatment 1"
levels(pg$group)[levels(pg$group) == "trt2"] <- "Treatment 2"
names(pg)[names(pg) == "group"] <- "Experimental Condition"
# 查看最终结果的几行
head(pg)
#> weight Experimental Condition
#> 1 4.17 Control
#> 2 5.58 Control
#> 3 5.18 Control
#> 4 6.11 Control
#> 5 4.50 Control
#> 6 4.61 Control
# 画图
ggplot(data = pg, aes(x = `Experimental Condition`, y = weight,
fill = `Experimental Condition`)) + geom_boxplot()
图例标题“实验条件”很长,如果它被分成两行可能看起来更好,但是这种方法效果不好,因为你必须在列的名称中加上一个换行符。另一种方法,有尺度,通常是更好的方法。
另请注意使用反引号而不是引号。由于变量名中的空格,它们是必需的。
9.7.2.7 修改图例标题和标签的外观
# 题目外观
+ theme(legend.title = element_text(colour = "blue",
bp size = 16, face = "bold"))
# 标签外观
+ theme(legend.text = element_text(colour = "blue", size = 16,
bp face = "bold"))
9.7.2.8 修改图例框
默认情况下,图例周围没有框。 添加框并修改其属性:
+ theme(legend.background = element_rect()) bp
+ theme(legend.background = element_rect(fill = "gray90",
bp size = 0.5, linetype = "dotted"))
9.7.2.9 改变图例位置
将图例位置放在绘图区域外(左/右/上/下):
+ theme(legend.position = "top") bp
也可以将图例定位在绘图区域内。 请注意,下面的数字位置是相对于整个区域的,包括标题和标签,而不仅仅是绘图区域。
# 将图例放在图表中,其中 x,y 为 0, 0(左下角)到 1,
# 1(右上角)
+ theme(legend.position = c(0.5, 0.5)) bp
# 设置图例的「锚点」(左下角为 0, 0; 右上角为 1, 1)
# 将图例框的左下角放在图的左下角
+ theme(legend.justification = c(0, 0), legend.position = c(0,
bp 0))
# 将图例框的右下角放在图表的右下角
+ theme(legend.justification = c(1, 0), legend.position = c(1,
bp 0))
9.7.2.10 隐藏在图例中的斜线
如果使用轮廓制作条形图(通过设置 color = “black”),它将通过图例中的颜色绘制斜线。 没有内置的方法来删除斜杠,但可以覆盖它们。
# 没有边缘线
ggplot(data = PlantGrowth, aes(x = group, fill = group)) +
geom_bar()
# 添加轮廓,但图例中会出现斜线
ggplot(data = PlantGrowth, aes(x = group, fill = group)) +
geom_bar(colour = "black")
# 隐藏斜线:首先绘制没有轮廓的条形图并添加图例,
# 然后用轮廓再次绘制条形图,但带有空白图例.
ggplot(data = PlantGrowth, aes(x = group, fill = group)) +
geom_bar() + geom_bar(colour = "black", show.legend = FALSE)
9.7.3 注意
更多信息,请阅读 ggplot2-图例属性。
9.8 线条
9.8.1 问题
你想要把线条加到图上。
9.8.2 方案
9.8.2.1 使用一个连续轴和一个分类轴
# 一些样本数据
<- read.table(header = TRUE, text = "
dat cond result
control 10
treatment 11.5
")
library(ggplot2)
9.8.2.1.1 一条线段
这些使用 geom_hline()
,因为y轴是连续的,但如果x轴是连续的,也可以使用 geom_vline()
(带有 xintercept
)。
# 基本柱状条
<- ggplot(dat, aes(x = cond, y = result)) + geom_bar(position = position_dodge(),
bp stat = "identity")
bp
# 添加水平线
+ geom_hline(aes(yintercept = 12)) bp
# 使线条变红并变为虚线
+ geom_hline(aes(yintercept = 12), colour = "#990000",
bp linetype = "dashed")
9.8.2.1.2 每个分类值的单独行
要为每个条形成单独的行,请使用 geom_errorbar()
。
# 为每个条形绘制单独的线条。 首先添加另一列到目前为止
$hline <- c(9, 12)
dat
dat#> cond result hline
#> 1 control 10.0 9
#> 2 treatment 11.5 12
# 需要重新指定 bp,因为数据已经改变
<- ggplot(dat, aes(x = cond, y = result)) + geom_bar(position = position_dodge(),
bp stat = "identity")
# 为每个柱状图画分开的线条
+ geom_errorbar(aes(ymax = hline, ymin = hline), colour = "#AA0000") bp
# 让线条更细一点
+ geom_errorbar(width = 0.5, aes(ymax = hline, ymin = hline),
bp colour = "#AA0000")
# 即使我们从第二个数据框获得 hline
# 值,也可以得到相同的结果 使用 hline 定义数据框
<- data.frame(cond = c("control", "treatment"),
dat_hlines hline = c(9, 12))
dat_hlines#> cond hline
#> 1 control 9
#> 2 treatment 12
# 柱状图形来自 dat,但是线条来自 dat_hlines
+ geom_errorbar(data = dat_hlines, aes(y = NULL, ymax = hline,
bp ymin = hline), colour = "#AA0000")
9.8.2.1.3 分组栏上的线条
可以在分组条上添加线条。 在这个例子中,实际上有四行(hline
的每个条目一行),但它看起来像两个,因为它们是相互重叠的。 我不认为可以避免这种情况,但它不会导致任何问题。
<- read.table(header = TRUE, text = "
dat cond group result hline
control A 10 9
treatment A 11.5 12
control B 12 9
treatment B 14 12
")
dat#> cond group result hline
#> 1 control A 10.0 9
#> 2 treatment A 11.5 12
#> 3 control B 12.0 9
#> 4 treatment B 14.0 12
# 定义基本柱状图
<- ggplot(dat, aes(x = cond, y = result, fill = group)) +
bp geom_bar(position = position_dodge(), stat = "identity")
bp
# 误差线相互绘制 - 有四个但看起来像两个
+ geom_errorbar(aes(ymax = hline, ymin = hline), linetype = "dashed") bp
9.8.2.2 各个组合柱状图上的线条
即使在分组时,也可以在每个单独的条上划线。
<- read.table(header = TRUE, text = "
dat cond group result hline
control A 10 11
treatment A 11.5 12
control B 12 12.5
treatment B 14 15
")
# 定义基本条形图
<- ggplot(dat, aes(x = cond, y = result, fill = group)) +
bp geom_bar(position = position_dodge(), stat = "identity")
bp
+ geom_errorbar(aes(ymax = hline, ymin = hline), linetype = "dashed",
bp position = position_dodge())
9.8.2.3 有两个连续轴
样本数据如下:
<- read.table(header = TRUE, text = "
dat cond xval yval
control 11.5 10.8
control 9.3 12.9
control 8.0 9.9
control 11.5 10.1
control 8.6 8.3
control 9.9 9.5
control 8.8 8.7
control 11.7 10.1
control 9.7 9.3
control 9.8 12.0
treatment 10.4 10.6
treatment 12.1 8.6
treatment 11.2 11.0
treatment 10.0 8.8
treatment 12.9 9.5
treatment 9.1 10.0
treatment 13.4 9.6
treatment 11.6 9.8
treatment 11.5 9.8
treatment 12.0 10.6
")
library(ggplot2)
9.8.2.3.1 基础线条
# 基本的散点图
<- ggplot(dat, aes(x = xval, y = yval, colour = cond)) +
sp geom_point()
# 添加一个水平线条
+ geom_hline(aes(yintercept = 10)) sp
# 添加红色虚线垂直线
+ geom_hline(aes(yintercept = 10)) + geom_vline(aes(xintercept = 11.5),
sp colour = "#BB0000", linetype = "dashed")
9.8.2.3.2 画线为平均值
还可以按一些变量分组计算每个数据子集的平均值。 组别必须计算并存储在单独的列中,最简单的方法是使用 dplyr 包。请注意,该行的 y 范围由数据确定。
library(dplyr)
#>
#> 载入程辑包:'dplyr'
#> The following objects are masked from 'package:plyr':
#>
#> arrange, count, desc, failwith, id, mutate,
#> rename, summarise, summarize
#> The following object is masked from 'package:car':
#>
#> recode
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
<- dat %>%
lines group_by(cond) %>%
summarise(x = mean(xval), ymin = min(yval), ymax = max(yval))
# 为每组的平均 xval 添加彩色线条
<- sp + geom_hline(aes(yintercept = 10)) + geom_linerange(aes(x = x,
sp y = NULL, ymin = ymin, ymax = ymax), data = lines)
sp
9.8.2.3.3 在分面使用线条
一般来说,如果你加一条线,它将出现在所有的分面上。
# 分面,基于cond
<- sp + facet_grid(. ~ cond)
spf spf
# 用相同的值在所有的分面上画水平线
+ geom_hline(aes(yintercept = 10)) spf
如果你希望不同的线条出现在不同的方面,有两个选项。 一种是创建具有所需线条值的新数据框。 另一种选择(控制更有限)是在 geom_line()
中设定 stat
和 xintercept
。
<- data.frame(cond = c("control", "treatment"),
dat_vlines xval = c(10, 11.5))
dat_vlines#> cond xval
#> 1 control 10.0
#> 2 treatment 11.5
+ geom_hline(aes(yintercept = 10)) + geom_vline(aes(xintercept = xval),
spf data = dat_vlines, colour = "#990000", linetype = "dashed")
+ geom_hline(aes(yintercept = 10)) + geom_linerange(aes(x = x,
spf y = NULL, ymin = ymin, ymax = ymax), data = lines)
9.9 分面
9.9.1 问题
你想要根据一个或多个变量对数据进行分割并且绘制出该数据所有的子图。
9.9.2 方案
9.9.2.1 样本数据
以下例子将使用 reshape2 包中的 tips
数据集
library(reshape2)
# 查看头几行数据
head(tips)
#> total_bill tip sex smoker day time size
#> 1 16.99 1.01 Female No Sun Dinner 2
#> 2 10.34 1.66 Male No Sun Dinner 3
#> 3 21.01 3.50 Male No Sun Dinner 3
#> 4 23.68 3.31 Male No Sun Dinner 2
#> 5 24.59 3.61 Female No Sun Dinner 4
#> 6 25.29 4.71 Male No Sun Dinner 4
根据小费 (tip) 占总账单 (total_bill) 的百分比绘制散点图
library(ggplot2)
<- ggplot(tips, aes(x = total_bill, y = tip/total_bill)) +
sp geom_point(shape = 1)
sp
9.9.2.2 facet_grid
根据一个或多个变量对数据进行分割,生成的子图按照水平或垂直的方向进行排列。这一功能是通过赋予 facet_grid()
函数一个 vertical ~ horizontal
公式来实现的(这里所说的「公式」是 R 中的一种数据结构,而不是数学意义上的公式)。
# 根据 'sex' 按垂直方向分割
+ facet_grid(sex ~ .) sp
# 根据 'sex' 按水平方向分割。
+ facet_grid(. ~ sex) sp
# 垂直方向以 'sex' 分割,水平方向以 'day' 分割。
+ facet_grid(sex ~ day) sp
9.9.2.3 facet_wrap
除了能够根据单个变量在水平或垂直方向上对图进行分面,facet_wrap()
函数可以通过设置特定的行数或列数,让子图排列到一起。此时每个图像的上方都会有标签。
# 以变量 `day` 进行水平分面,分面的行数为2。
+ facet_wrap(~day, ncol = 2) sp
9.9.2.4 修改分面标签的外观
+ facet_grid(sex ~ day) + theme(strip.text.x = element_text(size = 8,
sp angle = 75), strip.text.y = element_text(size = 12,
face = "bold"), strip.background = element_rect(colour = "red",
fill = "#CCCCFF"))
9.9.2.5 修改分面标签的文本
修改分面标签内容有两种方法。最简单的方法是为原来的名字匹配一个新的名字向量。例如,对数据中 sex
的类别进行重新定义 Female==>Women 和 Male==>Men:
<- c(Female = "Women", Male = "Men")
labels + facet_grid(. ~ sex, labeller = labeller(sex = labels)) sp
另一个方法就是直接在数据框中修改,将你想要显示的标签赋值给相应的数据:
<- tips
tips2 levels(tips2$sex)[levels(tips2$sex) == "Female"] <- "Women"
levels(tips2$sex)[levels(tips2$sex) == "Male"] <- "Men"
head(tips2, 3)
#> total_bill tip sex smoker day time size
#> 1 16.99 1.01 Women No Sun Dinner 2
#> 2 10.34 1.66 Men No Sun Dinner 3
#> 3 21.01 3.50 Men No Sun Dinner 3
<- ggplot(tips2, aes(x = total_bill, y = tip/total_bill)) +
sp2 geom_point(shape = 1)
+ facet_grid(. ~ sex) sp2
两种方法都能得到相同的结果。
labeller()
可以通过设定不同的 函数
来处理输入的字符向量。比方说 Hmisc
包里的 capitalize
函数可以将字符串的首字母变成大写。我们也可以这样来自定义函数,如下所示,将字符串中的字母倒序:
# 对每个字符向量进行倒序:
<- function(strings) {
reverse <- strsplit(strings, "")
strings vapply(strings, function(x) {
paste(rev(x), collapse = "")
FUN.VALUE = character(1))
},
}+ facet_grid(. ~ sex, labeller = labeller(sex = reverse)) sp
9.9.2.6 设置标度
一般而言,每幅图的坐标轴范围都是固定不变的,也就是说每幅图都拥有相同的尺寸和范围。你可以通过将 scales
设置为 free
,free_x
或 free_y
来改变坐标轴范围。
# 描绘一个 total_bill 的柱状图
<- ggplot(tips, aes(x = total_bill)) + geom_histogram(binwidth = 2,
hp colour = "white")
# 根据性别和是否吸烟进行分面
+ facet_grid(sex ~ smoker) hp
# 在同样的情况下设定 scales='free_y' (y 轴自由标度)
+ facet_grid(sex ~ smoker, scales = "free_y") hp
# 画布的缩放比例不变,但各分面的范围有所改变,因此每个分面的物理大小都不一致
+ facet_grid(sex ~ smoker, scales = "free", space = "free") hp
9.10 多图
9.10.1 问题
你想把多个图形放到同一个页面中。
9.10.2 方案
最简单的方法就是使用 multiplot()
函数。
multiplot()
函数可以将任意数量的图像对象作为参数,或者可以构建一个图像对象列表传递到该函数的 plotlist
参数中。
# 多图功能 ggplot 对象可以直接放入 `…`
# 中,也可以传递到 `plotlist` 里(这里的 ggplot
# 对象以列表形式存在) - cols: 图像的列数 - layout:
# 用来指定布局的一组矩阵。当其存在时,可以忽略 `cols`
# 参数。 假设 layout 参数是 matrix(c(1,2,3,3), nrow=2,
# byrow = TRUE),
# 那么第一幅图像会位于左上方,第二幅图会在右上方,而
# 第三幅图会占据整个下方。
<- function(..., plotlist = NULL, file, cols = 1,
multiplot layout = NULL) {
library(grid)
# 从参数 `…`中建立一个列表然后 plotlist
<- c(list(...), plotlist)
plots
= length(plots)
numPlots # 假如 layout 是 NULL, 那么可以用 `cols` 来定义布局
if (is.null(layout)) {
# 创建面板 ncol: 图像的列数 nrow:
# 根据上述给定的列数,计算所需要的行数
<- matrix(seq(1, cols * ceiling(numPlots/cols)),
layout ncol = cols, nrow = ceiling(numPlots/cols))
}if (numPlots == 1) {
print(plots[[1]])
else {
} # 创建页面
grid.newpage()
pushViewport(viewport(layout = grid.layout(nrow(layout),
ncol(layout))))
# 让每一幅图像排列在正确的位置
for (i in 1:numPlots) {
# 获取包含这一子图所在区域的坐标 matrix i,j
<- as.data.frame(which(layout == i,
matchidx arr.ind = TRUE))
print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row,
layout.pos.col = matchidx$col))
}
} }
如果它不能满足你的需求,你可以将其复制下来然后作出适当的修改。
首先,构建并保存图像但不需要对它们进行渲染,这些图像的细节并不重要。你只需要将这些图像对象储存为变量。
# 以下例子使用的是 ggplot2 包中自带的 Chickweight
# 数据集 第一幅图像
<- ggplot(ChickWeight, aes(x = Time, y = weight, colour = Diet,
p1 group = Chick)) + geom_line() + ggtitle("Growth curve for individual chicks")
# 第二幅图像
<- ggplot(ChickWeight, aes(x = Time, y = weight, colour = Diet)) +
p2 geom_point(alpha = 0.3) + geom_smooth(alpha = 0.2, size = 1) +
ggtitle("Fitted growth curve per diet")
# 第三幅图像
<- ggplot(subset(ChickWeight, Time == 21), aes(x = weight,
p3 colour = Diet)) + geom_density() + ggtitle("Final weight, by diet")
# 第四幅图像
<- ggplot(subset(ChickWeight, Time == 21), aes(x = weight,
p4 fill = Diet)) + geom_histogram(colour = "black", binwidth = 50) +
facet_grid(Diet ~ .) + ggtitle("Final weight, by diet") +
theme(legend.position = "none") #为了避免冗余,这里不添加图例
这些图像都构建好了后,我们可以用 multiplot()
对它们进行渲染。下面将这些图形分成两列进行展示:
multiplot(p1, p2, p3, p4, cols = 2)
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
9.11 颜色
9.11.1 问题
你想在图表中用 ggplot2 添加颜色。
9.11.2 方案
在 ggplot2 中设置颜色,对相互区分不同变量会有些困难,因为这些颜色有一样的亮度,且对色盲者不太友好。一个比较好的通用解决方案是使用对色盲友好的颜色。
9.11.2.1 样本数据
这两个数据集将用来产生下面的图表:
# 两个变量
<- read.table(header = TRUE, text = "
df cond yval
A 2
B 2.5
C 1.6
")
# 三个变量
<- read.table(header = TRUE, text = "
df2 cond1 cond2 yval
A I 2
A J 2.5
A K 1.6
B I 2.2
B J 2.4
B K 1.2
C I 1.7
C J 2.3
C K 1.9
")
9.11.2.2 简单的颜色设置
有颜色的线条和点可以直接用 colour = "red"
, 用颜色名称代替 "red"
。填充的对象的颜色,如柱状条,可以用 fill="red"
来进行设置。
如果你想用任何其他非常规颜色,用十六进位码来设置颜色更容易,比如 #FF6699
。
library(ggplot2)
# 设置:黑色柱状条
ggplot(df, aes(x=cond, y=yval)) + geom_bar(stat="identity")
# 柱状条外用红色边线
ggplot(df, aes(x=cond, y=yval)) + geom_bar(stat="identity", colour="#FF9999")
# 红色填充,黑色边线
ggplot(df, aes(x=cond, y=yval)) + geom_bar(stat="identity", fill="#FF9999", colour="black")
# 标准黑色线条和点
ggplot(df, aes(x=cond, y=yval)) +
geom_line(aes(group=1)) +
geom_point(size=3)
# 蓝黑色线条,红色点
ggplot(df, aes(x=cond, y=yval)) +
geom_line(aes(group=1), colour="#000099") + # 蓝线
geom_point(size=3, colour="#CC0000") # 红点
9.11.2.3 将变量值映射到颜色
不用全局改变颜色,你可以将变量映射到颜色——换言之,通过把颜色放到 aes()
函数中,可以设置条件性变量。
# 柱状条: x 和填充都依赖于cond2
ggplot(df, aes(x=cond, y=yval, fill=cond)) + geom_bar(stat="identity")
# 其他数据集的柱状条;填充依赖于cond2
ggplot(df2, aes(x=cond1, y=yval)) +
geom_bar(aes(fill=cond2), # 填充依赖于cond2
stat="identity",
colour="black", # 所有都是黑色轮廓线
position=position_dodge()) # 把线条并排放置而非堆叠
# 线和点;颜色依赖于cond2
ggplot(df2, aes(x=cond1, y=yval)) +
geom_line(aes(colour=cond2, group=cond2)) + # 颜色分组都依赖于cond2
geom_point(aes(colour=cond2), # 颜色依赖于cond2
size=3) # 更大的点,不同的形状
# 以上操作等价; 但把 "colour=cond2" 移到全局的映射用aes()
# ggplot(df2, aes(x=cond1, y=yval, colour=cond2)) +
# geom_line(aes(group=cond2)) +
# geom_point(size=3)
9.11.2.4 对色盲友好的颜色
下面这些是对色盲友好的颜色色板,一个用灰色,一个用黑色:
为了用 ggplot2, 我们在一个变量里储存颜色色板,然后之后调用。
# 灰色的颜色色板:
<- c("#999999", "#E69F00", "#56B4E9", "#009E73",
cbPalette "#F0E442", "#0072B2", "#D55E00", "#CC79A7")
# 黑色的颜色色板k:
<- c("#000000", "#E69F00", "#56B4E9", "#009E73",
cbbPalette "#F0E442", "#0072B2", "#D55E00", "#CC79A7")
# 为了填充颜色,加
scale_fill_manual(values = cbPalette)
#> <ggproto object: Class ScaleDiscrete, Scale, gg>
#> aesthetics: fill
#> axis_order: function
#> break_info: function
#> break_positions: function
#> breaks: waiver
#> call: call
#> clone: function
#> dimension: function
#> drop: TRUE
#> expand: waiver
#> get_breaks: function
#> get_breaks_minor: function
#> get_labels: function
#> get_limits: function
#> guide: legend
#> is_discrete: function
#> is_empty: function
#> labels: waiver
#> limits: NULL
#> make_sec_title: function
#> make_title: function
#> map: function
#> map_df: function
#> n.breaks.cache: NULL
#> na.translate: TRUE
#> na.value: grey50
#> name: waiver
#> palette: function
#> palette.cache: NULL
#> position: left
#> range: <ggproto object: Class RangeDiscrete, Range, gg>
#> range: NULL
#> reset: function
#> train: function
#> super: <ggproto object: Class RangeDiscrete, Range, gg>
#> rescale: function
#> reset: function
#> scale_name: manual
#> train: function
#> train_df: function
#> transform: function
#> transform_df: function
#> super: <ggproto object: Class ScaleDiscrete, Scale, gg>
# 为了在点线中使用颜色,加
scale_colour_manual(values = cbPalette)
#> <ggproto object: Class ScaleDiscrete, Scale, gg>
#> aesthetics: colour
#> axis_order: function
#> break_info: function
#> break_positions: function
#> breaks: waiver
#> call: call
#> clone: function
#> dimension: function
#> drop: TRUE
#> expand: waiver
#> get_breaks: function
#> get_breaks_minor: function
#> get_labels: function
#> get_limits: function
#> guide: legend
#> is_discrete: function
#> is_empty: function
#> labels: waiver
#> limits: NULL
#> make_sec_title: function
#> make_title: function
#> map: function
#> map_df: function
#> n.breaks.cache: NULL
#> na.translate: TRUE
#> na.value: grey50
#> name: waiver
#> palette: function
#> palette.cache: NULL
#> position: left
#> range: <ggproto object: Class RangeDiscrete, Range, gg>
#> range: NULL
#> reset: function
#> train: function
#> super: <ggproto object: Class RangeDiscrete, Range, gg>
#> rescale: function
#> reset: function
#> scale_name: manual
#> train: function
#> train_df: function
#> transform: function
#> transform_df: function
#> super: <ggproto object: Class ScaleDiscrete, Scale, gg>
这个颜色集来源于网站: http://jfly.iam.u-tokyo.ac.jp/color/。
9.11.2.5 颜色选择
默认情况下,离散比例的颜色围绕 HSL 色环均匀分布。例如,如果有两种颜色,那么它们将从圆圈上的相对点中选择; 如果有三种颜色,它们在色环上将相隔 120° 等等。用于不同级别的颜色如下所示:
默认颜色选择使用 scale_fill_hue()
和 scale_colour_hue()
。 例如,在以下情况下添加这些命令是多余的:
# 这两个是等价的; 默认使用scale_fill_hue()
ggplot(df, aes(x = cond, y = yval, fill = cond)) + geom_bar(stat = "identity")
# ggplot(df, aes(x=cond, y=yval, fill=cond)) +
# geom_bar(stat='identity') + scale_fill_hue()
# 这两个是等价的; 默认使用scale_colour_hue()
ggplot(df, aes(x = cond, y = yval, colour = cond)) + geom_point(size = 2)
# ggplot(df, aes(x=cond, y=yval, colour=cond)) +
# geom_point(size=2) + scale_colour_hue()
9.11.2.6 设置亮度和饱和度(色度)
虽然 scale_fill_hue()
和 scale_colour_hue()
在上面是多余的,但是当你想要改变默认值时,可以使用它们,比如改变亮度或色度。
# 使用 luminance = 45, 而不是默认 65
ggplot(df, aes(x = cond, y = yval, fill = cond)) + geom_bar(stat = "identity") +
scale_fill_hue(l = 40)
# 从 100 到 50 减少饱和度(亮度), 增加亮度
ggplot(df, aes(x = cond, y = yval, fill = cond)) + geom_bar(stat = "identity") +
scale_fill_hue(c = 45, l = 80)
# 注意:使用 scale_colour_hue() 设置线和点
亮度为 45 时的颜色图表:
9.11.2.7 调色板:Color Brewer
你还可以使用其他颜色标度,例如从 RColorBrewer 包中获取。 请参阅下面的 RColorBrewers 调色板图表。
ggplot(df, aes(x = cond, y = yval, fill = cond)) + geom_bar(stat = "identity") +
scale_fill_brewer()
ggplot(df, aes(x = cond, y = yval, fill = cond)) + geom_bar(stat = "identity") +
scale_fill_brewer(palette = "Set1")
ggplot(df, aes(x = cond, y = yval, fill = cond)) + geom_bar(stat = "identity") +
scale_fill_brewer(palette = "Spectral")
# 注意: 使用 scale_colour_brewer() 设置点和线条
9.11.2.8 调色板:手动定义
最后,你可以使用 scale_fill_manual()
定义自己的颜色集。 有关选择特定颜色的帮助,请参阅下面的十六进制代码表。
ggplot(df, aes(x = cond, y = yval, fill = cond)) + geom_bar(stat = "identity") +
scale_fill_manual(values = c("red", "blue", "green"))
ggplot(df, aes(x = cond, y = yval, fill = cond)) + geom_bar(stat = "identity") +
scale_fill_manual(values = c("#CC6666", "#9999CC", "#66CC99"))
# 注意:使用 scale_colour_manual() 设置线条和点
9.11.2.9 连续颜色
# 产生一些数据
set.seed(133)
<- data.frame(xval = rnorm(50), yval = rnorm(50))
df # 依赖 yval设置颜色
ggplot(df, aes(x = xval, y = yval, colour = yval)) + geom_point()
# 使用不同的渐变
ggplot(df, aes(x = xval, y = yval, colour = yval)) + geom_point() +
scale_colour_gradientn(colours = rainbow(4))
9.11.2.10 比色图表
9.11.2.11 十六进制色码图
颜色可以指定为十六进制 RGB 三元组合,例如 #0066CC
。 前两位数字是红色,接下来的两位是绿色,最后两位是蓝色。 每个值的范围从 00 到 FF,以十六进制(base-16)表示,在 base-10 中等于 0 和 255。 例如,在下表中,#FFFFFF
为白色,#990000
为深红色。
色码图来源于 http://www.visibone.com