基于 R 语言的科研论文绘图技巧详解(1)
点击下方公众号,回复资料分享,收获惊喜
简介
在查阅文献的过程中,看到了几幅非常不错的出版图,今天就跟着小编一起学习下,他们是怎么使用 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")),
-Inf, Inf, -Inf, Inf) +
annotation_custom(rasterGrob(image = img2,
x=0.73,
y=0.49,
width = unit(0.45,"npc"),
height = unit(0.87,"npc")),
-Inf, Inf, -Inf, Inf)
其他修饰图标
加入修饰图标,来说明问题。主要使用 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")),
-Inf, Inf, -Inf, Inf) +
annotation_custom(rasterGrob(image = img2,
x=0.73,
y=0.49,
width = unit(0.45,"npc"),
height = unit(0.87,"npc")),
-Inf, Inf, -Inf, Inf) +
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
小编有话说
本文主要学到的知识点如下:
使用
magick
包中的image_read()
导入两幅图,并通过image_flip()
进行转化;设置自定义主题
my_theme
,方便绘制其他图形使用;使用
annotate()
添加文字("text"
),使用geom_segment()
添加线段。
参考资料
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可视乎|气泡图