通常而言,在绘制图形的时候都是绘制某一种类型的一张图形,例如绘制一张散点图,绘制直方图。但有的时候我们希望同时展示多幅图形,可能是因为这些图形有某种联系,需要共同展示才能够更好的表达数据中蕴含的信息。之前介绍的边际图形就是这样的一个例子。本章节会介绍,当我们绘制了好了多幅图形之后,如何将多幅图形合并起来。
一、 合并多幅图形到一张图中
如果使用的是R的基础绘图形,则可以使用par和layout函数来将多幅图形放到一张图中。但是,如果是使用ggplot绘图系统,则要使用其他的方法来合并图形。包括:
- gridExtra包中的grid.arrange()。
- cowplot包中的plot_grid()。
cowplot包是由Claus O.Wilke开发的,它是ggplot2的一个扩展包,可以将多幅图形合并到同一张图形当中。cowplot包中有几个函数可以用来合并图形:
- plot_grid():可以轻松地组合多个绘图。
- ggdraw() draw_plot() draw_plot_label(): 将图形放置在具有特定大小的局部位置。
下面的代码首先绘制几幅图形,然后将图形合并到同一张图形之中,如图1所示。
代码语言:javascript复制require(ggplot2)
## Loading required package: ggplot2
# install.packages("gridExtra")
# install.packages("cowplot")
library("gridExtra")
library("cowplot")
##
## ********************************************************
## Note: As of version 1.0.0, cowplot does not change the
## default ggplot2 theme anymore. To recover the previous
## behavior, execute:
## theme_set(theme_cowplot())
## ********************************************************
# 使用的数据集是ToothGrowth
ToothGrowth$dose <- as.factor(ToothGrowth$dose)
data("economics") #加载数据集
# 设置颜色
my3cols <- c("red", "black", "yellow")
require(cowplot)
p <- ggplot(ToothGrowth, aes(x = dose, y = len))
# 绘制图形
bxp <- p geom_boxplot(aes(color = dose)) scale_color_manual(values = my3cols)
# 绘制点图
dp <- p geom_dotplot(aes(color = dose, fill = dose), binaxis='y',stackdir='center')
scale_color_manual(values = my3cols)
scale_fill_manual(values = my3cols)
lp <- ggplot(economics, aes(x = date, y = psavert)) geom_line(color = "#E46726")
plot_grid(bxp, dp, lp, labels = c("A", "B", "C"), ncol = 2, nrow = 2)
## `stat_bindot()` using `bins = 30`. Pick better value with `binwidth`.
图1 合并多幅图形
上面的代码中,首先绘制了三幅图形,箱线图,点图和时间序列图。然后使用cowplot包中的plot_grid函数将三幅图形合并到一幅图当中。代码‘plot_grid(bxp, dp, lp, labels = c(“Box plot”, “Jitter plot”, “time series”), ncol = 2, nrow = 2)’
表示的是将box,dp,lp这三幅图形合并,labels参数用于指定标签名称,nrow用于设定图形中子图的行数,ncol用于设置图中子图的列数。从图中可以看到,三幅图形被放到了同一幅图形中,图形包含两行两列,第四幅图形是空白的。
另外,使用ggdraw()
函数、draw_plot()
函数和draw_plot_label()
函数的组合可用于将图形和标签放置在具有特定大小的特定位置。ggdraw()
:初始化一个空的绘图画布;draw_plot()
:在绘图画布上的某个位置放置一个绘图。draw_plot_label()
:在图的左上角添加一个plot标签。draw_plot()
函数的格式如下:
draw_plot(plot, x = 0, y = 0, width = 1, height = 1)
参数含义如下:
- plot:要放置的plot (ggplot2图形或gtable图形)。
- x,y: 用于指定图形的位置。
- width,height:图形的宽度和高度。
draw_plot_label()
函数的格式如下:
draw_plot_label(label, x = 0, y = 1, size = 16, ...)
函数的参数含义是:
- plot:要放置的plot (ggplot2图形或gtable图形)。
- x,y::用于指定标签的位置。
- size :要绘制的标签的字体大小。
需要注意的是,默认情况下,x,y位置的表示是 从0到1,点(0,0)位于画布的左下角。下面的代码使用这种方式将上文的图形合并成为同一幅图形,如图2所示。
代码语言:javascript复制ggdraw()
draw_plot(bxp, x = 0, y = .5, width = .5, height = .5)
draw_plot(dp, x = .5, y = .5, width = .5, height = .5)
draw_plot(lp, x = 0, y = 0, width = 1, height = 0.5)
draw_plot_label(label = c("Box plot", "Jitter plot", "Time series"),
x = c(0.1, 0.5, 0), y = c(1, 1, 0.5), size = 15)
图2 合并多幅图形
代码中,首先使用了ggdraw()
函数添加了一张空白的画布。然后使用draw_plot
函数添加了第一幅图形bxp,位置在(0,0.5),宽度为0.5,高度为0.5。
然后使用draw_plot
函数添加了第二幅图形dp,位置在(0.5,0.5)宽度为0.5,高度为0.5。然后使用draw_plot
函数绘制了第三幅图形,lp,位置是(0,0),宽度为1,长度高度为0.5.最后使用draw_plot_label函数为图形添加标签label参数用于指定标签的名称。代码x = c(0.1, 0.5, 0), y = c(1, 1, 0.5)
,指定了三个标签的位置。例如第一个标签的位置是(0,1),size参数调整的标签的大小。
另外,如果需要保存图形的话,可以使用ggsave()
函数或者save_plot()
函数。ggsave函数是ggplot2自带的函数。如果合并了图形,则最好使用save_plot()
函数。下面的代码可以储存合并之后的图形。
p <- ggdraw()
draw_plot(bxp, x = 0, y = .5, width = .5, height = .5)
draw_plot(dp, x = .5, y = .5, width = .5, height = .5)
draw_plot(lp, x = 0, y = 0, width = 1, height = 0.5)
draw_plot_label(label = c("Box plot", "Jitter plot", "Time series"),
x = c(0.1, 0.5, 0), y = c(1, 1, 0.5), size = 15)
save_plot("plot2by2.pdf", plot2by2,
ncol = 2, # we're saving a grid plot of 2 columns
nrow = 2, # and 2 rows
# each individual subplot should have an aspect ratio of 1.3 base_aspect_ratio = 1.3
)
二、 gridExtra 包
使用gridExtra包同样可以将多幅图形合并起来。关键函数则是:grid.arrange()
。下面的代码使用了grid.arrange
函数来合并上文的三幅函数加上下面的代码新绘制的一幅直方图,如图3所示。
# 定义一组5种颜色
my5cols <- c("#6D9EC1", "#646567", "#A29B32", "#E46726", "#F3BF94")
# 绘制图形
data("diamonds")
brp <- ggplot(diamonds, aes(x = x))
geom_bar(aes(fill = cut)) scale_fill_manual(values = my5cols)
require(gridExtra)
grid.arrange(bxp, dp, lp ,brp, ncol = 2, nrow =2)
图3 合并多幅图形
gridExtra包中有一个函数很好用,arangeGrop()
函数。可以在图形中将图形分块。例如,如果希望首先将图形分成两块,在左边放一幅子图。然后在右边分两块,绘制两幅子图。则可以使用arangeGrop()
函数轻松的实现,下面的代码在图形的左侧放置了一幅点图,在右侧放置了两幅图形,箱线图的直方图,如图4所示。
grid.arrange(dp, arrangeGrob(bxp, brp), ncol = 2)
图4 合并多幅图形
上面的代码在使用grid.arrange
函数合并图形的时候,使用arrangeGrob
函数首先将dp和brp这两幅图合并在一起,然后再和bxp图形合并在一起。
从图中可以看到,左边只有一幅图形,而右边有两幅图形。另外你,使用grid.arrange
函数的layout_matrix
参数同样可以进行这样的设置,如图5所示。
grid.arrange(brp, bxp, dp,lp, ncol = 2, nrow = 2, layout_matrix = rbind(c(1,1), c(2,3,4)))
图5 合并多幅图形
上面的代码将使用了grid.arrange
函数合并四幅图形。参数ncol=2和nrow =2 表示将整个图形分成四个部分。代码
’layout_matrix = rbind(c(1,1,1), c(2,3,4)’
设置了这四个部分是如何显示图形的.这里表示将第一幅图设置为第一行,将第2,3,4幅图显示在第四行,如图所示,整个图形的上方显示了直方图,下方显示了三幅图形。
需要注意的layout_matrix本质上是要传入一个矩阵,用于描述每一行或者每一列绘制什么图形。
代码语言:javascript复制rbind(c(1,1,1), c(2,3,4))
## [,1] [,2] [,3]
## [1,] 1 1 1
## [2,] 2 3 4
rbind函数本质上是创建了一个二行三列的矩阵矩阵,如果希望图形划分成为一个三行的图形。首先创建一个三行的矩阵。
代码语言:javascript复制cbind(c(1,1,1), c(2,3,4))
## [,1] [,2]
## [1,] 1 2
## [2,] 1 3
## [3,] 1 4
上面的代码创建了一个三行的矩阵,矩阵的第一列都是1.然后传入layout_matrix参数,如图6所示。
代码语言:javascript复制grid.arrange(brp, bxp, dp,lp, ncol = 2, nrow = 2, layout_matrix = cbind(c(1,1,1), c(2,3,4)))
图6 合并多幅图形
从图中可以看到,图形的左方变成了直方图,这是因为矩阵的第一列都是1。右边由于三幅图形构成。
三、添加边缘分布图
在绘制散点图的时候,如果希望进一步了解单个变量的分布,可以在散点图中添加边际分布图。使用ggExtra包可以非常轻松的在图形中添加边缘分布图,可以添加的图形包括直方图,箱线图和密度图。
下面的代码首先绘制了一幅散点图,然后添加了边际图形,如图7所示。
代码语言:javascript复制library("ggExtra") # 加载包
# 创建数据集
set.seed(1234)
x <- c(rnorm(350, mean = -1), rnorm(350, mean = 1.5),
rnorm(350, mean = 4))
y <- c(rnorm(350, mean = -0.5), rnorm(350, mean = 1.7), rnorm(350, mean = 2.5))
group <- as.factor(rep(c(1, 2, 3), each = 350))
df2 <- data.frame(x, y, group)
scatterPlot <- ggplot(df2, aes(x, y)) geom_point(aes(color = group)) scale_color_manual(values = my3cols) theme(legend.position=c(0,1), legend.justification=c(0,1))
# 添加边际分布图
ggMarginal(scatterPlot)
# 添加边际分布
ggMarginal(scatterPlot, type = "histogram", fill = "#6D9EC1", color = "#BFD5E3")
图7 添加边际图形
上面的代码中使用了ggMarginal
函数为散点图添加编辑图形。默认添加的是密度曲线。代码’ggMarginal(scatterPlot)’
表示为图形添加密度曲线。代码
’ggMarginal(scatterPlot, type = “histogram”, fill = “#6D9EC1”, color = “#BFD5E3”)’
表示为图形添加边际分布,分别调用多个ggMarginal
函数的时候,图形是会叠加的。从图中可以看到,散点图同时添加了密度曲线和直方图。
四、 在ggplot中插入一个外部图形元素
使用annotation_custom()
函数,可以在图中添加表,图和其他的元素。函数的格式如下:
annotation_custom(grob, xmin, xmax, ymin, ymax)
- grob:要显示的外部图形元素。
- xmin, xmax:数据坐标中的x位置(水平位置)。
- ymin, ymax:数据坐标中的y位置(垂直位置)。
通过下面的步骤可以在一幅散点图中添加图形元素:
- 首先创建一幅散点图。
- 在散点图中添加一个关于x轴的箱线图。
使用annotation_custom()
函数函数添加图形元素,由于添加一个箱线图会与原来的图形有一些点重叠,因此可以调整图形的透明度。如下图8所示。
require(hrbrthemes)
## Loading required package: hrbrthemes
## NOTE: Either Arial Narrow or Roboto Condensed fonts are required to use these themes.
## Please use hrbrthemes::import_roboto_condensed() to install Roboto Condensed and
## if Arial Narrow is not on your system, please see http://bit.ly/arialnarrow
p1 <- scatterPlot # 散点图
# 绘制箱线图
p2 <- ggplot(df2, aes(factor(1), x)) geom_boxplot(width=0.3,color = 'black') coord_flip() theme_ipsum()
# 箱线图
p3 <- ggplot(df2, aes(factor(1), y)) geom_boxplot(width=0.3,color = 'red') theme_ipsum()
# 创建图形元素
p2_grob = ggplotGrob(p2)
p3_grob = ggplotGrob(p3)
# 合并图形
p1 annotation_custom(grob = p2_grob, xmin = 0, xmax =7,ymin = -4, ymax = 0)
图8 添加图形元素
上面的代码首先使用散点图中x轴对应的数据创建了一幅箱线图,然后使用y轴对应的变量绘制了一幅箱线图。然后将图形使用ggplotGrob函数转换成为一个图形元素(grob对象)。最后使用annotation_custom函数添加创建好的图形元素。代码
代码语言:javascript复制’p1 annotation_custom(grob = p2_grob, xmin = 0, xmax = 5,ymin = -2, ymax = 0)’
表示将p2_grob这个图形元素添加到p1中。通过xmin,xmax,ymin和ymax这几个参数调整了图形元素的位置。从图中可以看到,添加的箱线图被放在图形的右下方,如图9所示。
代码语言:javascript复制# 在散点图中插入p3_grob
p1 annotation_custom(grob = p3_grob,
xmin = -6, xmax = -2,ymin = -3, ymax =2)
图9 添加图形元素
从图可以看到,箱线图被添加到了图形的左下角的位置。使用这种方式可以以任意的方式合并图形。在这种情况下,需要注意的是,图形之间可能存在覆盖的显现,这种情形是需要避免的。
在本章节中,介绍了合并多幅图形的内容,本章节的内容是数据可视化过程中非常重要的一个步骤,将多幅图形合并成一幅图形也是比较多幅的一个重要的方法。