初识 R 语言科学绘图包 —— ggplot2

前言

  能够进行科学绘图的软件很多很多,我身边的同学有用 Matlab 的,有用 Python 的,有用 Origin 的,当然也有用 Excel 的。博主之所以采用 R 画图,主要是前面几个做出来的图实在是不忍直视(博主颜控,Python 除外)。其实 R 的基础包里面也有很多画图函数,例如 plot ()barplot ()qqplot () 等, 但是 ggplot2 包的函数画出的图比较漂亮,而且使用灵活。
  博主没有采用大家最常用的 RStudio 编辑器,而是采用了 VSCode 这款编辑器。VSCode 是博主最喜欢的编辑器之一,可以写各种各样的语言,它经常出现在我的文章里,点击链接查看本站关于它的介绍。为了让你更好的体验 VSCode 写 R 语言的快感,点击本站文章 —— 用 VSCode 愉快地写 R,进行相关配置。

ggplot2 简介

  ggplot2 是 R 语言中最优秀的绘图包(目前在 Python 中也已经可以使用,不过 Python 中使用的是 ggplot,低级版本的 ggplot2),也可以说是目前最优秀的绘图工具之一。这得益于 Leland Wilkinson 在他的著作《The Grammar of Graphics》中提出了一套图形语法,把图形元素抽象成可以自由组合的成分,Hadley Wickham 把这套想法在 R 中实现。
  顺道提一下 ggplot2 名字的由来。ggplot2 这个术语需要进行拆词:ggplot2=gg+plot+2:其中的 gg 是《Grammer of Graphics》这本书的英文缩写;plot 是绘图的英文单词;2 顾名思义就是第二代的意思了。所以 ggplot2 就是指基于图形语法的第二代绘图包。
  因为 ggplot2 是基于图形语法这本书,因此 ggplot2 中的绘图是基于图层概念,这与其它绘图系统存在很大不同。其它绘图系统是直接出图,然后再对图形的不同部分进行格式调整,但是 ggplot2 绘图就是典型的堆积木,按照你的想法把各个要素添加到画布之上,这一点相信学过 Photoshop 的同学肯定很熟悉。所以 ggplot2 绘图的本质就是数据到图形属性的映射。

安装 ggplot2 包

  使用 install.packages("ggplot2") 命令即可安装。

ggplot2 基本要素

  这里有必要先和大家说一下 ggplot2 绘图中的一些基本要素。基本要素有:

  • 数据(Data)和映射(Mapping)
  • 几何对象(Geometric)
  • 标尺(Scale)
  • 统计变换(Statistics)
  • 坐标系统(Coordinante)
  • 图层(Layer)
  • 分面(Facet)
  • 主题(Theme)

  这些基本要素之间是通过 + 连接,以图层 (layer) 的方式来粘合构图的,所以图层是 ggplot2 中一个重要的概念。
  因为本文属于一篇先导文章,所以就不对这些概念进行系统介绍,而是简单地介绍这些概念的运用(介绍概念很麻烦的,不如在实践中自己理解,后面我会单独介绍每一个元素的)。

数据(Data)和映射(Mapping)

  下面以一份钻石的数据为例,这份数据非常大,随机取一个子集来画图:

require(ggplot2)
data(diamonds)
set.seed(42)
small <- diamonds[sample(nrow(diamonds), 1000), ]
head(small)
# A tibble: 6 x 10
    carat cut color clarity depth   table   price       x     y     z

    1  0.71 Very Good H     SI1      62.5    60  2096  5.68  5.75  3.57
    2  0.79 Premium   H     SI1      61.8    59  2275  5.97  5.91  3.67
    3  1.03 Ideal     F     SI1      62.4    57  6178  6.48  6.44  4.03
    4  0.5  Ideal     E     VS2      62.2    54  1624  5.08  5.11  3.17
    5  0.27 Ideal     E     VS1      61.6    56   470  4.14  4.17  2.56
    6  0.3  Premium   E     VS2      61.7    58   658  4.32  4.34  2.67
