29 ggplot作图入门

在作常用的图形时, 只需要两个步骤: 首先将图形所展现的数据输入到ggplot()函数中, 然后调用某个geom_xxx()函数, 指定图形类型,如散点图、曲线图、盒形图等。

如果需要进一步控制图形细节, 只要继续调用其它函数, 就可以控制变量值的表现方式(scale)、图例、配色等。 这使得我们很容易做出基本的图形, 在有需要时再深入学习, 做出更为满意的图形。

与基本R中的作图系统相比, ggplot2的作图有规律可循, 作图结果直接达到出版印刷质量, 除了可以按照一些既定模式做出常见种类的图形, 也很容易将不同图形种类组合在一起, 或者设计新颖的图形。 基本R的作图结果通常不够美观, 如果要将不同种类图形组合在一起比较困难, 对设计新的图形类型支持也不够好。

ggplot2的作图一般步骤为:

  • 准备数据,一般为数据框, 且一般为长表, 即每个观测时间占一行, 每个观测变量占一列。
  • 将数据输入到ggplot()函数中, 并指定参与作图的每个变量分别映射到哪些图形特性, 比如映射为x坐标、y坐标、颜色、形状等。 这些映射称为aesthetic mappings或aesthetics。
  • 选择一个合适的图形类型, 函数名以geom_开头, 如geom_point()表示散点图。 图形类型简称为geom。 将ggplot()部分与geom_xxx()部分用加号连接。 到此已经可以作图,下面的步骤是进一步的细化设定。
  • 设定适当的坐标系统, 如coord_cartesian(), scale_x_log10()等。 仍用加号连接。
  • 设定标题和图例位置等,如labs()。 仍用加号连接。

这个流程的一个大致的模板为:

p <- ggplot(data=<输入数据框>,
mapping=aes(<维度>=<变量名>,
<维度>=<变量名>, <...>))
p + geom_<图形类型>(<...>) +
scale_<映射>_<类型>(<...>) +
coord_<类型>(<...>) +
labs(<...>)

其中<…>表示额外的选项。 变量p包含做出的图形的所有数据与设定, 变量名可以任意取。

# 安装socviz
devtools::install_github("kjhealy/socviz")

后续的例子中用到一些数据集:

  • 来自gapminder扩展包的gapminder数据集, 有若干个国家不同年份的一些数据, 包括所属洲、期望寿命、人口数、人均GDP。 有1704个观测和6个变量。
  • socviz包的gss_sm数据集,是2016年美国一般社会调查数据的部分内容。 有2867个观测,32个变量。 社会调查数据的变量主要取属性值, 比如无序分类、有序分类、分组的数值、整数值等。
  • socviz包的organdata数据集, 是17个OECD国家历年的器官捐献情况以及一些其它记录。
  • socviz扩展包的elections_historic数据集。 包括美国历次总统大选当选人、所属党派、支持比例等。
  • socviz扩展包的asasec数据集。 这是美国社会学学会(ASA)的各分会2005年到2015年的一些数据。
  • ggplot2包中的midwest数据集包含了美国中西部的一些县的统计数据, 如面积等。
  • 来自ggplot2包的钻石数据集。
# gapminder的头部:
# install.packages("gapminder")
library(gapminder)
head(gapminder, 5) %>%
knitr::kable()
country continent year lifeExp pop gdpPercap
Afghanistan Asia 1952 28.801 8425333 779.4453
Afghanistan Asia 1957 30.332 9240934 820.8530
Afghanistan Asia 1962 31.997 10267083 853.1007
Afghanistan Asia 1967 34.020 11537966 836.1971
Afghanistan Asia 1972 36.088 13079460 739.9811

出现的问题记录

加载R包的时候,出现报错
......,载入了名字空间‘rlang’ 0.4.5,但需要的是>= 0.4.6

解决方案:

remove.packages('rlang')
install.packages("munsell")
install.packages('ralng')

