查看原文
其他

基于 R 语言的科研论文绘图技巧详解(1)

庄闪闪 庄闪闪的R语言手册 2023-04-03

点击下方公众号,回复资料分享,收获惊喜

简介

在查阅文献的过程中,看到了几幅非常不错的出版图,今天就跟着小编一起学习下,他们是怎么使用 R 绘制出来的。

今天主要介绍第一幅图(A),初步观察来看,改图是由两张照片合并而成,并且在上面加上了箭头、圆圈,来说明作者想表达的问题。

后面几幅图会一一介绍,读者在学习过程中,可以将内部学到的知识点应用到自己的图形绘制中。

那我们来看看,他是怎么实现这个功能的吧,对应代码可在 GitHub - marco-meer/scifig_plot_examples_R: Scientific publication figure plotting examples with R[1] 可以找到。

主要知识点

  • 学会如何导入图形,并将其并排展示;
  • 学会设置自定义主题,简化代码,统一主题,方便绘制其他图形使用;
  • 学会使用 ggplot2 包内置参数添加文字已经其他其他修饰图标。

绘图

加载包

首先加载一些需要使用到的包。

library(ggplot2) # Grammar of graphics
library(cowplot) # Arranging multiple plots into a grid
library(png) # Load JPEG, PNG and TIFF format
library(scales) # Generic plot scaling methods
library(viridis) # Default color maps from 'matplotlib'
library(grid) # A rewrite of the graphics layout capabilities
library(magick) # graphics and image processing
library(rsvg) # Render svg image into a high quality bitmap
library(ggforce) # Collection of additional ggplot stats + geoms

设置主题

接下来,为了方便起见,作者在绘图前设置好了主题,并将该函数命名为 my_theme

这个主题并没有在第一幅图中使用,但是在后面几幅图中都会使用,这里先将其展示下。使用方式会在下一篇推文中进行介绍。

手动修改大部分面板,具体可以参考本篇文章[2]。或者观看我在 B 站发布的《R 语言可视化教程》,里面也有一些简单主题设置介绍。

# 全局字体大小
base_size = 12

# 手动修改大部分面板
# documentation: https://ggplot2.tidyverse.org/reference/theme.html
my_theme <- function() {
theme(
aspect.ratio = 1,
axis.line =element_line(colour = "black"),

# shift axis text closer to axis bc ticks are facing inwards
axis.text.x = element_text(size = base_size*0.8, color = "black",
lineheight = 0.9,
margin=unit(c(0.3,0.3,0.3,0.3), "cm")),
axis.text.y = element_text(size = base_size*0.8, color = "black",
lineheight = 0.9,
margin=unit(c(0.3,0.3,0.3,0.3), "cm")),

axis.ticks = element_line(color = "black", size = 0.2),
axis.title.x = element_text(size = base_size,
color = "black",
margin = margin(t = -5)),
# t (top), r (right), b (bottom), l (left)
axis.title.y = element_text(size = base_size,
color = "black", angle = 90,
margin = margin(r = -5)),
axis.ticks.length = unit(-0.3, "lines"),
legend.background = element_rect(color = NA,
fill = NA),
legend.key = element_rect(color = "black",
fill = "white"),
legend.key.size = unit(0.5, "lines"),
legend.key.height = NULL,
legend.key.width = NULL,
legend.text = element_text(size = 0.6*base_size,
color = "black"),
legend.title = element_text(size = 0.6*base_size,
face = "bold",
hjust = 0,
color = "black"),
legend.text.align = NULL,
legend.title.align = NULL,
legend.direction = "vertical",
legend.box = NULL,
panel.background = element_rect(fill = "white",
color = NA),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.title = element_text(size = base_size,
color = "black"),
)
}

绘图步骤详解

由于代码复杂,知识点较多,为了读者更好理解代码逻辑和含义,小编将其分布讲解。最后再将完整代码放到本节末。

导入图片

首先使用 magick 包中的 image_read() 导入两幅图,并通过image_flip()进行转化。

img1 <- magick::image_flip(magick::image_read("./image1.jpg"))
img2 <-  magick::image_flip(magick::image_read("./image2.png"))

接下来,将两幅图并行合并,放置到一幅图中。这里的代码,小编也是第一次见。通过 grid 包中的 grid.raster() 设置光栅(raster)对象,并使用 annotation_custom()设置摆放位置。这时候得到的结果是