summary(small)
    carat               cut      color      clarity        depth           table           price              x               y               z
    Min.   :0.2200   Fair     : 28   D:121   SI1    :258   Min.   :55.20   Min.   :50.10   Min.   :  342.0   Min.   :3.850   Min.   :3.840   Min.   :2.330
    1st Qu.:0.4000   Good     : 88   E:186   VS2    :231   1st Qu.:61.00   1st Qu.:56.00   1st Qu.:  989.5   1st Qu.:4.740   1st Qu.:4.758   1st Qu.:2.920
    Median :0.7100   Very Good:227   F:164   SI2    :175   Median :61.80   Median :57.00   Median : 2595.0   Median :5.750   Median :5.775   Median :3.550
    Mean   :0.8187   Premium  :257   G:216   VS1    :141   Mean   :61.71   Mean   :57.43   Mean   : 4110.5   Mean   :5.787   Mean   :5.791   Mean   :3.572
    3rd Qu.:1.0700   Ideal    :400   H:154   VVS2   : 91   3rd Qu.:62.50   3rd Qu.:59.00   3rd Qu.: 5495.2   3rd Qu.:6.600   3rd Qu.:6.610   3rd Qu.:4.070
    Max.   :2.6600                   I:106   VVS1   : 67   Max.   :72.20   Max.   :65.00   Max.   :18795.0   Max.   :8.830   Max.   :8.870   Max.   :5.580
                                      J: 53   (Other): 37

  画图实际上是把数据中的变量映射到图形属性上。以克拉 (carat) 数为 X 轴变量,价格 (price) 为 Y 轴变量。

p <- ggplot(data = small, mapping = aes(x = carat, y = price))

  上面这行代码把数据映射 XY 坐标轴上,需要告诉 ggplot2,这些数据要映射成什么样的几何对象,下面以散点图为例:

p + geom_point()

  几何对象将在下面的小节介绍,这一节,关注的是数据和图形属性之间的映射。如果想将切工(cut)映射到形状属性。只需要:

p <- ggplot(data=small, mapping=aes(x=carat, y=price, shape=cut))
p+geom_point()

  再比如我想将钻石的颜色(color)映射颜色属性:

p <- ggplot(data=small, mapping=aes(x=carat, y=price, shape=cut, colour=color))
p + geom_point()

几何对象(Geometric)

  在上面的例子中,各种属性映射由 ggplot() 函数执行,只需要加一个图层,使用 geom_point() 告诉 ggplot() 要画散点图,于是所有的属性都映射到散点上。geom_point() 完成的就是几何对象的映射,ggplot2 提供了各种几何对象映射,如 geom_histogram() 用于直方图,geom_bar() 用于画柱状图,geom_boxplot() 用于画箱式图等等(后期博主会写一篇用 ggplot2 画各种图的文章,这里只简单列举几个)。
  注意:不同的几何对象,要求的属性会有些不同,这些属性也可以在几何对象映射时提供,比如上一个图,也可以用以下语法来画:**

p <- ggplot(small)
p + geom_point(aes(x=carat, y=price, shape=cut, colour=color))

  ggplot2 支持图层,我通常把不同的图层中共用的映射提供给 ggplot() 函数,而某一几何对象才需要的映射参数提供给 geom_xxx() 函数。

直方图

  直方图最容易,提供一个 x 变量,画出数据的分布。

ggplot(data=small, mapping=aes(x=price)) + geom_histogram()

直方图

  同样可以根据另外的变量给它填充颜色,比如按不同的切工:

ggplot(data=small, mapping=aes(x=price, fill=cut))+geom_histogram()

直方图

柱状图

  柱状图非常适合于画分类变量。在这里以透明度(clarity)变量为例。按照不同透明度的钻石的数目画柱状图。

ggplot(data=small, mapping=aes(x=clarity))+geom_bar()

柱状图

  柱状图两个要素,一个是分类变量,一个是数目,也就是柱子的高度。数目在这里不用提供,因为 ggplot2 会通过 x 变量计算各个分类的数目。

密度函数图

  说到直方图,就不得不说密度函数图,数据和映射和直方图是一样的,唯一不同的是几何对象,geom_histogram() 告诉 ggplot 要画直方图,而 geom_density 则说我们要画密度函数图,在我们熟悉前面语法的情况下,很容易画出:

ggplot(data=small, mapping=aes(x=price, colour=cut))+geom_density()

密度函数图

  你也可以根据另外的变量给它填充颜色,比如按不同的切工:

ggplot(data=small, mapping=aes(x=price, fill=clarity))+geom_density()

密度函数图

  colour 参数指定的是曲线的颜色,而 fill 是往曲线下面填充颜色。

其他

  ggplot2 提供了很多的 geom_xxx() 函数,可以满足我们对各种图形绘制的需求。