然后又出现报错

错误: package or namespace load failed for ‘tidyverse’ in loadNamespace(i, c(lib.loc, .libPaths()), versionCheck = vI[[i]]):
载入了名字空间‘vctrs’ 0.2.4,但需要的是>= 0.3.0

使用上面的方法安装包

remove.packages('vctrs')
install.packages('vctrs')

gss_sm的头部:

library(socviz)
head(gss_sm, 5) %>%
knitr::kable()
year id ballot age childs sibs degree race sex region income16 relig marital padeg madeg partyid polviews happy partners grass zodiac pres12 wtssall income_rc agegrp ageq siblings kids religion bigregion partners_rc obama
2016 1 1 47 3 2 Bachelor White Male New England $170000 or over None Married Graduate High School Independent Moderate Pretty Happy NA NA Aquarius 3 0.9569935 Gt $170000 Age 45-55 Age 34-49 2 3 None Northeast NA 0
2016 2 2 61 0 3 High School White Male New England $50000 to 59999 None Never Married Lt High School High School Ind,near Dem Liberal Pretty Happy 1 Partner Legal Scorpio 1 0.4784968 Gt $50000 Age 55-65 Age 49-62 3 0 None Northeast 1 1
2016 3 3 72 2 3 Bachelor White Male New England $75000 to $89999 Catholic Married High School Lt High School Not Str Republican Conservative Very Happy 1 Partner Not Legal Pisces 2 0.9569935 Gt $75000 Age 65+ Age 62+ 3 2 Catholic Northeast 1 0
2016 4 1 43 4 3 High School White Female New England $170000 or over Catholic Married NA High School Not Str Republican Moderate Pretty Happy NA NA Cancer 2 1.9139870 Gt $170000 Age 35-45 Age 34-49 3 4+ Catholic Northeast NA 0
2016 5 3 55 2 2 Graduate White Female New England $170000 or over None Married Bachelor High School Not Str Democrat Slightly Liberal Very Happy 1 Partner Legal Scorpio 1 1.4354903 Gt $170000 Age 45-55 Age 49-62 2 2 None Northeast 1 1

散点图

基本的散点图
以gapminder数据集作为输入数据, 做出简单的散点图, 并逐步进行改善。 这个数据集有多个国家在多个年份的期望寿命与人均GDP值, 作期望寿命对人均GDP的散点图, 每个国家的每个年份作为一个点。 散点图最重要的映射是x轴与y轴两个维度。

首先调用ggplot()函数, 指定数据集, 将人均GDP映射到x轴, 将期望寿命映射到y轴, 结果保存为一个R变量:

p <- ggplot(data = gapminder,
mapping = aes(
x=gdpPercap,
y=lifeExp
))

x、y轴是最常见的映射, 也可以将变量映射为颜色、符号、线型等.
p <- ggplot(gapminder, aes(gdpPercap, lifeExp))

在如上指定了数据和映射后, 只要用geom_xxx()指定一个图形类型, 并与ggplot()的结果用加号连接就可以作图了

p + geom_point()

上面的程序等同于调用print(p + geom_point())。

print(p + geom_point())


在R函数中或者在循环中需要显式地调用print(), 否则不会显示结果。 当载入了tidyverse系统时可以写成 (p + geom_point()) %>% print()。

逐步改善

指定数据集、指定映射、选择适当的图形类型就可以做出基本的图形, 随后可以逐步对坐标系、坐标系刻度、标签与图例、配色等进行改善。 实际上,ggplot2包已经提供了十分合理的预设值, 用户只要进行一些必要的改动即可。

作图步骤之间用加号连接,这是ggplot包特有的语法。 例如, 用相同的映射做出拟合曲线图:

p + geom_smooth()


使用最了解的数据集d.class作图

p2 <- ggplot(data=d.class, mapping = aes(x=height,y=weight))
p2+geom_point()


