R语言ggplot2 | 如何自定义facet分面的坐标轴范围

本文详细介绍如何在ggplot2中使用geom_blank解决 facet_wrap和facet_grid分面后的y轴刻度问题,包括构建数据、调整比例、geom_blank应用技巧,并推荐ggh4x的facetted_pos_scales函数。特别关注字符型x轴的处理和图例管理。
  • 💂 个人信息酷在前行
  • 👍 版权: 博文由【酷在前行】原创、需要转载请联系博主
  • 👀 如果博文对您有帮助,欢迎点赞、关注、收藏 + 订阅专栏


   ggplot2包是R语言中最强大的绘图包之一,具有各种各样的功能,能够模仿甚至超越目前99%的绘图软件。它的 底层逻辑类似ps的图层逻辑,通过一层又一层的函数,不断地覆盖并完善图形。而 facet( 分面)是ggplot2包中一个常用的函数,当绘制的变量分类较多时,可以利用 facet相关函数完成各子图的绘制,使数据展现得更美观和协调。由于不同指标数据可能存在数量级差异,因此今天主要与大家分享如何使用 geom_blank 函数在各个分面获得更好的 y轴自定义范围。同时,在使用geom_blank()的时候容易出现出现问题,例如,绘制的图是以x轴变量为数值型的都容易解决,特别是当 x轴变量为字符型数据时容易出现问题。除此之外,再给大家推荐一个 ggh4x包中 facetted_pos_scales( ) 函数,可以理解为是geom_blank()函数的升级版。希望今天的分享能给大家带来帮助~

🐣 一、ggplot2中的分面类型

大家可能好奇为什么要做分面?分面作用是什么?

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

1) facet_wrap – 封装类型

生成一个 1 维的多宫格,然后按行或按列顺序添加子图。具体如下图:
在这里插入图片描述
2) facet_grid – 网格类型

生成一个 2 维的多网格,通过行列对应不同因子型的变量。具体如下图:
在这里插入图片描述
  特别提醒 :这两张图具体区别为一个是生成1维的,另一个是生成2维的。换句话是,如果想要分别用两个变量来表征多个子数据集时可以优先考虑用facet_grid函数来绘图。当然,如果想用facet_wrap函数来绘制2维也可以,但是绘图的效果可能不好

  接下来,与大家简单分享下分面示例:

  🚙 1、绘制单独的面板 – facet_null ()

#使用R语言中自带的数据集"mpg"
#加载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)

在这里插入图片描述

🐤 二、利用geom_blank解决facet分面的y轴刻度(方法1)

  关于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轴不变的情况下变化
theme_bw()

在这里插入图片描述
这里可以利用?facet_wrap查看相关函数的介绍 , scales = “free” 对应的介绍也非常详细~可以看出来由于图形比例不当,导致的绘图并不美观。

  🏎️ 3、调整图形坐标轴(y轴)的比例

如果使用单一面板或所有相同比例的数据时,通常使用 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()

  🚓 4、关于geom_blank()中数据集的几点考虑

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

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

3)要考虑清楚各分面哪个范围的图看起来更加美观,比例更加协调
在这里插入图片描述
最后,我们可以看到,geom_blank() 完美呈图了,也满足了我们的需求。

  🚒 5、geom_blank() 使用的常见问题及注意事项

背景介绍

这是一位公众号的粉丝,她使用geom_blank时所遇到的一些问题。她希望将各分面的y轴范围调整合适的比例,并且能够放置图例和误差棒以及显著性字母的标签。这里需要声明:数据的构建全是为了更好的展示这次粉丝所遇到的问题,并不是实际的数值,供大家参考和学习。

经过调整,我们构建了一份新的数据进行描绘,并逐一解决以x轴为字符型变量所遇到的相关问题。

library(ggplot2)
str(create_dat)

在这里插入图片描述
首先,我们先绘制使用geom_blank()前的结果:

Total <- ggplot(data = create_dat, aes(x = variable, y = value, fill = content))+
    geom_bar(color = "black", size=.5,position = position_dodge(0.9), stat = "identity")+
    #geom_boxplot(outlier.size = 2,position=position_dodge(0.8),size=0.5,width=0.5)+
    geom_errorbar(aes(ymax = value + sd, ymin = value),
                 position = position_dodge(0.9), width = 0.25)+
    facet_wrap(type ~ ., scales = "free_y")+
    # geom_blank(data = blank_data, aes(x = x, y = y)) +
    scale_fill_manual(values = c('white',"grey","#3B3838")) + #'#999999',"#6495ED","#00B050"
    # scale_color_manual(values = c('black',"black","black"))+
    geom_text(aes(label = label1 , y=value+sd+0.5), position = position_dodge(0.9), size=4.925)+#添加误差棒及显著性差异字母
    # geom_text(aes(label = label2 , y=value+sd+1.0), position = position_dodge(0.9), size=4.925)+#添加误差棒及星号label(**)
    # theme_classic()+
  theme_test()+
   # theme(legend.position = "none")+
    theme(legend.title = element_blank(),legend.background=element_blank(),legend.position = c(.9,0.92),legend.direction="vertical")+#图例位置vertical,horizontal
   labs(x = "", y = "")+ #不添加x y轴标签
    theme(axis.title.x =element_text(size=14), axis.title.y=element_text(size=14),axis.text.x =element_text(size=14),
        axis.text.y =element_text(size=14),legend.text =element_text(size=14))+#标签字体大小
   theme(axis.text.x = element_text(size = 14,hjust = 0.5,vjust = 0.5,angle =0,color = "black"), axis.text.y = element_text(size = 14,hjust = 0.5,vjust = 0.5,color = "black"), strip.text = element_text(size = 14))+
   scale_y_continuous(expand = c(0,0),limits = c(0, 12),breaks=seq(0,12, 4))+# 剔除y轴0点处4%的缓冲区
  guides(fill = guide_legend(reverse = FALSE, keywidth = 1.5, keyheight = 0.5))#图例矩形宽度,图例边框粗细调节
Total

在这里插入图片描述
从这张图来看,就是针对各自分面中每个处理情况的显著性比较。能发现BF这个面板中的数值偏低,导致留白处偏多,但是我们如果就用scale_y_continuous是统一修改y轴的,并不能好的解决这个问题。

我们先构建一个blankdata,首先明确我对应需要分面的变量名和具体的字符向量。经过查看,我们对type进行了分面。变量名为type,而具体的字符向量为"AF",“BF”,“CF”,“DF”。

blank_data <- data.frame(type = c("AF","AF","BF","BF","CF","CF","DF","DF"),
                         x=0, y = c(0, 8, 0,4,0,10,0,15))

为什么要对各字符构建两次,因为y轴范围是两个点。

如果需要调整顺序,应该对blankdata中对应要展示的因子型变量做个level,设定顺序。例如:blank_data $ type = factor(blank_data$type, levels = c(“AF”,“BF”,“CF”,“DF”))。由于ABCD本身就是按照字母顺序排列的,因此在本例中不需要额外对其设定顺序。

添加blankdata后,记得将scale_y_continous的limits范围删除。

Total <- ggplot(data = create_dat, aes(x = variable, y = value, fill = content))+
  geom_bar(color = "black", size=.5,position = position_dodge(0.9), stat = "identity")+
  geom_boxplot(outlier.size = 2,position=position_dodge(0.8),size=0.5,width=0.5)+
  geom_errorbar(aes(ymax = value + sd, ymin = value),
             position = position_dodge(0.9), width = 0.25)+
 facet_wrap(type ~ ., scales = "free_y")+
  geom_blank(data = blank_data, aes(x = x, y = y)) +
  scale_fill_manual(values = c('white',"grey","#3B3838")) + #'#999999',"#6495ED","#00B050"
  # scale_color_manual(values = c('black',"black","black"))+
 geom_text(aes(label = label1 , y=value+sd+0.5), position = position_dodge(0.9), size=4.925)+#添加误差棒及显著性差异字母
  # geom_text(aes(label = label2 , y=value+sd+1.0), position = position_dodge(0.9), size=4.925)+#添加误差棒及星号label(**)
#theme_classic()+
theme_test()+
#theme(legend.position = "none")+
theme(legend.title = element_blank(),legend.background=element_blank(),legend.position = c(.9,0.92),legend.direction="vertical")+#图例位置vertical,horizontal
  labs(x = "", y = "")+ #不添加x y轴标签
