首发于 R入门
关于ggplot2包的基本功能介绍(上)

关于ggplot2包的基本功能介绍(上)

本文是在ggplot2包的开发者Hadley Wicham大神的个人主页上找到一篇文章,被收录在他所著的R for Data Science一书中作为一个章节的教程。这一章主要是对ggplot2包的一些基础功能的做出了较为详细的介绍,大家可以结合《R语言实战》一书中第19章的内容来进行学习。虽然经过一遍校正,但是下面的翻译中还有一些不准确的地方,希望大家见谅,这里也附上原文的链接

R for Data Science

1. 介绍

“相比于其他形式,简单的图表可以让数据分析师获取更多信息。” ——John Tukey

这一章将教会你如何使用gglplot2包对你的数据进行可视化处理。R有很多用来绘制图表的程序,但是ggplot2是其中最优雅也是功能最强大的工具。ggplot2按照一种条理清楚的图表语法来描述和创建图表。通过学习ggplot2这一种工具包,你可以很好地提升你的工作效率,并且可以将它应用到更多地方。

在开始之前,如果你想了解更多关于ggplot2包理论性的基础知识,我推荐你阅读“图表的分层语法”, vita.had.co.nz/papers/l .

1.1前言

这章我们主要关注ggplot2包,它是tidyverse的一个核心成员。为了访问数据资料,帮助页面和一些我们将要在本章用到的功能,请首先通过以下代码加载tidyverse:

library(tidyverse)
#> Loading tidyverse: ggplot2
#> Loading tidyverse: tibble
#> Loading tidyverse: tidyr
#> Loading tidyverse: readr
#> Loading tidyverse: purrr
#> Loading tidyverse: dplyr
#> Conflicts with tidy packages
----------------------------------------------
#> filter(): dplyr, stats
#> lag():    dplyr, stats

一行代码即可加载tidyverse包,这是一个你几乎将会在每一次数据处理过程中都会用到的工具包。它也会告诉你包中的哪些功能会和基础R中的功能发生冲突(或者是与其他一些你已经加载过的包)。

如果你运行这句代码并且得到了错误信息“there is no package called ‘tidyverse’”,这说明你首先需要安装tidyverse包,然后再次运行library():

install.packages("tidyverse")
library(tidyverse)

每个工具包只需要被安装一次,但是当你每次开始编写一段新的代码时你都要重新加载工具包。

如果我们需要搞清楚某一个功能(或是数据)来源于哪,我们将会使用一种特殊的形式:package::function().例如ggplot2::ggplot() 会明确的告诉你我们正在使用ggplot2包中ggplot()的功能。

2. 第一步

让我们使用下面第一幅图表来回答一个问题:是汽车的发动机型号越大使用的燃料越多吗?你也许已经有了一个答案,但是尝试着让你的答案更加准确。发动机的型号和燃油率之间有什么关系,是正相关还是负相关,是线性还是非线性?

2.1mpg数据框

你可以通过ggplot2中的mpg数据框来检验你的答案。数据框是把变量和观测值收集整理到一个矩形框中(变量为列,观测为行)。mpg包含了US Environment Protection Agency收集到的38种车型的观测值。

mpg
#> # A tibble: 234 × 11
#>   manufacturer model displ  year   cyl      trans   drv   cty   hwy    fl
#>          <chr> <chr> <dbl> <int> <int>      <chr> <chr> <int> <int> <chr>
#> 1         audi    a4   1.8  1999     4   auto(l5)     f    18    29     p
#> 2         audi    a4   1.8  1999     4 manual(m5)     f    21    29     p
#> 3         audi    a4   2.0  2008     4 manual(m6)     f    20    31     p
#> 4         audi    a4   2.0  2008     4   auto(av)     f    21    30     p
#> 5         audi    a4   2.8  1999     6   auto(l5)     f    16    26     p
#> 6         audi    a4   2.8  1999     6 manual(m5)     f    18    26     p
#> # ... with 228 more rows, and 1 more variables: class <chr>

mpg中的变量:

1. displ,汽车的发动机型号,以公升计量。

2. hwy,汽车在公路上行驶时的燃油率,单位是加仑每米。行驶同样距离,低燃油率的汽车比高燃油率汽车消耗更多燃料。

为了了解更多关于mpg的信息,请通过?mpg语句使用帮助功能。

2.2创造一个ggplot

为了绘制mpg的图表,请运行以下代码来使displ成为x轴hwy成为y轴:

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy))

点图展示了发动机型号和燃油率的负相关关系。换句话说,大型号发动机的汽车会使用更多燃料。这是否证实或否认了你之前的观点?

通过ggplot2,你可以用ggplot()功能来绘制一个图表。ggplot()会创建一个坐标系,你可以在坐标系上添加图层。ggplot()中的第一条语句声明了将要在图表中使用的数据组。也就是说ggplot(data=mpg)会创建的一个空的图表,但是这没什么意思,所以我就不在这里演示它了。