用相同的映射做出散点图并叠加拟合曲线图:

p + geom_point() + geom_smooth()

p2 + geom_point() + geom_smooth()


geom_smooth()的默认设置调用了gam()函数来拟合曲线, 可以用geom_smooth()的参数选择不同的拟合方法, 如直线拟合:

p + geom_point() + geom_smooth(method = 'lm')

p2 + geom_point() + geom_smooth(method = 'lm')

注意geom_xxx()函数计算所需的变量值是从ggplot()函数保存在变量p中的信息提取的。
在以上的所有图形中, x轴变量(人均GDP)分布非正态,严重右偏, 使得大多数散点重叠地分布在直角坐标系的左下角。 将x轴用对数刻度可以改善, 函数为scale_x_log10():

p + geom_point() + geom_smooth(method = "gam") + scale_x_log10()

对数刻度实际上是对原始数据进行对数变换, 而geom_smooth()的拟合计算是在对数变换之后进行的。

p + scale_x_log10() + geom_point() + geom_smooth(method = "gam") 

调用scales扩展包的适当函数进行改善, 作为scale_x_log10()的labels选项

p + scale_x_log10(labels=scales::dollar) + geom_point() + geom_smooth(method = "gam")

scale_xxx()的labels选项指定如何标出坐标刻度数字, 参数值是一个函数对象, 如果scales包中找不到适当的功能, 可以自定义一个函数将数值转换为字符串。 scales包提供了comma, date, dollar, math, number, ordinal, pvalue, scientific, time等坐标刻度值转换函数。

测试将图形的x和Y轴颠倒,然后对y轴取10为底的对数

pt <- ggplot(data=gapminder, mapping = aes(x=lifeExp, y = gdpPercap))

pt + scale_y_log10(labels=scales::dollar) + geom_point() + geom_smooth()

颜色、符号、线型等映射

在ggplot()函数的mapping参数的aes()设定中将变量映射到x、y轴, 颜色、符号、线型等图形元素类型, 也可以作为图形设置将某些图形元素设置为固定值。

例如, 用不同颜色表示不同大洲, 就是将continent变量映射到color:

p <- ggplot(data=gapminder,
mapping = aes(x=gdpPercap,
y=lifeExp,
color=continent))

程序中仅指定了将大洲映射到颜色维, 并不具体指定所用的颜色。
作带有局部多项式曲线拟合的散点图:

p + geom_point() +
geom_smooth(method = "gam") +
# 此处调用的scales包的dollar函数不能带括号调用
scale_x_log10(labels=scales::dollar)

不同散点用了不同颜色表示其continent变量的值, 五个大洲分别进行了曲线拟合, 曲线使用了不同颜色但置信域颜色相同, 使得难以认读。 在图形右侧自动生成了颜色与continent变量值的对应关系图例。

下面的图形仍分不同大洲作曲线拟合, 并将置信区间阴影的颜色也用不同大洲区分, 方法是在aes()中将color和fill都指定为变量continent:

p <- ggplot(data=gapminder,
mapping = aes(
x = gdpPercap,
y = lifeExp,
color = continent,
fill = continent))
p + geom_point() +
geom_smooth(method="loess") +
scale_x_log10(labels=scales::dollar)

将颜色指定为一个固定值
选颜色:

select.colors <- function(){
nc <- length(colors())
x <- rep(seq(26), 26)[1:nc]
y <- rep(seq(26), each=26)[1:nc]
cols <- colors()
plot(x,y,type="p", pch=16, cex=2, col=cols)
res <- cols[identify(x,y,labels = cols)]
res
}
p <- ggplot(data=gapminder,
mapping = aes(
x = gdpPercap,
y = lifeExp,
color = "green"))
p + geom_point() +
geom_smooth(method="loess") +
scale_x_log10(labels=scales::dollar)