theme(axis.title.x =element_text(size=14), axis.title.y=element_text(size=14),axis.text.x =element_text(size=14), axis.text.y =element_text(size=14),legend.text =element_text(size=14))+#标签字体大小
theme(axis.text.x = element_text(size = 14,hjust = 0.5,vjust = 0.5,angle =0,color = "black"),
        axis.text.y = element_text(size = 14,hjust = 0.5,vjust = 0.5,color = "black"),
        strip.text = element_text(size = 14))+
 scale_y_continuous(expand = c(0,0))+# 剔除y轴0点处4%的缓冲区
  guides(fill = guide_legend(reverse = FALSE, keywidth = 1.5, keyheight = 0.5))#图例矩形宽度,图例边框粗细调节
Total
  🚈 1、问题一

  在绘图时,大多数情况都直接在底层ggplot()就设定好data。但是由于blankdata是新构建的数据,与底图描绘所用数据不同。如果提前在ggplot()加载好数据,这样会出现Error in unique.default(x, nmax = nmax) : unique() applies only to vectors 或者其他报错,例如Error in FUN(X[[i]], …) : object ‘xx’ not found等等,这里的解决方法为:将ggplot()函数底层铺好,但是在具体所需绘制图形上再添加数据,这样代码识别的时候就不会认为从头到尾都是用的同一份数据。

#只需要修改这几行代码即可
Total <- ggplot()+
geom_bar(data = create_dat, aes(x = variable, y = value, fill = content),
           color = "black", size=.5,position = position_dodge(0.9), stat = "identity")+
  geom_errorbar(data = create_dat, aes(ymax = value + sd, ymin = value),
                position = position_dodge(0.9), width = 0.25)+
geom_blank(data = blank_data, aes(x = x, y = y)) +
geom_text(data = create_dat, aes(label = label1 , y=value+sd+0.5),
            position = position_dodge(0.9), size=4.925)+#添加误差棒及显著性差异字母
Total

在这里插入图片描述
但是我们发现还是会报错,那么需要继续解决问题。

  🚊 2、问题二

  由于我们现在是对应需要描绘的函数直接添加data和aes(映射),因此需要写清楚x 和 y,否则就会出现 Error in check_required_aesthetics(): ! geom_errorbar requires the following missing aesthetics: x or y, xmin and xmax 等类似报错。最开始的时候在上一版的代码,并不需要在各描绘函数中添加x的对象,是因为我们在底层ggplot()中已经告知了,因此可以省略。一旦具体到每个描绘函数,则需要注意,添加上x对象。

#对这几行代码中aes()映射部分添加x对象
geom_errorbar(data = create_dat, aes(ymax = value + sd, ymin = value, x = variable),
                position = position_dodge(0.9), width = 0.25)+
geom_text(data = create_dat, aes(label = label1 , y=value+sd+0.5, x = variable), position = position_dodge(0.9), size=4.925)+#添加误差棒及显著性差异字母

在这里插入图片描述
这时候已经不再报错,出图了。但是可以发现图是有很多细节问题的。我们继续针对问题完善。

  🚘 3、问题三

  在构建blankdata的时候,我使用的是data.frame()函数来构建一个数据框,对应里面的变量名及其字符型向量要与底层所绘数据的变量名,保持完全一致,否侧就会出错。例如,我们先尝试第一种错误,改动变量名称。将type改成typ,结果可以看到图中各分面的y轴范围并没有改变。我们将typ改成type(这里是与底图数据一致的)再看看,从图中来看是成功了。

blank_data <- data.frame(typ = c("AF","AF","BF","BF","CF","CF","DF","DF"),
                         x=0, y = c(0, 8, 0,4,0,10,0,15)) # type写少一个e

在这里插入图片描述

  🚡 4、问题四

  如果我们对应变量名中的字符向量写错了又是什么结果?我们会发现出现一个空白框,这意味着构建的blankdata出现了问题,有些分面的y轴成功修改了,有的并没有。

blank_data <- data.frame(type = c("GF","GF","BF","BF","CF","CF","DF","DF"),
                         x=0, y = c(0, 8, 0,4,0,10,0,15)) # AF改成GF

