可视化绘制技巧|对多图合理排版布局

2021-02-22 11:53:18 浏览数 (2)

通常而言,在绘制图形的时候都是绘制某一种类型的一张图形,例如绘制一张散点图,绘制直方图。但有的时候我们希望同时展示多幅图形,可能是因为这些图形有某种联系,需要共同展示才能够更好的表达数据中蕴含的信息。之前介绍的边际图形就是这样的一个例子。本章节会介绍,当我们绘制了好了多幅图形之后,如何将多幅图形合并起来。

一、 合并多幅图形到一张图中

如果使用的是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()函数的格式如下:

代码语言:javascript复制
draw_plot(plot, x = 0, y = 0, width = 1, height = 1)

参数含义如下:

  • plot:要放置的plot (ggplot2图形或gtable图形)。
  • x,y: 用于指定图形的位置。
  • width,height:图形的宽度和高度。

draw_plot_label()函数的格式如下:

代码语言:javascript复制
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()函数。下面的代码可以储存合并之后的图形。

代码语言:javascript复制
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所示。

代码语言:javascript复制
# 定义一组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所示。

代码语言:javascript复制
grid.arrange(dp, arrangeGrob(bxp, brp), ncol = 2)

图4 合并多幅图形

上面的代码在使用grid.arrange函数合并图形的时候,使用arrangeGrob函数首先将dp和brp这两幅图合并在一起,然后再和bxp图形合并在一起。

从图中可以看到,左边只有一幅图形,而右边有两幅图形。另外你,使用grid.arrange函数的layout_matrix参数同样可以进行这样的设置,如图5所示。

代码语言:javascript复制
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 表示将整个图形分成四个部分。代码

代码语言:javascript复制
’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)’表示为图形添加密度曲线。代码

代码语言:javascript复制
’ggMarginal(scatterPlot, type = “histogram”, fill = “#6D9EC1”, color = “#BFD5E3”)’

表示为图形添加边际分布,分别调用多个ggMarginal函数的时候,图形是会叠加的。从图中可以看到,散点图同时添加了密度曲线和直方图。

四、 在ggplot中插入一个外部图形元素

使用annotation_custom()函数,可以在图中添加表,图和其他的元素。函数的格式如下:

代码语言:javascript复制
annotation_custom(grob, xmin, xmax, ymin, ymax)
  • grob:要显示的外部图形元素。
  • xmin, xmax:数据坐标中的x位置(水平位置)。
  • ymin, ymax:数据坐标中的y位置(垂直位置)。

通过下面的步骤可以在一幅散点图中添加图形元素:

  • 首先创建一幅散点图。
  • 在散点图中添加一个关于x轴的箱线图。

使用annotation_custom()函数函数添加图形元素,由于添加一个箱线图会与原来的图形有一些点重叠,因此可以调整图形的透明度。如下图8所示。

代码语言:javascript复制
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 添加图形元素

从图可以看到,箱线图被添加到了图形的左下角的位置。使用这种方式可以以任意的方式合并图形。在这种情况下,需要注意的是,图形之间可能存在覆盖的显现,这种情形是需要避免的。

在本章节中,介绍了合并多幅图形的内容,本章节的内容是数据可视化过程中非常重要的一个步骤,将多幅图形合并成一幅图形也是比较多幅的一个重要的方法。

0 人点赞