分享

ggplot2--facet各分面的坐标轴自定义设置

 科白君 2022-02-19




 不求做的最好,但求做的更好。”   --科白君


"ggplot2绘图"专题·第1篇
  编辑 | Dr.Cool

今日分享
ggplot2包是R语言一个强大的绘图包,具有各种各样的功能,能够模仿甚至超越目前存在99%的绘图软件。它的底层逻辑类似ps的图层逻辑,通过一层又一层的函数,不断地覆盖并完善图形,直至完成。而facet(分面)是ggplot2中常用的一个函数,通常绘制的指标过多时,我们可以利用facet相关函数完成各子图的绘图,使得数据展示完整且美观。由于不同指标可能存在数据的数量级差异,因此今天主要与大家分享如何使用 geom_blank 函数在各个分面获得更好的坐标轴范围。

01

ggplot2的分面类型


大家可能会好奇为什么进行分面?分面有什么作用?

这里可以大声告诉你,在做报告和发表专业期刊甚至是顶级期刊(Nnature、Science、Cell、PNAS)时的很多图形中都用到了分面。它就好比九宫格甚至可以设置更多图像,先将数据划分成多个子集,并将每个子数据集填充绘制到页面各小图形中。通常我们绘图时用到的分面有两种类型:

1)facet_wrap -- 封装类型

生成一个 1 维的多宫格,然后按行或按列顺序添加子图。具体如下图:

2)facet_grid -- 网格类型

生成一个 2 维的多网格,通过行列对应不同因子型的变量。具体如下图:

特别提醒:两者具体区别也可以看出,一个是生成1维的,另一个是生成2维的。也就是说,如果想要分别用两个变量来表征多个子数据集时可以优先考虑用facet_grid函数来绘图。当然,如果想用facet_wrap函数来绘制2维也可以,但是绘图的效果可能不好。接下来,先与大家简单分享下分面示例:

这里使用R语言自带的数据集:"mpg"

1) 绘制单独的面板 -- facet_null ()

# 加载R包
library(ggplot2)

# 绘制散点图
ggplot(mpg, aes(cty, hwy)) +
  geom_point() +
  facet_null() # 绘制单一面板

2) facet_wrap 单一变量:var ~ .

ggplot(mpg, aes(cty, hwy)) + 
  geom_point() +
  facet_wrap(cyl ~ .)

3) facet_wrap 多变量:var(var1 , var2) 或者 var1~var2

ggplot(mpg, aes(cty, hwy)) + 
  geom_point() +
  #facet_wrap( cyl ~ drv)
  facet_wrap(vars(cyl, drv))

4) facet_grid 一行多列或者是多行一列:. ~ var 或 var ~ .

ggplot(mpg, aes(cty, hwy)) + 
  geom_point() +
  # facet_grid( . ~ cyl)
  facet_grid(cyl ~ .)

5) facet_grid 多行多列:. ~ var 或 var ~ .

ggplot(mpg, aes(cty, hwy)) + 
  geom_point() +
  facet_grid(drv ~ cyl)

02

facet分面设定更好的坐标轴范围


关于facet分面其他的一些细节,在这里我们不赘述了,网上也有很多相关介绍。今天主要想和大家分享一下有关facet分面中如何设定更好的坐标轴范围。该函数有一个参数--scales,它主要作用是可以根据不同子图(各分面的数据集范围)对x和y轴进行自动生成(但是自动生成的范围不一定是我们想要的,如导致图的比例不美观等等)。而平时我们使用的scale_y_continuous都是对一个图形背景所有的范围进行设置,不能很好的解决这个问题。这里,我们推荐使用geom_blank函数来完成该功能。

1) 我们先构建一个数据集:由于是随机构建的,可以设置种子保证每次结果复现一致

set.seed(20220219)
create_df <- rbind(data.frame(group="a", x = runif(100), y = rnorm(100, mean = 5)),
                  data.frame(group="b", x = runif(100), y = rnorm(100, mean = 5, sd = 3)+20),
                  data.frame(group="c", x = runif(100), y = rnorm(100, mean = 5, sd = 5)+30))
str(create_df)

2) 紧接着把构建的数据绘制成图

ggplot() + 
  geom_point(data = create_df, aes(x = x, y = y, colour = group), size = 3) +
  facet_wrap( ~ group, scales = "free_y") + # 使用scale ="free_y"允许y轴在保持x轴不变的情况下变化(意思可以更改y轴范围)
  theme_bw()

这里可以利用?facet_wrap查看相关函数的介绍 , scales = "free" 对应的介绍也非常详细~可以看出来由于图形比例不当,导致的图结果并不美观。

3) 调整图形坐标轴的比例,如果使用单一面板或所有相同比例的数据时,通常使用 coord_cartesian 函数。但是当具有不同比例的面板时,这个效果并不好,它强制所有内容都处于相同的比例。结果如下:

ggplot() + 
  geom_point(data = create_df, aes(x = x, y = y, colour = group), size = 3) +
  facet_wrap( ~ group, scales = "free_y") +
  coord_cartesian(ylim = c(0, 75)) +
  theme_bw()

从图来看,点确实是聚集起来了,但是各分面都有多余的空间。使得图看起来不协调。

另一种我们还可以选择使用 expand_limits() 强制各分面从原点开始。使用 scale_y_continuous(expand=c(0,0)) 删除 y 轴限制的缓冲区。其中expand 的两个值是 c(乘数缓冲区,加法缓冲区)。通过包含 c(0,0),我们不包含轴刻度上的任何缓冲区。结果如下:

ggplot() + 
  geom_point(data = create_df, aes(x = x, y = y, colour = group), size = 3) +
  facet_wrap( ~ group, scales = "free_y") +
  expand_limits(y = 0) +
  scale_y_continuous(expand = c(0, 0)) +
  theme_bw()

从图来看还是不能很好的解决不同比例的y轴范围。为了解决这些问题,可以使用 geom_blank,它能够完美的解决各分面的坐标轴范围问题。首先创建一个数据集,其中包含我们数据集中每个组级别的 y 轴的最小值和最大值。然后使用 geom_blank 将其传递给 ggplot 就可以完美解决了。

blank_data <- data.frame(group = c("a", "a", "b", "b", "c", "c"), 
                         x = 0, y = c(2, 8, 10, 40, 20, 50))

ggplot() +
  geom_point(data = create_df, aes(x = x, y = y, colour = group), size = 3) +
  geom_blank(data = blank_data, aes(x = x, y = y)) +
  facet_wrap( ~ group, scales = "free_y") +
  scale_y_continuous(expand = c(0, 0)) +
  theme_bw()

关于数据集的设定我们需要考虑几点

1)group分组的名称要一一对应

2)对应y轴的值要有两个点(最小值和最大值)

3)要考虑清楚各分面哪个范围的图看起来更加美观,比例更加协调

感兴趣的同学可以通过以下方式与我们沟通、交流和学习:

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多