前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >基于 R 语言的科研论文绘图技巧详解(1)

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

作者头像
庄闪闪
发布于 2022-05-24 07:42:50
发布于 2022-05-24 07:42:50
1.5K00
代码可运行
举报
运行总次数:0
代码可运行

简介

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

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

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

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

主要知识点

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

绘图

加载包

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 语言可视化教程》,里面也有一些简单主题设置介绍。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 全局字体大小
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()进行转化。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
img1 <- magick::image_flip(magick::image_read("./image1.jpg"))
img2 <-  magick::image_flip(magick::image_read("./image2.png"))

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 中)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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() 添加线段(右下角白色的线段),这里没有设置箭头。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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") 

这时候得到的结果为:

微调主题

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  theme_void() +# blank plot w/o axes etc.
  theme(plot.margin = unit(c(-0,0,1,0), "cm"),
        aspect.ratio = 1)

完整代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

小编有话说

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

  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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 庄闪闪的R语言手册 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
EXCEL VBA语句集300
        定制模块行为 (1) Option Explicit ‘强制对模块内所有变量进行声明 Option Private Module ‘标记模块为私有,仅对同一工程中其它模块有用,在宏对话框中不显示  Option Compare Text ‘字符串不区分大小写  Option Base 1 ‘指定数组的第一个下标为1 (2) On Error Resume Next ‘忽略错误继续执行VBA代码,避免出现错误消息 (3) On Error GoTo ErrorHandler ‘当错误发生时跳转到过程中的某个位置 (4) On Error GoTo 0 ‘恢复正常的错误提示 (5) Application.DisplayAlerts=False ‘在程序执行过程中使出现的警告框不显示 (6) Application.ScreenUpdating=False ‘关闭屏幕刷新 Application.ScreenUpdating=True ‘打开屏幕刷新 (7) Application.Enable.CancelKey=xlDisabled ‘禁用Ctrl+Break中止宏运行的功能  工作簿 (8) Workbooks.Add() ‘创建一个新的工作簿 (9) Workbooks(“book1.xls”).Activate ‘激活名为book1的工作簿 (10) ThisWorkbook.Save ‘保存工作簿 (11) ThisWorkbook.close ‘关闭当前工作簿 (12) ActiveWorkbook.Sheets.Count ‘获取活动工作薄中工作表数 (13) ActiveWorkbook.name ‘返回活动工作薄的名称 (14) ThisWorkbook.Name ‘返回当前工作簿名称 ThisWorkbook.FullName ‘返回当前工作簿路径和名称 (15) ActiveWindow.EnableResize=False ‘禁止调整活动工作簿的大小 (16) Application.Window.Arrange xlArrangeStyleTiled ‘将工作簿以平铺方式排列 (17) ActiveWorkbook.WindowState=xlMaximized ‘将当前工作簿最大化  工作表 (18) ActiveSheet.UsedRange.Rows.Count ‘当前工作表中已使用的行数 (19) Rows.Count ‘获取工作表的行数(注:考虑向前兼容性) (20) Sheets(Sheet1).Name= “Sum” ‘将Sheet1命名为Sum (21) ThisWorkbook.Sheets.Add Before:=Worksheets(1) ‘添加一个新工作表在第一工作表前 (22) ActiveSheet.Move After:=ActiveWorkbook. _ Sheets(ActiveWorkbook.Sheets.Count) ‘将当前工作表移至工作表的最后 (23) Worksheets(Array(“sheet1”,”sheet2”)).Select ‘同时选择工作表1和工作表2 (24) Sheets(“sheet1”).Delete或 Sheets(1).Delete ‘删除工作表1 (25) ActiveWorkbook.Sheets(i).Name ‘获取工作表i的名称 (26) ActiveWindow.DisplayGridlines=Not ActiveWindow.DisplayGridlines ‘切换工作表中的网格线显示,这种方法也可以用在其它方面进行相互切换,即相当于开关按钮 (27) ActiveWindow.DisplayHeadings=Not ActiveWindow.DisplayHeadings ‘切换工作表中的行列边框显示 (28) ActiveSheet.UsedRange.FormatConditions.Delete ‘删除当前工作表中所有的条件格式 (29) Cells.Hyperlinks.Delete ‘取消当前工作表所有超链接 (30) ActiveSheet.PageSetup.Orientation=xlLandscape 或ActiveSheet.PageSetup.Orientation=2 ‘将页面设置更改为横向 (31) ActiveSheet.PageSetup.RightFooter=ActiveWorkbook.FullName ‘在页面设置的表尾中输入文件路径 ActiveSheet.PageSetup.Le
