本系列主要介绍R语言ggplot2的使用
参考资料:
ggplot2: Elegant Graphics for Data Analysis
在我们数据可视化时,一个好的注释能使图形更好的传递信息,ggplot2本身和一些扩展包都提供了一些给图形加注释的技巧
在我们画图时,需要给图形、坐标轴和图例加标题。在ggplot2中,我们可以使用labs()函数完成这一系列的设置
我们还是以ggplot2中的mpg数据集为例
library(ggplot2)
head(mpg)
manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class |
---|---|---|---|---|---|---|---|---|---|---|
audi | a4 | 1.8 | 1999 | 4 | auto(l5) | f | 18 | 29 | p | compact |
audi | a4 | 1.8 | 1999 | 4 | manual(m5) | f | 21 | 29 | p | compact |
audi | a4 | 2.0 | 2008 | 4 | manual(m6) | f | 20 | 31 | p | compact |
audi | a4 | 2.0 | 2008 | 4 | auto(av) | f | 21 | 30 | p | compact |
audi | a4 | 2.8 | 1999 | 6 | auto(l5) | f | 16 | 26 | p | compact |
audi | a4 | 2.8 | 1999 | 6 | manual(m5) | f | 18 | 26 | p | compact |
下面我们要绘制发动机排量(displ)和每加仑里程(hwy)散点图,并定义颜色为cyl
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(col=factor(cyl)))+#因为cyl是数值新数据,在这里我们将其转换为分类型数据
labs(
x='发动机排放量(displ)/L',
y='每加仑行驶里程',
colour = "Number of cylinders",
title = "Mileage by engine size and cylinders",
subtitle = '数据来源:http://fueleconomy.gov'
)
labs()一般是增加文本数据,但也可以使用quote()
函数定义数学表达式,下面这个例子可以说明
values <- seq(from = -2, to = 2, by = .01)
df <- data.frame(x = values, y = exp(values ))
ggplot(df, aes(x, y)) +
geom_line() +
labs(y = quote(f(x) == e^x))
同时,labs还支持markdown语法,例如有时我们需要加粗或者倾斜某些文字,我们需要指定相关theme为ggtext:element_markdown
library(ggtext)
df <- data.frame(x = 1:3, y = 1:3)
p <- ggplot(df, aes(x, y)) +
geom_point() +
labs(x = "x轴**加粗**")
p
p + theme(axis.title.x = ggtext::element_markdown())
向绘图中添加文本是最常见的注释形式之一。在标记异常值和其他重要点非常有用。使用geom_text()来添加指定文本,在指定的x和y位置添加标签文本。
family
美学设置字体的款式,例如sans(默认),serif,momo等,来看下面这个例子df <- data.frame(x=1, y = 3:1, family = c('sans','serif','mono'))
ggplot(df,aes(x,y))+
geom_text(aes(label = family,family=family))
df <- data.frame(x=1, y = 3:1, face = c('plain','bold','italic'))
ggplot(df,aes(x,y))+
geom_text(aes(label = face,fontface=face))
hjust
可以选择(‘left’,‘right’,‘center’,‘inward’,‘outward’)vjust
选择(‘bottom’,‘middle’,‘top’,‘inward’,‘outward’),默认是居中对齐,但有时候我们需要调整。最常用的是inward。它将文本对齐到图形内部,从而确保标签保持在绘图限制内:df <- data.frame(
x = c(1, 1, 2, 2, 1.5),
y = c(1, 2, 1, 2, 1.5),
text = c(
"bottom-left", "bottom-right",
"top-left", "top-right", "center"
)
)
ggplot(df, aes(x, y)) +
geom_text(aes(label = text))
ggplot(df, aes(x, y)) +
geom_text(aes(label = text), vjust = "inward", hjust = "inward")
从上图可以看出使用inword可以叫四个角落的文本全部保证在图形内部显示
size
来控制文本的大小,但是ggplot2默认的单位是mm,和大多数默认的pt不同,1pt = 72.27/25.4mm
angle
指定文本的旋转角度(以度为单位)。
通常我们希望标记图形中的点,为了避免文本与点重叠,我们可以使用nudeg_x和nudeg_y来设置文本偏移
df <- data.frame(trt = c("a", "b", "c"), resp = c(1.2, 3.4, 2.5))
ggplot(df, aes(resp, trt)) +
geom_point() +
geom_text(aes(label = paste('(',resp,')')) ,nudge_y = -0.15) +
xlim(1, 3.6)
ggplot(mpg, aes(displ, hwy)) +
geom_text(aes(label = model)) +
xlim(1, 8)
ggplot(mpg, aes(displ, hwy)) +
geom_text(aes(label = model), check_overlap = TRUE) +
xlim(1, 8)
虽然看起来这个功能似乎没有什么用,但是当我们是按优先级顺序对数据进行排序,结果是一个带标签的图形,那么显示的就是重要的树数据点
label <- data.frame(
waiting = c(55, 80),
eruptions = c(2, 4.3),
label = c("peak one", "peak two")
)
ggplot(faithfuld, aes(waiting, eruptions)) +
geom_raster(aes(fill = density)) +
geom_label(data = label, aes(label = label))
给数据加标签需要注意一下问题:
ggrepel
包中的geom_text_repel(),会自动避免重叠。ggplot(mpg, aes(displ, hwy)) + geom_point(colour = "red") +
ggrepel::geom_text_repel(data = mpg, aes(label = class))
Warning message:
"ggrepel: 164 unlabeled data points (too many overlaps). Consider increasing max.overlaps"
上述我们用文本标记单个点是一种重要的注释,ggplot2软件包还提供了其他方法,可以使用与显示数据相同的几何图形对绘图进行注释。具体如下:
下面我们距离说明一下这些方法的用法,本案例使用economics数据,我们先来看一下economics数据包括哪些变量
head(economics)
date | pce | pop | psavert | uempmed | unemploy |
---|---|---|---|---|---|
1967-07-01 | 506.7 | 198712 | 12.6 | 4.5 | 2944 |
1967-08-01 | 509.8 | 198911 | 12.6 | 4.7 | 2945 |
1967-09-01 | 515.6 | 199113 | 11.9 | 4.6 | 2958 |
1967-10-01 | 512.2 | 199311 | 12.9 | 4.9 | 3143 |
1967-11-01 | 517.4 | 199498 | 12.8 | 4.7 | 3066 |
1967-12-01 | 525.1 | 199657 | 11.8 | 4.8 | 3018 |
下面我们绘制一下失业率的时间序列曲线
ggplot(economics, aes(date, unemploy)) +
geom_line()
假设我们现在想要展示各个总统在任的时候失业率的情况,我们可以先使用geom_rect()引入背景颜色,然后使用geom_vline()引入分隔符,再使用geom_text()添加标签,presidential数据集如下
head(presidential)
name | start | end | party |
---|---|---|---|
Eisenhower | 1953-01-20 | 1961-01-20 | Republican |
Kennedy | 1961-01-20 | 1963-11-22 | Democratic |
Johnson | 1963-11-22 | 1969-01-20 | Democratic |
Nixon | 1969-01-20 | 1974-08-09 | Republican |
Ford | 1974-08-09 | 1977-01-20 | Republican |
Carter | 1977-01-20 | 1981-01-20 | Democratic |
# 选取presidential子集
presidential <- subset(presidential, start > economics$date[1])
ggplot(economics) +
#使用geom_rect()增加矩形背景
geom_rect(
aes(xmin = start, xmax = end, fill = party),
ymin = -Inf, ymax = Inf, alpha = 0.2,
data = presidential
)+
#使用geom_vline()增加垂直分割线
geom_vline(
aes(xintercept = as.numeric(start)),
data=presidential,
col = 'grey',alpha=0.5
)+
#使用geom_text()添加文本标签
geom_text(
aes(x=start,y=2500,label=name),
data=presidential,
size = 3,nudge_x=50
)+
# 使用geom_line()绘制时间序列曲线
geom_line(aes(date, unemploy)) +
scale_fill_manual(values = c("blue", "red")) +#颜色设置
xlab("date") +
ylab("unemployment")
这里没有什么新内容:在大多数情况下,在ggplot2中注释绘图是对现有几何图形的直接操作。注意在geom_rect()使用-Inf和Inf作为位置。这些是指绘图的左侧和右侧的限制
geom
,指定我们要添加注释的对象是文本还是点等其他的yrng <- range(economics$unemploy)
xrng <- range(economics$date)
caption <- paste(strwrap("Unemployment rates in the US have
varied a lot over the years", 40), collapse = "\n")
ggplot(economics, aes(date, unemploy)) +
geom_line() +
annotate(
geom = "text", x = xrng[1], y = yrng[2],
label = caption, hjust = 0, vjust = 1, size = 4
)
annotate()函数的便利性在其他情况下也很有用。例如,注释的一种常见形式是通过在主数据集下方绘制不同颜色的较大点来突出显示点的子集。要突出显示subaru制造的车辆,我们可以使用以下内容创建基本绘图:
library(dplyr)
p <- ggplot(mpg, aes(displ, hwy)) +
# 绘制突出点
geom_point(
data = filter(mpg, manufacturer == "subaru"),
colour = "orange",
size = 3
) +
geom_point()
下面我们希望在图形右上侧展示该突出点,使用annotate变得很方便
p +
annotate(geom = "point", x = 5.5, y = 40, colour = "orange", size = 3) +
annotate(geom = "point", x = 5.5, y = 40) +
annotate(geom = "text", x = 5.6, y = 40, label = "subaru", hjust = "left")
这种方法的优点是在绘图区域内创建标签,但缺点是标签与它选择的点距离较远,另一种方法是使用不同的几何图形来完成这项工作。我们之前说过,geom_curve()和geom_segment()可用于绘制带箭头的曲线和直线,并可与annotate()结合使用,如下所示:
# 添加直线箭头
p +
annotate(
geom = "segment", x = 4, y = 35, xend = 2.65, yend = 27,
arrow = arrow(length = unit(5, "mm"))
) +
annotate(geom = "text", x = 4.1, y = 35, label = "subaru", hjust = "left")
注意:annotate(geom=‘segment’) 等价于 geom_segment(),annotate(geom=‘text’)等价于geom_text(),但是使用annotate会相对更灵活,下面我们来验证一下
p +
geom_segment( x = 4, y = 35, xend = 2.65, yend = 27,
arrow = arrow(length = unit(5, "mm"))
) +
geom_text(, x = 4.1, y = 35, label = "subaru", hjust = "left")
得到的图形一样
上面图提供了“直接标记”的示例,其中打印区域本身包含不同组的标签,而不是使用图例。
这通常使绘图更容易阅读,因为它使标签更接近数据。directlabels软件包提供了许多函数是的其更容易实现:
ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point()
ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point(show.legend = FALSE) +
directlabels::geom_dl(aes(label = class), method = "smart.grid")
directlabels提供了一系列直接标签的定位方法,我们一般使用smart.grid即可。还有一个ggplot2的拓展包很有意思,ggforce,例如使用geom_mark_ellipse,就可以通过椭圆来选择不同类型的点
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
ggforce::geom_mark_ellipse(aes(label = cyl, group = cyl))
data(Oxboys, package = "nlme")
ggplot(Oxboys, aes(age, height, group = Subject)) +
geom_line() +
geom_point() +
gghighlight::gghighlight(Subject %in% 1:3)
Warning message:
"Tried to calculate with group_by(), but the calculation failed.
Falling back to ungrouped filter operation..."
Warning message:
"Tried to calculate with group_by(), but the calculation failed.
Falling back to ungrouped filter operation..."
label_key: Subject
一张好的图胜过1000个字,那么一个好的注释胜过任何技巧。
如果使用得当,注释可以成为帮助读者理解数据的强大工具。
例如,当我们想比较不同方面的数据时。
例如,在下面的图中,很容易看到每个方面之间的关系,但各个方面之间的细微差异不会显现出来:
ggplot(diamonds, aes(log10(carat), log10(price))) +
geom_bin2d() +
facet_wrap(vars(cut), nrow = 1)
下面我们可以考虑增加一条参考线,来比较各个方面的细微区别,由于上述每个方面的2维散点图都显示出明显的线性关系,我们考虑用所有数据的回归曲线作为参考线
# 计算回归系数
coef <- coef(lm(log10(price)~log10(carat), data = diamonds))
ggplot(diamonds, aes(log10(carat), log10(price))) +
geom_bin2d() +
geom_abline(intercept = coef[1], slope=coef[2],col='white',size=3)+
facet_wrap(~cut, nrow = 1)
在这个图中,每个分面图都以相同的回归线显示一个类别的数据。这样可以更容易地相互比较面,因为有共享的参考线比较。
ggplot(mpg, aes(displ, hwy, colour = factor(cyl))) +
geom_point() +
gghighlight::gghighlight() +
facet_wrap(~cyl)
可以看出不同的cyl的displ和hwy关系不同
如果想了解更多ggplot2数据可视化技巧,欢迎访问下列文章
玩转数据可视化之R语言ggplot2:(三)ggplot2实现将多张图放在一起,包括并排和插图绘制(快速入门)
玩转数据可视化之R语言ggplot2::(四)单一基础几何图形绘制
玩转数据可视化之R语言ggplot2:(五)分组画图
☀️玩转数据可视化之R语言ggplot2:(六)统计变换绘图:包括加权绘图、数据分布图、曲面图、图形重叠处理等