简介
在查阅文献的过程中,看到了几幅非常不错的出版图,今天就跟着小编一起学习下,他们是怎么使用 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