你将通过ggplot()里添加一个或两个图层来完成你的图表。函数geom-point()可添加一层点图到你的图表里,也就是创建一副散点图。ggplot2提供了许多几何图形的功能来添加不同类型的图层到图表中。通过本章的学习你将会对它们有所了解。

每一个几何函数都需要一个mapping语句。它可以定义数据组中的变量是如何被绘制成可视图形的。mapping语句经常配合aes()语句使用,aes()中的x和y语句规定了哪个变量将会被绘制到x轴和y轴。ggplot2在data中寻找可添加的变量,在本案例中就是mpg数据组。

2.3一个图表模板

让我们把这段代码转变成一个可反复使用的制图模板。当你要绘制图表时,请首先用数据组、几何功能语句或者是一些绘图语句的集合来代替下面代码中括号里的内容。

ggplot(data = <DATA>) +
  <GEOM_FUNCTION>(mapping = aes(<MAPPINGS>))

本章接下来的内容将想你展示如何去完成并且扩展这个模板,以来绘制不同类型的图表。我们将首先从<MAPPINGS> 这部分开始讲解。

2.4练习

1.运行代码ggplot(data=mpg),你会看到什么?

2.mtcars中有多少行和多少列数据?

3.drv变量表示什么?阅读?mpg的帮助页面来查看一下。

4.绘制一幅hwy和cyl的散点图。

5.如果你绘制一幅class和drv的散点图会发生什么?为什么无法进行绘制?

3. Aesthetic mappings

“图表最大的价值在于它让我们注意到了我们从没有想到过的问题”

——John Tukey

在下面的图表中,一组点(用红色强调)看起来似乎脱离了线性趋势。这些汽车会行驶更远的距离,你怎样来解释这个现象?

让我们来假设这些是混合动力型的汽车。一种来检验这种假设的方式是查看每一辆汽车的class值。mpg中的class变量将汽车的种类进行了划分,例如小型、中型、suv等等。如果离线的点是混合型汽车,它们应该被划分为小型汽车或者是超小型汽车(请记住这份数据是在混合型卡车和SUV流行之前被整理收集的)。

你可以在图表中加入第三个变量,比如说类型,通过改变形状来将它加入到一个二维的散点图中。图形是你的图表中一种可视化的表达。图形包括它的大小、形状或者是颜色。你可以通过改变图形参数来展示出不同类型的点。因为我们已经使用“value”来描述数据,所以接下来我们使用“level”来表示图形参数。这里我们改变图形的大小、形状和颜色等参数来让我们的点变小,变成 三角形或者是变成蓝色:

你可以通过图表中的图形来表达变量的信息。例如,你可以使用点的颜色来表示不同类型的汽车。

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, color = class))

为了用图形来表示变量,你要在aes()中将图形参数的名字和变量联系在一起。ggplot2将会自动分配一个唯一的图形参数(这里是颜色)给每一个唯一的变量值,这个过程被称作是scaling。ggplot2也会自动添加一个图例来解释不同的图形参数和变量之间是如何对应的。

通关观察颜色我们可以发现图形中的离群点是两座汽车。这些汽车似乎并不是混合型而是跑车。跑车拥有很大的发动机,例如SUV和皮卡,和中小型的车身,因此会提高它们每公升汽油所行驶的里程数。

在上面的例子中,我们用图形的不同颜色来表达class变量,同样我们也可以用图形的大小来表示class变量。这种情况下,每一个点的大小将会表示出它的种类信息。这里我们得到了一个警告信息,因为我们将一个无序变量(class)添加了一个有序的图形参数(大小),这并不是一个好主意。

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, size = class))
#> Warning: Using size for a discrete variable is not advised.

或者我们也可以为class添加alpha 的图形参数,可以控制点的形状和透明度。

# Left
ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, alpha = class))
# Right
ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, shape = class))

SUV为什么没有显示图形?ggplot2一次性只能使用六种图形。默认情况下,当你使用图形的形状参数时额外的变量将不被绘制。

对于每一个图形参数,你都可使用aes()将参数的名称和所要表示的变量相关联。aes()函数的功能是将所有图形参数集合在一起,并将其映射到一个图层,再通过语句将其添加到图表上。语法强调了一个关于x和y的有用信息:一个点的x和y轴的坐标位置是它们本身所自带的一种图形信息,你可以通过这种视觉上的特性来表达数据变量所包含的信息。

一旦你处理好了图形参数,ggplot2包将会自动处理余下的问题。它会挑选一个合适的大小绘制图形,并且会构建一个图例项来解释图形参数和变量之间的关系。对于x和y的参数,ggplot2不会创建一个图例,但是它会自动创建一个带有刻度线和标签的x坐标轴。坐标轴在这里发挥图例的功能,它解释了图形的位置和它 的值之间的关系。