散点并没有使用指定的颜色, 而且图形右侧有一个chartreuse4图例。 这是因为, aes()仅用来指定变量与图形元素类型的映射, 所以实际上是生成了一个仅有一个常数值"chartreuse4"的新变量, 用颜色表示这个新变量。 为了指定固定颜色, 应将color=作为geom_xxx()函数的选项, 而不是放在aes()映射中

p <- ggplot(data=gapminder,
mapping = aes(
x = gdpPercap,
y = lifeExp))
p + geom_point(color="black") +
geom_smooth(method="loess") +
scale_x_log10(labels=scales::dollar)

geom_xxx()函数接受许多关于颜色、透明度、符号、线型的设置参数。 比如, 下面的程序指定了散点的透明度, 以及拟合直线的粗细:

# 其中参数alpha代表透明度
p + geom_point(alpha=0.2, color="red") +
geom_smooth(method = 'gam', color='black', size = 1, alpha = 0.3) +
scale_x_log10(labels=scales::dollar)

size指定了线的以毫米为单位的粗细, se = FALSE关闭了置信区间显示。 用alpha =设置了透明度, 取0和1之间的值, 数值越小越透明。 在有许多个点时适当设置透明度可以比较好地显示出重叠的点, 重叠点越多点的颜色越深。 虽然这里设置了固定的透明度, 也可以在aes()中将透明度alpha映射到某个变量, 使得该变量值大小用点的透明度表示。
画线时可以用linetype参数指定线型, 0表示实线, 1到6分别表示不同的虚线线型。

下面用labs()函数给图形加上适当的标题:

p <- ggplot(data=gapminder,
mapping = aes(
x = gdpPercap,
y = lifeExp))
p + geom_point(alpha = 0.3) +
geom_smooth(method="gam") +
scale_x_log10(labels=scales::dollar) +
labs(
x = "人均GDP",
y = "期望寿命(年数)",
title = "经济增长与期望寿命",
subtitle = "数据点为每个国家每年",
caption = "数据来源: gapminder" )

可以看出, labs()规定了上方的标题、小标题, x轴、y轴的标题, 右下方的标注(caption)。 坐标轴刻度数值的规定则需要在scale_xxx()函数中给出。

在geom函数中映射变量

在前面的一个例图中, 在ggplot()函数中将colorfill映射到了continent变量, 使得不仅散点颜色代表了不同大洲, 还使得每个大洲单独拟合了曲线。 如果希望所有大洲拟合同一条曲线怎么办?

在必要时, 可以在geom_xxx()函数中用mapping = aes(<...>)单独指定变量映射。 例如, 下面的程序在geom_point()中将不同大洲映射为不同颜色, 而不影响geom_smooth()中的颜色以及分组:

p <- ggplot(data=gapminder,
mapping = aes(
x = gdpPercap,
y = lifeExp))
p + geom_point(mapping = aes(color = continent)) +
geom_smooth(method="loess") +
scale_x_log10(labels=scales::dollar)

这里常用到的aes是aesthetic(美学的)的缩写

也可以将一个分类变量映射到不同绘图符号。 例如,取gapminder 2007年数据子集, 将大洲映射到符号(shape):

# 如果报错为找不到对象year,则是没有启用filter函数对应的包
# library(tidyverse) 即可
p <- ggplot(data = filter(gapminder, year == 2007),
mapping = aes(
x = gdpPercap,
y = lifeExp,
shape = continent))
p + geom_point(alpha = 0.4, size = 4) +
scale_x_log10(labels=scales::dollar)

这种映射仅适用于点数比较少的情况, 还用了size参数指定符号的大小(单位:毫米)。 如果所有点使用同一符号并需要指定符号, 可以在geom_point()中用shape参数指定, 可以用0到25的整数值表示, 比如19为实心点, 也可以用字符串符号名称表示, 如"circle"表示实心点。 参见ggplot2帮助目录中的vignette ggplot2: ggplot2-specs。