函数名称 描述
geom_abline 线、由斜率和截距决定
geom_area 面积图
geom_bar 条形图、以 x 轴为底的矩形
geom_bin2d 二维热图
geom_blank 空白,什么都不画
geom_boxplot 箱线图
geom_contour 等高线图
geom_crossbar 带有水平中心线的盒子图
geom_density 光滑密度曲线图
geom_density2d 二维密度登高线图
geom_dotplot 点直方图,用点来表示观测值的个数
geom_errorbar 误差棒
geom_errorbarh 水平的误差棒
geom_freqpoly 频率多边形图
geom_hex 用六边形表示的二维热图
geom_histogram 直方图
geom_hline 水平线
geom_jitter 给点添加扰动,减轻图形重叠问题
geom_line 按照 x 坐标的大小顺序依次连接各个观察值
geom_lineranger 一条线代表一个区间的竖直线
geom_map 基准地图里的多边形
geom_path 按数据的原始顺序连接各个观察值
geom_point 点,用点来绘制散点图
geom_pointranger 用一条中间带点的竖直线代表一个区间
geom_polygon 多边形,相当于一个有填充的路径
geom_quantile 添加分位数回归线
geom_raster 高效的矩形瓦片图
geom_rect 二维的矩形图
geom_ribbon 色带图,连续的 x 值所对应的 y 的范围
geom_rug 边际地毯图
geom_segment 添加线段或箭头
geom_smooth 添加光滑的条件均值线
geom_text 文本注释
geom_tile 瓦片图
geom_violin 小提琴图
geom_vline 竖直线

标尺(Scale)

  前面我们已经看到了,画图就是在做映射,不管是映射到不同的几何对象上,还是映射各种图形属性。这一小节介绍标尺,在对图形属性进行映射之后,使用标尺可以控制这些属性的显示方式,比如坐标刻度,可能通过标尺,将坐标进行对数变换;比如颜色属性,也可以通过标尺,进行改变。

ggplot(data=small, mapping=aes(x=carat, y=price, shape=cut, colour=color))+geom_point()+scale_y_log10()+scale_colour_manual(values=rainbow(7))

  以数据(Data)和映射(Mapping) 一节中所画散点图为例,将 Y 轴坐标进行 log10 变换,再自己定义颜色为彩虹色。

统计变换(Statistics)

  统计变换对原始数据进行某种计算,然后在图上表示出来,例如对散点图上加一条回归线。

ggplot(small, aes(x=carat, y=price))+geom_point()+scale_y_log10()+stat_smooth()

  这里我们就不按颜色、切工来分了,不然 ggplot 会按不同的分类变量分别做回归,如下图我用颜色分类,图就很乱。如果我们需要这样做,我们可以使用分面,这个将在后面介绍。

  注意:这里,aes 所提供的参数,就通过 ggplot() 提供,而不是提供给 geom_point(),因为 ggplot 里的参数,相当于全局变量,geom_point()stat_smooth() 都知道 x、y 的映射,如果只提供给 geom_point(),则相当于是局部变量,geom_point() 知道这种映射,而 stat_smooth() 不知道,当然你可以再给 stat_smooth() 提供 x,y 的映射,不过显得很麻烦,不够简洁美观,还是提供给 ggplot() 好。
  ggplot2 提供了多种统计变换方式:

统计变换方法 统计变换方法 统计变换方法 统计变换方法
stat_abline stat_contour stat_identity stat_summary
stat_bin stat_density stat_qq stat_summary2d
stat_bin2d stat_density2d stat_quantile stat_summary_hex
stat_bindot stat_ecdf stat_smooth stat_unique
stat_binhex stat_function stat_spoke stat_vline
stat_boxplot stat_hline stat_sum stat_ydensity

  统计变换是非常重要的功能,我们可以自己写函数,基于原始数据做某种计算,并在图上表现出来,也可以通过它改变 geom_xxx() 函数画图的默认统计参数。比如可以把 geom_boxplot() 的中位线替换成了平均值来作图。

坐标系统(Coordinante)

  坐标系统控制坐标轴,可以进行变换,例如 XY 轴翻转,笛卡尔坐标和极坐标转换,以满足我们的各种需求。
  坐标轴翻转由 coord_flip() 实现:

ggplot(data=small, mapping=aes(x=cut, fill=cut))+geom_bar()+coord_flip()

  而转换成极坐标可以由 coord_polar() 实现:

ggplot(data=small, mapping=aes(x=factor(1), fill=cut))+geom_bar()+coord_polar(theta="y")

图层(Layer)

  ggplot 的厉害之处在于使用 + 号来叠加图层,这堪称是泛型编程的典范。在前面散点图上,我们已经见识过,加上了一个回归线拟合的图层。有了图层的概念,使用 ggplot 画起图来,就更加得心应手。
  做为图层的一个很好的例子是蝙蝠侠 logo,batman logo 由 6 个函数组成,在下面的例子中,我先画第一个函数,之后再加一个图层画第二个函数,不断重复这一过程,直到六个函数全部画好。

require(ggplot2)