你也可以手动为图形添加一些特定的参数信息,例如,我们可以让图形中 的所有点都变成蓝色:

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy), color = "blue")

这里颜色并没有表示变量的信息,只是改变了图表的外观。手动添加图形参数,直接将参数名字写到你的aes()语句里。你需要为图形挑选一个参数值:

用字符串来表示的一种颜色;

以毫米为单位来计量的图形大小;

用数字来表示的点的大小(如下表所示)。

R中可以用数字来定义25种不同的图形。其中一些看起来似乎是重复的,例如 0,15和22全都是正方形。不同之处在于图形的边框颜色和填充颜色。无填充的图形(0-14)通过colour定义其边界线条的颜色,有填充而无边界的图形(15-18)通过colour来定义填充色,既有填充也有边界的图形(21-24)通过colour定义边界颜色,通过fill定义填充色。

3.1练习

1. 下面这段代码出了什么问题,为什么他们的颜色不是蓝色?

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, color = "blue"))
mpg中哪些变量是类别型变量,哪些变量是连续型变量?(提示:用?mpg来读取数据组中的信息)。当你使用mpg的时候你如何知道这些信息。

2. 为一个连续型变量添加颜色、大小、形状的参数,这些图形在表示类别型和连续型变量时有什么不同?

3. 如果将给同一个变量添加多种类型的图形参数会怎样?

4. stroke图形参数是什么?它会使用什么样的形状?(提示:使用?geom-point)

5. 如果你将图形参数添加给非变量值会发生什么?例如:ase(colour=displ<5)?

4. 常见问题

当你使用R的时候,你会遇到很多问题。别担心,每个人都会遇到这些问题。我使用R语言很多年了,每天仍会写出很多错误代码。

仔细比较你写好的代码和书中的代码。R非常挑剔,一个字符的错误就会造成很大的不同。要确保每个字符都有所匹配。有时你运行了代码但是却没有结果显示,检查一下你左手边的控制台,如果显示a+,这说明R认为你还没有写完代码,它在等待你完成你的代码。这种情况下,你通常可以按下ESCAPE键来结束正在运行的程序继续编写你的代码。

当你创建ggplot2图表时一个常见的问题就是把“+”放在了错误的位置,它需要放在一行代码的结尾处,而不是开头处。换句话说,确保你的代码不会写成下面这样:

ggplot(data = mpg) 
+ geom_point(mapping = aes(x = displ, y = hwy))

如果你仍然不太明白,请使用help功能。你可以通过在控制台中运行?funtion-name来查询R的任何功能,或者是在RStudio中选择好函数的名字然后按F1键。不要担心看不懂帮助里的说明,你可以直接拉到页面底部查看贴合你需求的使用案例。

如果还是不能解决的问题,仔细阅读以下报错信息,有时答案也许就藏在里面。但是当你是个R语言的使用新手时,你也许还不能理解这些报错信息。另一个比较好用的工具就是Google,尝试去google这些报错信息,很有可能别人也有同样的问题,并且在网上找到了答案。

5. Facets

改变图形参数是为你的图表添加额外变量的一种方式。另一种方式是将你的图表划分为若干区域(特别是对类别型变量十分有用),每一块区域都会展示出变量中的一个小子集。

为了能通过一个变量来将你的图表划分区域,你可以使用facet-wrap()函数。函数中的第一个语句应为公式,这里你可以通过使用“~”符号加上变量名来创建它(这里所说的公式是指R中数据结构的名称,而不是指一个等式)。你向facet-wrap()函数中传递的变量应该是离散型的。

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) + 
  facet_wrap(~ class, nrow = 2)

要在两个变量的组合上绘制图形,请将facet_grid()添加到绘图调用中。 facet_grid()的第一个参数也是一个公式, 这一次,公式应该包含两个由〜分隔的变量名。

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) + 
  facet_grid(drv ~ cyl)

5.1练习

1.如果将一个连续型变量进行划分会怎么样?

2.在facet-grid(drv~cyl)所绘制的图表中,空的单元区域代表什么?它们和图表有什么关系?

3.下面的代码会绘制出什么样的图形,“.”符号有什么作用?

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_grid(drv ~ .)
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
facet_grid(. ~ cyl)

4.本节中第一个使用这种划分区域型图表的案例:

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
facet_wrap(~ class, nrow = 2)

相较于用颜色来代表变量它有什么优势?有什么劣势?如果你有一个更大的数据组你要如何来平衡它们之间的优劣。

5.读一下?facet-wrap的帮助页。nrow和ncol各有什么功能?还有哪些其它选项可以控制表格的布局?为什么facet-grid()中没有nrow和ncol选项?