ggplot() +
  annotation_custom(rasterGrob(image =  img1, 
                               x=0.27,
                               y=0.49,
                               width = unit(0.45,"npc"),
                               height = unit(0.87,"npc")), 
                    -InfInf, -InfInf) +
  annotation_custom(rasterGrob(image = img2, 
                               x=0.73,
                               y=0.49,
                               width = unit(0.45,"npc"),
                               height = unit(0.87,"npc")), 
                    -InfInf, -InfInf

其他修饰图标

加入修饰图标,来说明问题。主要使用 geom_ellipse() 构建椭圆形,geom_segment() 增加线段(箭头设置,在内部参数 arrow 中)。

geom_ellipse(aes(x0 = 0.25
                 y0 = 0.3,
                 a = 0.1
                 b = 0.04
                 angle = 0),
              color="yellow",
              size=1) +
  scale_x_continuous(limits = c(0,1))+ # 设置 x 轴坐标范围
  scale_y_continuous(limits=c(0,1)) +
  geom_segment(aes(x=0.15,
                   xend=0.2,
                   y=0.75,
                   yend=0.7),
               arrow = arrow(length=unit(0.30,"cm"),
                             ends="last"
                             type = "closed"),
               size = 1,
               color="white") +
  geom_segment(aes(x=0.3,
                   xend=0.9,
                   y=0.7,
                   yend=0.7),
               arrow = arrow(length=unit(0.30,"cm"),
                             ends="both"
                             type = "closed"),
               size = 1,
               color="red"

添加文字

使用 annotate() 添加文字("text"),使用 geom_segment() 添加线段(右下角白色的线段),这里没有设置箭头。

annotate("text", x = 0.25, y = 0.5, label = "PNG",color="white") +
  annotate("text", x = 0.75, y = 0.5, label = "JPEG",color="white") +
  annotate("text", x = 0.25, y = 1, label = "image 1",color="black") +
  annotate("text", x = 0.75, y = 1, label = "image 2",color="black") +
  annotate("text", x = 0.39, y = 0.07, label = "20~mu*m",color="white",parse=T) +
  annotate("text", x = 0.89, y = 0.07, label = "20~mu*m",color="white",parse=T) +
  geom_segment(aes(x=0.33,xend=0.45,y=0.03,yend=0.03), size = 2,color="white") +
  geom_segment(aes(x=0.83,xend=0.95,y=0.03,yend=0.03),size = 2,color="white"

这时候得到的结果为:

微调主题

去除主题,并修改图形边界。

  theme_void() +# blank plot w/o axes etc.
  theme(plot.margin = unit(c(-0,0,1,0), "cm"),
        aspect.ratio = 1)

完整代码

library(ggplot2) # Grammar of graphics
library(cowplot) # Arranging multiple plots into a grid
library(png)     # Load JPEG, PNG and TIFF format 
library(scales)  # Generic plot scaling methods
library(viridis) # Default color maps from 'matplotlib'
library(grid)    # A rewrite of the graphics layout capabilities
library(magick)  # graphics and image processing
library(rsvg)    # Render svg image into a high quality bitmap
library(ggforce) # Collection of additional ggplot stats + geoms

# global font size
base_size = 12 

# Manual theme for most panels
# documentation: https://ggplot2.tidyverse.org/reference/theme.html
my_theme <-  function() {
  theme(
    aspect.ratio = 1,
    axis.line =element_line(colour = "black"),  
    
    # shift axis text closer to axis bc ticks are facing inwards
    axis.text.x = element_text(size = base_size*0.8, color = "black"
                               lineheight = 0.9,
                               margin=unit(c(0.3,0.3,0.3,0.3), "cm")), 
    axis.text.y = element_text(size = base_size*0.8, color = "black"
                               lineheight = 0.9,
                               margin=unit(c(0.3,0.3,0.3,0.3), "cm")),  
    
    axis.ticks = element_line(color = "black", size  =  0.2),  
    axis.title.x = element_text(size = base_size, 
                                color = "black"
                                margin = margin(t = -5)), 
    # t (top), r (right), b (bottom), l (left)
    axis.title.y = element_text(size = base_size, 
                                color = "black", angle = 90,
                                margin = margin(r = -5)),  
    axis.ticks.length = unit(-0.3"lines"),  
    legend.background = element_rect(color = NA
                                     fill = NA), 
    legend.key = element_rect(color = "black",  
                              fill = "white"),  
    legend.key.size = unit(0.5"lines"),  
    legend.key.height =NULL,  
    legend.key.width = NULL,      
    legend.text = element_text(size = 0.6*base_size, 
                               color = "black"),  
    legend.title = element_text(size = 0.6*base_size, 
                                face = "bold"
                                hjust = 0
                                color = "black"),  
    legend.text.align = NULL,  
    legend.title.align = NULL,  
    legend.direction = "vertical",  
    legend.box = NULL
    panel.background = element_rect(fill = "white"
                                    color  =  NA),  
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    plot.title = element_text(size = base_size, 
                              color = "black"), 
  ) 
  
  
}

# Panel A ----

img1 <- magick::image_flip(magick::image_read("./image1.jpg"))
img2 <-  magick::image_flip(magick::image_read("./image2.png"))

panel_A <- ggplot() +
  annotation_custom(rasterGrob(image =  img1, 
                               x=0.27,
                               y=0.49,
                               width = unit(0.45,"npc"),
                               height = unit(0.87,"npc")), 
                    -InfInf, -InfInf) +
  annotation_custom(rasterGrob(image = img2, 
                               x=0.73,
                               y=0.49,
                               width = unit(0.45,"npc"),
                               height = unit(0.87,"npc")), 
                    -InfInf, -InfInf) +
  
  geom_ellipse(aes(x0 = 0.25
                            y0 = 0.3,
                            a = 0.1
                            b = 0.04
                            angle = 0),
                        color="yellow",
                        size=1)+
  scale_x_continuous(limits = c(0,1))+
  scale_y_continuous(limits=c(0,1)) +
  geom_segment(aes(x=0.15,
                   xend=0.2,
                   y=0.75,
                   yend=0.7),
               arrow = arrow(length=unit(0.30,"cm"),
                             ends="last"
                             type = "closed"),
               size = 1,
               color="white") +
  geom_segment(aes(x=0.3,
                   xend=0.9,
                   y=0.7,
                   yend=0.7),
               arrow = arrow(length=unit(0.30,"cm"),
                             ends="both"
                             type = "closed"),
               size = 1,
               color="red") +

  annotate("text", x = 0.25, y = 0.5, label = "PNG",color="white") +
  annotate("text", x = 0.75, y = 0.5, label = "JPEG",color="white") +
  annotate("text", x = 0.25, y = 1, label = "image 1",color="black") +
  annotate("text", x = 0.75, y = 1, label = "image 2",color="black") +
  annotate("text", x = 0.39, y = 0.07, label = "20~mu*m",color="white",parse=T) +
  annotate("text", x = 0.89, y = 0.07, label = "20~mu*m",color="white",parse=T) +
  geom_segment(aes(x=0.33,xend=0.45,y=0.03,yend=0.03), size = 2,color="white") +
  geom_segment(aes(x=0.83,xend=0.95,y=0.03,yend=0.03),size = 2,color="white")  + 
  theme_void() +# blank plot w/o axes etc.
  theme(plot.margin = unit(c(-0,0,1,0), "cm"),
        aspect.ratio = 1)

panel_A

小编有话说

本文主要学到的知识点如下:

  1. 使用 magick 包中的 image_read() 导入两幅图,并通过image_flip()进行转化;

  2. 设置自定义主题 my_theme,方便绘制其他图形使用;

  3. 使用 annotate() 添加文字("text"),使用 geom_segment() 添加线段。

参考资料

[1]

GitHub: https://github.com/marco-meer/scifig_plot_examples_R

[2]

文章: https://ggplot2.tidyverse.org/reference/theme.html


推荐: 可以保存以下照片,在 b 站扫该二维码,或者 b 站搜索【庄闪闪】观看 Rmarkdown 系列的视频教程。Rmarkdown 视频新增两节视频(写轮眼幻灯片制作)需要视频内的文档,可在公众号回复【rmarkdown



可视化推文推荐


R可视乎|空间地理数据可视化(1)


R可视乎|用R给心仪的对象表白吧


R可视乎|棒棒糖图


R可视乎|合并多幅图形


R可视乎|等高线图


R可视乎|气泡图


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存