Tony老师
2020/03/05
2.3K0
Excel VBA编程
在Excel中,数据只有文本,数值,日期值,逻辑值和错误值五种类型。但是在VBA中,数据类型跟Excel不完全相同。根据数据的特点,VBA将数据分为布尔型(boolean),字节型(byte),整数型(integer),单精度浮点型(single),双精度浮点型(double),货币型(currency),小数型(decimal),字符串型(string),日期型(date),对象型等等
全栈程序员站长
2022/08/11
46.8K0
Excel VBA编程
小游戏2048
用Excel VBA来实现的手机上玩的那种组合数字的小游戏。 Public Row As Integer, Col As Integer '偏移 Di
xyj
2020/07/28
8060
小游戏2048
VBA字典(详解,示例)「建议收藏」
如果对上面水果种类进行计数:countifs,只需要将分类汇总的值改为数值1即可,每出现一次‘+1’
全栈程序员站长
2022/07/22
6.5K1
VBA字典(详解,示例)「建议收藏」
Excel 宏编程的常用代码
我们常用Excel统计一些数据,如果善用VBA,就能自动做出各种复杂的报表,懒人就是追求一劳永逸!不过,也不是真懒啦,只是用智慧(脑力劳动)将自身从体力劳动中解放出来而已,人类也是这样进步的。我有这样的感觉,就是每见到一个语句或函数,都会激发出偷懒的灵感来,哈哈,很自恋了,其实好玩而已。
全栈程序员站长
2022/06/30
2.9K0
循环语句For each...next语句
大家好,前面已经介绍过循环结构的for..next和do...loop系列语句。还有一种用于处理对象集合的循环语句,即for each...next语句,在本节介绍。(下面程序控制结构图帮助回顾)
无言之月
2019/10/13
2.2K0
VBA自动筛选完全指南(下)
这可能看起来很奇怪,但无论想要多少个前面的条目,运算符值始终为xlTop10Items。
fanjy
2022/11/16
3.9K0
VBA自动筛选完全指南(下)
ExcelVBA筛选法按分类条件拆分一个工作表为多个工作簿
对上次的文章进行优化 ==========代码如下===== Sub 筛选拆分() Dim d As Object, sht As Worksheet, arr, brr, r, kr, i&, j&, k&, x& Dim Rng As Range, Rg As Range, tRow&, tCol& Dim wb As Object, mysht As Worksheet Set d = CreateObject("scripting.dictionary") 'se
哆哆Excel
2022/10/31
3.7K1
一小时搞定 简单VBA编程 Excel宏编程快速扫盲
Excel宏编程可以快速完成批量表格操作:复制粘贴、数据过滤等,宏代码基于VB语言实现,有基础的编程经验就能快速阅读。下面是我的学习笔记。
全栈程序员站长
2022/08/10
1.8K0
VBA专题11:详解UsedRange属性
UsedRange属性是Worksheet对象的一个有用的属性,可以返回工作表中已使用的单元格区域。实际上,根据UsedRange的意思,我们就可以明白,该属性代表工作表中已使用的区域,不仅包括可以看到内容的单元格,而且不包括应用了格式、添加批注或其他一些修改的单元格。我们可以使用UsedRange属性来操控Excel工作表数据。
fanjy
2021/06/01
8.5K0
VBA专题11:详解UsedRange属性
代替VBA!用Python轻松实现Excel编程(文末赠书)
面向Excel数据处理自动化的脚本编程,目前主要有VBA和Python两种语言可供选择。
小F
2023/01/03
6.1K0
代替VBA!用Python轻松实现Excel编程(文末赠书)
VBA汇总一个文件多工作表到一个表
VBA汇总一个文件多工作表到一个表 . 今天在工作中,同事传来一个excel文件中有很多个工作表,要我汇总,每个表的标题是一样的,虽然一个一个复制、粘贴是可以做到的,但时间很长,所以把以前学习一个代码,拿来用一下,代码找了很久才找到,想想还是把他放在这里好一点,以后查找方便 . 把多个工作表的内容汇总到一个“汇总”表中 Sub sheets_to_one() Dim mysht As Worksheet, rng As Range, sht As Worksheet Dim
哆哆Excel
2022/10/31
5850
Excel实战技巧54: 创建导航工作表
当工作簿中的工作表不多时,我们只需要单击底部的工作表名到达想要操作的工作表。然而,当有很多工作表时,要找到想要的工作表就需要边单击滚动按钮边查找工作表,这可能要花点时间了。
fanjy
2019/09/24
1.1K0
Excel实战技巧54: 创建导航工作表
使用VBA自动化处理表的代码大全5
下面的代码检查是否表已经存在于工作簿。注意,可以根据实际情况修改tblName变量来满足你的需要。
fanjy
2023/08/30
4860
使用VBA自动化处理表的代码大全5
ExcelVBA运用Excel的【条件格式】(五)
FormatConditions.Add`方法在VBA中用于向工作表上的某个范围添加新的条件格式规则。这个方法是`FormatConditions`集合的一个成员,而`FormatConditions`集合属于`Range`对象。这意味着你必须首先选定一个范围,然后才能调用`Add`方法来添加条件格式。
哆哆Excel
2024/07/25
3850
ExcelVBA运用Excel的【条件格式】(五)
ExcelVBA汇总多工作簿中指定工作表到新工作簿
哆哆Excel
2023/09/09
5380
ExcelVBA汇总多工作簿中指定工作表到新工作簿
VBA 在 Excel 中的常用操作
设置单元格 Value 里使用 Chr(10) 和 Chr(13),分别表示回车、换行。
mzlogin
2020/04/16
4.2K0
Excel VBA 编程练习
最近做了一个VBA的小case,用于方便excel数据的处理,主要的功能代码记录如下。
全栈程序员站长
2022/09/05
9150
VBA实战技巧01: 在代码中引用动态调整单元格区域的5种方法
在VBA代码中,经常要引用单元格数据区域并对其进行操作。然而,如果对数据区域采用“硬编码”地址,那么当该区域大小变化时,必须修改相应的引用该区域的代码。本文整理了可以动态引用数据区域的5种方法,供编写代码时参考。
fanjy
2020/02/12
5K0
Excel VBA对象模型
上一讲说了Range对象,这一次我们从总体方面来说一下Excel VBA的对象模型。
xyj
2020/07/28
2.5K0
Excel VBA对象模型
相关推荐
EXCEL VBA语句集300
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验