f1 <- function(x) {
    y1 <- 3*sqrt(1-(x/7)^2)
    y2 <- -3*sqrt(1-(x/7)^2)
    y <- c(y1,y2)
    d <- data.frame(x=x,y=y)
    d <- d[d$y > -3*sqrt(33)/7,]
    return(d)
}

x1 <- c(seq(3, 7, 0.001), seq(-7, -3, 0.001))
d1 <- f1(x1)
p1 <- ggplot(data=d1,aes(x,y)) + geom_point(color="red")

x2 <- seq(-4,4, 0.001)
y2 <- abs(x2/2)-(3*sqrt(33)-7)*x2^2/112-3 + sqrt(1-(abs(abs(x2)-2)-1)^2)
d2 <- data.frame(x2=x2, y2=y2)
p2 <- p1 + geom_point(data=d2, aes(x=x2,y=y2), color="yellow")

x3 <- c(seq(0.75,1,0.001), seq(-1,-0.75,0.001))
y3 <- 9-8*abs(x3)
d3 <- data.frame(x3=x3, y3=y3)
p3 <- p2+geom_point(data=d3, aes(x=x3,y=y3), color="green")

x4 <- c(seq(0.5,0.75,0.001), seq(-0.75,-0.5,0.001))
y4 <- 3*abs(x4)+0.75
d4 <- data.frame(x4=x4,y4=y4)
p4 <- p3+geom_point(data=d4, aes(x=x4,y=y4), color="steelblue")

x5 <- seq(-0.5,0.5,0.001)
y5 <- rep(2.25,length(x5))
d5 <- data.frame(x5=x5,y5=y5)
p5 <- p4+geom_point(data=d5, aes(x=x5,y=y5))

x6 <- c(seq(-3,-1,0.001), seq(1,3,0.001))
y6 <- 6 * sqrt(10)/7 +
    (1.5 - 0.5 * abs(x6)) * sqrt(abs(abs(x6)-1)/(abs(x6)-1)) -
    6 * sqrt(10) * sqrt(4-(abs(x6)-1)^2)/14
d6 <- data.frame(x6=x6,y6=y6)
p6 <- p5+geom_point(data=d6,aes(x=x6,y=y6), colour="blue")

p <- p6+theme_bw()
print(p)

分面(Facet)

  分面可以让我们按照某种给定的条件,对数据进行分组,然后分别画图。
  在统计变换一节中我们提到如果按切工分组作回归线,显然图会很乱,有了分面功能,我们可以分别作图。

ggplot(data=small, aes(x=carat, y=price))+geom_point(aes(colour=cut))+scale_y_log10() +facet_wrap(~cut)+stat_smooth()

主题(Theme)

  通过 ggplot 画图之后,我们可能还需要对图进行定制,像 title, xlab, ylab 这些高频需要用到的图形元素,ggplot2 提供了 ggtitle(), xlab()ylab() 来实现。

p <- ggplot(small, aes(x=cut, y=price, fill=color))+geom_boxplot()+ggtitle("Prince vs Cut")+theme(plot.title = element_text(hjust = 0.5))
p

  但是这个远远满足不了需求,我们需要改变字体,字体大小,坐标轴,背景等各种元素,这需要通过 theme() 函数来完成。
  theme()函数的具体介绍放在后面,这里介绍一下 ggplot2 提供一些已经写好的主题,比如 theme_grey() 为默认主题,而 theme_bw() 为白色背景的主题,还有 theme_classic() 主题,和 R 的基础画图函数较像。别外 ggthemes 包提供了一些主题可供使用,包括:

  1. theme_economist()
  2. theme_economist_white()
  3. theme_wsj()
  4. theme_excel()
  5. theme_few()
  6. theme_foundation()
  7. theme_igray()
  8. theme_solarized()
  9. theme_stata()
  10. theme_tufte()

  我们可以加载 ggthemes 包来快速的修改我们上面的图的主题:

require(ggplot2)
require(ggthemes)
data(diamonds)
set.seed(42)
small <- diamonds[sample(nrow(diamonds), 1000), ]
p <- ggplot(small, aes(x=cut, y=price, fill=color))+geom_boxplot()+ggtitle("Prince vs Cut")
p + theme_wsj()

总结

  ggplot2 包画出来的图真的很漂亮,再加上 R 语言本身就是专门做统计的,前期的数据处理有大量的包可以调用,对于一个颜控的工科生来说,这简直就是我做论文插图的秘密武器。
  本文只是一篇很简单的介绍文,很多东西都没有讲到和讲清楚,主要是想先让大家意识到 ggplot2 包的优秀和简单,后期博主会一一进行讲解的。


   转载规则


《初识 R 语言科学绘图包 —— ggplot2》 Huang Pan 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录