6.当你使用facet-grid()时你应该将列中含有更多唯一值的变量放入其中,为什么?

6. Geometric objects

下面这两幅图是不是看起来很像?

两个图标拥有同样的x轴和Y轴,它们都描述了同样的数据。但是它们的图表类型并不相同,两个图表各自使用了不同的图形来展示数据。在ggplot2语法中,我们称它们使用了不同的geoms。

geoms是图表用来展示数据的几何图形的参数选项。人们通常会用这些这些几何图形来描述数据。 例如,条形图使用了条形几何参数,线形图使用了线性几何参数,箱线图使用了箱线几何参数等等。正如我们所看到的,我们可以使用不同的几何图形来绘制同样的数据图表。左侧的图表使用了点图,右侧使用了平滑的曲线图,通过一条平滑曲线来拟合了数据的分布情况。

为了改变图表中的几何形状,你可以在gglot()中添加geom功能,例如,你可以通过以下代码来绘制出上面的两幅图:

# left
ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy))
# right
ggplot(data = mpg) + 
  geom_smooth(mapping = aes(x = displ, y = hwy))

在gglot2中的每一个geom功能都需要一个mapping语句。然而并不是每一个图形参数和几何类型之间都可以互相匹配。你可以为一个点设置形状,但是你不能为一条线设置“形状”。geom-smooth()可以画出不同类型的线,你可以用不同的线来代表不同的变量。

ggplot(data = mpg) + 
  geom_smooth(mapping = aes(x = displ, y = hwy, linetype = drv))

这里geom-smooth()根据drv值将汽车分成了三种不同的线型。一条线代表drv为4的数据,一条线代表drv为f的数据,一条线代表drv为r的数据。这里4代表四轮驱动,f代表前轮驱动,r代表后轮驱动。

如果这看起来比较奇怪,我们可以让它更清晰一点,将线条直接叠加在原来的散点图上,并且用颜色区分不同的drv值。

注意到了吗,这幅图表包含了两种不同的几何类型。不要激动,下一节我们就会学习怎样在同一副图表中添加不同类型几何图形。

ggplot2提供了超过30种几何类型的图表,而扩展包提供了更多(这个网页中有示例https://www.ggplot2-exts.org)。 ggplot2的备忘单是简单理解这些图表的最好的方式,你可以在这里找到它Cheatsheets。如果想单独学习某一种图表可以使用帮助功能查看。

很多图表比如geom-smooth()使用一种几何图形就可以展示多个变量。对于这些图表,你可以设定一组图形参数来表达这些不同的变量。ggplot2会给这组变量中的每一个唯一值都画一个可区分的图形。在练习中,ggplot2将会自动为离散型的变量分组,并且给他们添加几何参数(就像上面那个线形图的例子)。R的这种功能用起来很方便,因为这样的自动分组不需要我们再去做多余的解释说明。

ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy))
ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy, group = drv))

为了能在同一副图表中展示多种图表类型,你可以将要添加的图表类型的geom函数写在ggplot()中:

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy)) +
  geom_smooth(mapping = aes(x = displ, y = hwy))

然而在上面的代码中有一些重复的部分。试想一下如果我们想要改变Y轴,用cty变量来代替hwy变量,你需要在两个位置修改变量名称,这样你也许漏掉其中一个。你可以通过直接在ggplot()中定义xy轴来避免这种重复的写法,ggplot2会把这样的图形参数当作全局参数来应用到每一幅图表上。换句话说,下面的代码可以绘制出和上面相同的图表:

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point() + 
  geom_smooth()

如果你再向geom中添加参数,ggplot2会把它们当做图层叠加在图表上。它会使用这些参数来扩展或者是修改在ggplot()中定义好的全局变量。这就使图表在不同的图层可以展示出不同的图形特性。

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point(mapping = aes(color = class)) + 
  geom_smooth()

你可以用同样的想法来使每一个图层单独表示一组数据。这里我们的曲线仅仅代表mpg数据组中小型汽车的数据资料。geom-smooth()中的语句在它的图层上改变了ggplot()中没有遵守ggplot()中定义的全局变量:

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point(mapping = aes(color = class)) + 
  geom_smooth(data = filter(mpg, class == "subcompact"), se = FALSE)

你会在下一章里学到如何使用filter()函数,这里你只需要知道这条命令是用来挑选小型汽车这个变量的就可以了。)

6.1练习

1.在你绘制线图、直方图、箱线图和区域图的时候你都会用到什么geom语句。

2.首先在你的脑海里运行下面这段代码并想象会输出什么结果,然后在R中运行代码检验你的猜想。

ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) + 
  geom_point() + 
  geom_smooth(se = FALSE)

3.show.legend=FALSE有什么用?如果去掉它会有什么影响?

4.geom-smooth()中的se语句有什么用?

5.下面的这两幅图表有什么区别吗?为什么?

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point() +