在这里插入图片描述

  🚋 5、问题五

  我们会发现如果x轴的变量为字符型数据时,x轴0点到第一个变量的留白处比较多。经过反复尝试,我们发现当x轴的变量不是数值型时,即本例中的字符型数据,x轴并不是从0点开始的,可能从零点几或者是1开始的。于是,根据这样的推断,我将x=0,调整成x=1,发现可以看到x轴左右俩边的留白处基本一致了,这也证实了我们前面的猜测。

blank_data <- data.frame(type = c("AF","AF","BF","BF","CF","CF","DF","DF"),
                         x=1, y = c(0, 8, 0,4,0,10,0,15)) # x=0改成x=1

在这里插入图片描述

  🚔 6、问题六

  图目前都在往好的方向完善,但是我们发现我们的误差棒和label都重叠了,即使添加了position=
position_dodge(0.9)也没有将误差棒和标签对应,按理A或者B中三个处理 C0 C100 C200对应分开。这里,我们可以添加color的对象,分别为C0 C100 C200,应该就可以分开(记得添加color的自定颜色,因为这里是A下的三个处理,应该有三种颜色)。那么,误差棒的颜色应该是一样的,所以我们在绘制误差棒和标签的时候添加即可,Bingo!!!

#主要修改以下代码
geom_errorbar(data = create_dat, aes(ymax = value + sd, ymin = value, x = variable, color = content),
                position = position_dodge(0.9), width = 0.25)+ # color参数的添加
scale_color_manual(values = c('black',"black","black"))+
geom_text(data = create_dat, aes(label = label1 , y=value+sd+0.5, x = variable, color = content), position = position_dodge(0.9), size=4.925)+#添加误差棒及显著性差异字母    

在这里插入图片描述
基本解决了所有的问题,最后可以将图例删除,然后在AI或者PPT中重新绘制。也可以选择以PPT格式导出当前plot区域的图,然后删除图例中不需要的部分即可。关于PPT的导出可以使用一下code:

library(export)
graph2ppt(file="aa.ppt", width=5, height=5)

关于geom_blank()的使用还是因人而异,根据大家实际情况来选择最佳的方案就可以了~

🐤 三、利用ggh4x包的facetted_pos_scales函数(方法2)

除此之外,还可以使用ggh4x自带的facetted_pos_scales( ) 来自定义分面图形的y轴刻度。

#利用自带的数据集airquality
#将数据集中前4个变量转化成长数据,方便会
library(tidyverse)
air <- airquality %>% 
  pivot_longer(cols = 1:4,names_to = "Air_Env_vars",
              values_to = "Changes_by_time")
head(air)

在这里插入图片描述

p1 <- ggplot(air, aes(x = Day,y = Changes_by_time, 
                color = as.factor(Month),group = Month)) + 
 geom_point(size=3.5) + 
geom_line() +
 scale_color_brewer(palette = "Set2", labels = c("May","June","July","August","September")) +
 theme_bw()+
 facet_wrap(~ Air_Env_vars, nrow = 2, 
             scales = "free_y",#strip.position = "left", 
             labeller = as_labeller(c(Temp = "Temperature (°C)", 
                                      Solar.R = "Solar radiance (W/m2)",
                                      Wind = "Wind (m/s)",
                                      Ozone = "Ozone (ppb)"))) + 
 theme(#strip.background = element_blank(),
        #strip.placement = "outside",
        legend.position = "top",
        legend.title = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())+`
  labs(x = "Day of month", y = "Climate Changes")
p1

在这里插入图片描述
然后利用ggh4x包中的facetted_pos_scales( ) 函数,可以一步到位,而且方便改其他类型的变量(不限于数值型label)。如下图:

library(ggh4x)
p1 + facetted_pos_scales(
  y = list(Air_Env_vars == "Ozone" ~ scale_y_continuous(limits=c(0,170),breaks=seq(0,170,40)),
           Air_Env_vars == "Solar.R" ~ scale_y_continuous(limits=c(0, 350),breaks=seq(0,350,50)),
           Air_Env_vars == "Temp" ~ scale_y_continuous(limits=c(50, 110),breaks=seq(50,110,20)),
           Air_Env_vars == "Wind" ~ scale_y_continuous(limits=c(0,25),breaks=seq(0,25,5),
                                                   labels = letters[1:6])))

在这里插入图片描述
从代码的简洁度来看,方法二确实优势。需要注意的是:在自定义y轴范围的时候要保证对应的变量名称一直,另外是以一个list打包的。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷在前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值