• R语言
  • 求助:自定义函数中禁止ggplot()图片打印

我在一个自定义函数(下文称为函数1)中用ggplot()画图,然后在另外一个自定义函数(下文称为函数2)中引用了函数1,但是在执行函数2的时候,不希望生成的图片被打印出来。详情如下。

# 载入需要的包
library(ggplot2)
library(ggpubr)

# 构建数据
mydata <- data.frame(name_1 = c(1: 10), 
                     name_2 = c(11: 20), 
                     name_3 = c(111: 120))

# 函数1:以下函数能对数据的某一列作图
func_plot <- function(var_data, name) {
  # 作图参数设置
  plot_data <- ggplot() + 
    geom_point(aes(c(1:10), mydata[, name])) +
    labs(y = name)
  # 把图打出来
  print(plot_data)
}
func_plot(mydata, "name_1")
# 可以看到输出了一个图如下

上一段代码的输出

# 函数2:以下函数能对数据的所有列作图
func_plot_df <- function(var_data) {
  # 建一个列表先存储要打印的图
  plot_ls <- vector("list")
  for (i in names(var_data)) {
    plot_ls[[i]] <- func_plot(var_data, i)
  }
  # 然后把这些图安排在一个2 X 2的画布上
  ggarrange(plotlist = plot_ls, nrow = 2, ncol = 2)
}
func_plot_df(mydata)
可以看到输出四个图如下

上一段代码的输出的第一个图
上一段代码的输出的第二个图
上一段代码的输出的第三个图
上一段代码的输出的第四个图

想达到的效果是:执行函数1的时候,能正常打印图片;而执行函数2的时候,只输出第四个图,而不输出前三个。

可能的解决方法:目前函数2是基于函数1建立的,如果将函数1的源代码整合到函数2的源代码中并稍加修改,就可以实现我想要的效果。但是这样维护起来就比较麻烦,比如我想对函数1和函数2打印出图的样式做改变的话,就需要对两段源代码都做修改。

拓展:这段描述跟问题本身没有关系,主要解释我为什么希望解决这个问题,可以忽略。通过这个提问想解决的问题其实是,我需要两个函数,一个是对数据框的特定列作图,而另一个是对每一列作图,并将他们排在一个2x2的画布上以便查看。后者相当于对前者功能的一个扩展:前者应用于简单向量,后者应用于数据框。甚至,我还需要在这个基础上写一个列表版本的函数,实现类似的功能,只不过打印出图的排列形式也要进行一定的精简,以便查看。

barnett874 哈哈哈,意外的回答。不好意思,我没说清楚哈。我是希望第一函数能打印出来图片,而第二个函数只打出第四个图片。想了一下,增加了一个输入变量,目前的解决方案是:

# 载入需要的包
library(ggplot2)
library(ggpubr)

# 构建数据
mydata <- data.frame(name_1 = c(1: 10), 
                     name_2 = c(11: 20), 
                     name_3 = c(111: 120))

# 函数1:以下函数能对数据的某一列作图
func_plot <- function(var_data, name, figure = TRUE) {
  # 作图参数设置
  plot_data <- ggplot() + 
    geom_point(aes(c(1:10), mydata[, name])) +
    labs(y = name)
  # 把图打出来
  if (figure == TRUE) {print(plot_data)} else {plot_data}
}
# 运行函数1,出一个大图
func_plot(mydata, "name_1")

# 函数2:以下函数能对数据的所有列作图
func_plot_df <- function(var_data) {
  # 建一个列表先存储要打印的图
  plot_ls <- vector("list")
  for (i in names(var_data)) {
    plot_ls[[i]] <- func_plot(var_data, i, figure = FALSE)
  }
  # 然后把这些图安排在一个2 X 2的画布上
  ggarrange(plotlist = plot_ls, nrow = 2, ncol = 2)
}
# 运行函数2,几个分开的大图不打印,只打印最后拼出来的图
func_plot_df(mydata)

    KANG1943 不好意思,本人老阅读障碍了

    func_plot <- function(var_data, name) {
      # 作图参数设置
      plot_data <- ggplot() + 
        geom_point(aes(c(1:10), mydata[, name])) +
        labs(y = name)
      # 把图打出来,这里不用print
      plot_data
    }
    func_plot(mydata, "name_1")
    
    # 函数2:以下函数能对数据的所有列作图
    func_plot_df <- function(var_data) {
      # 建一个列表先存储要打印的图
      plot_ls <- vector("list")
      for (i in names(var_data)) {
        plot_ls[[i]] <- invisible(func_plot(var_data, i)) #使用invisible
      }
      # 然后把这些图安排在一个2 X 2的画布上
      ggarrange(plotlist = plot_ls, nrow = 2, ncol = 2)
    }
    func_plot_df(mydata)

      barnett874 哈!原来可以这样!谢谢哇。原来有invisible()这样的函数,看到它的一瞬间想起哈利波特的隐形斗篷。

      我不明白你们绕这么大一圈在干啥,直接把最初的代码中的 print() 去掉就可以了啊。下面的 invisible() 函数并不起任何作用,它只在顶层表达式中才有用:https://yihui.org/en/2017/06/top-level-r-expressions/ 用在 for循环内的话,那就不是顶层表达式。一个表达式内部的值是不会被打印出来的,只有顶层表达式的最后一个值会打印。所以 for 循环内的这个 invisible() 有或没有都没关系,问题不是出在这里,而是最初第一个函数显式调用了 print(),通常来说这不是编程的好习惯。除非不可避免,否则函数尽量只返回值,而不要涉及副作用(打印就是常见的副作用)。

        yihui 原来是这么回事啊……回看之前的代码,之所以用print()应该是当时函数2无法打印或者其他什么原因,就像是一个环状的因果报应邪恶循环:一开始需要的只是函数1,后来写了函数2发现无法打印,于是想着那就给函数1加个print()吧;再后来觉得一张张打印出来太麻烦啦,不如把函数2的图整合一下吧……如此种种,于是有了这个提问帖。谢谢哇😃